一、事出有因
1、上周工作原因项目的事情每天都很忙,周五下班和乐师兄下班的时候已经晚上11点了,然后和师兄吃了一个烧烤吃到了12点了(结果啥也没吃,钱也花了挺多的。ps:程序员建议养生)扯远了 ,主要是一周比较忙周六没有进行我们学校的健康报备信息填写。导致辅导员给我打了6个电话(现在我还是在实习中,所以还归学校管)自己刚好在看网络请求这一块的东西,说干就干!!!
二、知其所以然
1、我们学校的健康报备主要是将自己每天的信息填写到学校的公众号上面。打开页面是这样的。这个时候我们就要想一下我们是要做什么了。首先我们健康报备的流程是=>打开学校公众号=>输入自己的学号点击查询=>然后输入自己的正式情况=>点击提交。
三、反向分析
1、做为程序员的我们首先要清楚我们要什么,现在我们已经清楚了要做什么了和步骤。现在就是用我们的程序模拟用户进行正常的报备工作。首先我将报备链接从学校公众号复制出来,然后使用浏览器访问查看我们在进行报备的时候我们向浏览器发起了什么和做了什么。
2、在左边的是我们的页面,右边的是网页请求的文件和信息,我们就一步一步的进行分析。这里我们可以看到我们在访问页面的时候会生成一些信息__EVENTVALIDATION 、 __VIEWSTATEGENERATOR 我以前在长沙写的也是ASPX页面使用控件程序的时候也会生成一些这个,需要通过这些东西来获取我们页面填写的信息所以这里我们需要保留,因为我们等下需要输入学号然后点击查询,不过不传这些参数获取不到我输入的学号,这个是我尝试过的哈哈。
3、下面看到的是我输入了自己的学号点击查询获取到的信息,我们可以看到这里使用的是formData进行传递到后台去的,也可以明显的看到我填写的信息了。然后进入到了填写页面。然后我们在查看页面上面生成的东西。
4、我们输入完整的信息之后,点击报备按钮又会发生什么呢?可以看到我们提交的formData信息,请求也成功了(今天我已经报备了,所以是这个提示)。现在整个流程我们已经清楚了,以及请求的参数。那就让我们开始整活吧。
四、开始动手
1、废话不多说直接创建一个.net core 的项目整活。我先写了一个简单的html页面进行填写学号信息,然后在写一个定时任务每天晚上12点之后自动执行,健康报备信息。
public async static Task AsyncQuartz()
{
await Task.Run(async () =>
{
//创建一个键值集合
NameValueCollection nameValue = new NameValueCollection {
//定时任务的序列类型是二进制的
{ "quartz.serializer.type", "binary" }
};
//创建定时任务调度器工厂
StdSchedulerFactory factory = new StdSchedulerFactory(nameValue);
//获取工厂中的调度器
IScheduler scheduler = await factory.GetScheduler();
//开启调度器
await scheduler.Start();
//然后就是创建我们的任务
//给任务一个身份
//在进行建立
IJobDetail userServiceJob = JobBuilder.Create<HealthForJob>()
.WithIdentity("UserServiceJob", "UserServiceJobGroup")
.Build();
//任务有了创建触发器
ITrigger userServiceTrigger = TriggerBuilder.Create()
.WithIdentity("userServiceTrigger", "userServiceTriggerGroup")
.StartNow()
//给定执行时间,然后在重复执行
.WithSimpleSchedule(x => x.WithIntervalInHours(6).RepeatForever())
.Build();
//将任务和触发器进行绑定放入触发器中
//单任务调用
await scheduler.ScheduleJob(userServiceJob, userServiceTrigger);
});
}
2、首先我们需要模拟用户向报备网页发起请求这里我使用的是HttpClient 对象发起请求,怕被拦截我还填了很多请求头哈哈。但我们发起get请求的时候获取的是一段长的html字符串。
3、然后我们要使用一个神器进行html分析了。HtmlAgilityPack 它可以解析我们获取的html字符串代码
4、我们创建一个HtmlDocument htmlDoc1 = new HtmlDocument(); 对象然后然后将我们获取的html 对象放到 htmlDoc1.LoadHtml(strHtml);就可以解析成了正常的html了,也可以直接在页面上面复制xpath结构,然后直接放进来就好了
htmlDoc1.LoadHtml(strHtml);
//这里就是通过html结构寻找我们想要的节点信息
var liNodes1 = htmlDoc1.DocumentNode.SelectNodes("//div[@class='aspNetHidden']/input");
5、这里我们获取到了html节点之后就可以进行正常取数据啦。
6、这里就开始我们的请求三大步了,直接模拟一个form表单请求将我们的数据传递就好了
using (HttpContent httpContent = new FormUrlEncodedContent(keyValuePairs))
{
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
strHtml = httpClient.PostAsync("URL", httpContent).Result.Content.ReadAsStringAsync().Result;
if (!string.IsNullOrWhiteSpace(strHtml))
{
htmlDoc1.LoadHtml(strHtml);
var liNodes1 = htmlDoc1.DocumentNode.SelectNodes("//div[@class='aspNetHidden']/input");
keyValuePairs.Clear();
foreach (var item in liNodes1)
{
var id = item.Attributes["id"].Value;
var value = item.Attributes["value"].Value;
if (!string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(value))
{
keyValuePairs.Add(id, value);
}
}
}
}
五、项目部署
1、项目部署使用的是Docker +JenKins 实现自动化部署,现在我在公司也想慢慢推广因为我们公司服务器权限管的比较严格,每次发布测试环境都需要找师兄发布,导致师兄很多时间都在帮我们发布项目。所以这个技术我觉得是很有必要在我们部门推广。这个项目主要使用的了.net core 3.1 作为框架 Dapper作为数据访问层,Quartz 定时任务 ,HtmlAgilityPack 进行Html结构分析,Docker 部署项目 ,JenKins 实现项目自动化部署。由于篇幅问题这些技术会在后面的文章分享出来。我自己也多研究一下避免误人子弟。哈哈
六、个人说明
1、以上就是我写的全部流程,我们需要重复试错,因为你要了解写这个程序的同行的思路哈哈,我还看过一些网站是通过惰性加载信息,还有一些比较重要的信息会通过其他的方式传递,就是防止我们爬取,比如京东的商品价格使用Js请求的方式传递Jsonp请求,这个这个就需要我们多动手动倒腾了。重点说明一下这个只是自己学习使用的,对于学校健康报备我双手支持。源码就不分享了大家动起手来吧。
来源:oschina
链接:https://my.oschina.net/u/4323713/blog/4314189