winform网站资源抓取及下载设计方案和理念

设计方案

根据之前项目需求,抓取网站资源并下载,整理出两套设计方案:

1)采用HtmlAgilityPack.HtmlDocument(),当前网Html进行解析,再使用XPath路径表达式获取节点集合;

2)分析网页资源脚本和页面,若当前加载资源内容部分为异步操作,必定会存在异步调用操作的JS,获取JS存在AJAX请求的的相关参数作为模拟客户端请求的方式;

以下将对两种设计方案进行详细说明:

HtmlAgilityPack.HtmlDocument解析

winform的界面设计

此方式希望一次性加载所有资源目录并通过异步线程实现资源下载。

 资源抓取程序

 1         private void button1_Click(object sender, EventArgs e)
 2         {
 3             if (string.IsNullOrEmpty(chaperName.Text))
 4             {
 5                 MessageBox.Show("请输入课程名称");
 6                 return;
 7             }
 8             if (string.IsNullOrEmpty(directory.Text))
 9             {
10                 MessageBox.Show("请选择存放路径");
11                 return;
12             }
13             var root = directory.Text+@"\"+chaperName.Text;
14             if (!Directory.Exists(root))
15             {
16                 Directory.CreateDirectory(root);
17             }
18             List<JumonyZ> list = new List<JumonyZ>();
19             string htmlstr = "";
20             htmlstr = broswer.Document.GetElementById("horizontal_container").InnerHtml;
21             HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
22             doc.LoadHtml(htmlstr);
23             //XPath路径表达式,这里表示选取所有span节点中的font最后一个子节点,其中span节点的class属性值为num
24             //根据网页的内容设置XPath路径表达式
25             HtmlNode rootnode = doc.DocumentNode;    
26             var xpathString= "//div[@class='course-catalog  students-catalog']/ol/li";
27             HtmlNodeCollection chapters= rootnode.SelectNodes(xpathString);
28             if (chapters != null)
29             {
30                 foreach (var li in chapters)
31                 {
32                     var spanText = li.SelectSingleNode("//span").InnerText;
33                     var chapterDir = !string.IsNullOrEmpty(spanText)?root + @"\" + spanText: root;
34                     if (!Directory.Exists(chapterDir))
35                     {
36                         Directory.CreateDirectory(chapterDir);
37                     }
38                     var sections = li.SelectNodes("//ol/li[@data-hid]");
39                     if (sections!=null)
40                     {
41                         foreach (var sect in sections)
42                         {
43                             var sectionNode = sect.SelectSingleNode("//div[@class='catalog-row clefix']");
44                             var sectFolderName = sectionNode.SelectSingleNode("//div[@class='catalog-left']/div[@class='catalog-name']/a/span").InnerText;
45                             var sectionDir = !string.IsNullOrEmpty(sectFolderName) ? chapterDir + @"\" + sectFolderName : chapterDir;
46                             if (!Directory.Exists(sectionDir))
47                             {
48                                 Directory.CreateDirectory(sectionDir);
49                             }
50                             var sectionResNode = sectionNode.SelectNodes("//div[@class='catalog-right h_cells']/a");
51                             if (sectionResNode!=null)
52                             {
53                                 foreach (var resource in sectionResNode)
54                                 {
55                                     var res_Name = resource.GetAttributeValue("title", "");
56                                     var res_Url = resource.GetAttributeValue("href", "");
57                                     Action<string, string, string> w_downloadRes = LoadingResource;
58                                     w_downloadRes.BeginInvoke(res_Name, res_Url, sectionDir,null,null);
59                                 }
60                             }
61                             var itemNodes = sect.SelectNodes("//li");
62                             if (itemNodes!=null)
63                             {
64                                 foreach (var item in itemNodes)
65                                 {
66                                     var itemNode = item.SelectSingleNode("//div[@class='catalog-row clefix']");
67                                     var itemFolderName = itemNode.SelectSingleNode("//div[@class='catalog-left']/div[@class='catalog-name']/a/span").InnerText;
68                                     var itemDir = !string.IsNullOrEmpty(sectFolderName) ? sectionDir + @"\" + itemFolderName : sectionDir;
69                                     if (!Directory.Exists(itemDir))
70                                     {
71                                         Directory.CreateDirectory(itemDir);
72                                     }
73                                     var itemResNode = sectionNode.SelectNodes("//div[@class='catalog-right h_cells']/a");
74                                     if (itemResNode != null)
75                                     {
76                                         foreach (var resource in itemResNode)
77                                         {
78                                             var res_Name = resource.GetAttributeValue("title", "");
79                                             var res_Url = resource.GetAttributeValue("href", "");
80                                             Action<string, string, string> w_downloadRes = LoadingResource;
81                                             w_downloadRes.BeginInvoke(res_Name, res_Url, sectionDir, null, null);
82                                         }
83                                     }
84                                 }
85                             }
86                         }
87                     }
88                 }
89             }
90             else
91             {
92                 MessageBox.Show("暂无资源");
93                 return;
94             }
95         }

执行异步线程实现下载部分代码如下:

下面设计的代码就是根据当前downloadUrl采用Webclient模拟客户端请求下载资源通过filestream文件流的方式输出下载的文件;很可惜,这里没有得到想要的结果,因为winform采用[STAThread]单线程操作,导致在异步请求启用WebBrowser时提示单线程无法执行,从而终止此设计方案;

Webclient模拟数据接口,反序列化数据对象

抓取设计理念及程序

首先需要分析当前页面站点发送请求的方式,针对资源部分若是异步请求,你用谷歌浏览器F12查看JS异步请求参数,采用WebClient模拟异步请求,程序如下:

注意若被抓取的服务器需要验证Session,那么就需要事先加载WebBrowser并登录获取当前用户Cookie,并将Cookie添加到Webclient中再发送请求;然后将返回的对象字符串进行反序列化:

可以先用在线解析序列化网站,对指定的返回的数据结构的解析,并生成Model,现在解析网站:https://www.bejson.com/jsonviewernew/

 最后对解析的数据进行处理,详情内容不做具体赘述,程序如下:

资源下载主要程序

 1             WebClient client = new WebClient();
 2             //  第一种
 3             string url="http://zy2.icve.com.cn:80/doc/b@18D4B8F3E4FB002A638E59549C034E38.pdf/download?&filename=01案例欣赏《AniRoschier动画场景设计欣赏》";
 4             Stream str = client.OpenRead(url);
 5             StreamReader reader = new StreamReader(str);
 6             byte[] mbyte = new byte[1000000];
 7             int allmybyte = (int)mbyte.Length;
 8             int startmbyte = 0;
 9             while (allmybyte > 0)
10             {
11                 int m = str.Read(mbyte, startmbyte, allmybyte);
12                 if (m == 0)
13                     break;
14                 startmbyte += m;
15                 allmybyte -= m;
16             }
17 
18             reader.Dispose();
19             str.Dispose();
20             string path = "E:\\" + System.IO.Path.GetFileName(url);
21 
22 
23             FileStream fstr = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
24             fstr.Write(mbyte, 0, startmbyte);
25             fstr.Flush();
26             fstr.Close();

以上内容为项目总结,若有不足支出,请多多建议。

 

posted @ 2017-12-07 10:50  念冬的叶子  阅读(586)  评论(0编辑  收藏  举报