中没有接触这一块,不过私下可以玩一玩,研究研究。
既然要抓取网页的内容,肯定我们会有一个startUrl,通过这个startUrl就可以用广度优先的方式遍历整个站点,就如我们学习数据结
构中图的遍历一样。
既然有“请求网页”和“解析网页”两部分,在代码实现上,我们得需要有两个集合,分别是Todo和Visited集合,为了简单起见,我们
从单机版爬虫说起,说起爬虫,就必然逃避不了海量数据,既然是海量数据,那么性能问题不容忽视,在Todo和Visited集合的甄别
上,我们选择用Queue和HashSet,毕竟HashSet在定位查找方面只需常量的时间,下面我们用活动图来阐述一下。
在广度优先的时候,我们需要注意两个问题:
①:有的时候网页是相对地址,我们需要转化为绝对地址。
②:剔除外链。
看看其中我们一个部门的官网,广度遍历一下,看看有多少链接,当然是剔除外链的。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Net;
6 using System.IO;
7 using System.Text.RegularExpressions;
8
9 namespace ConsoleApplication1
10 {
11 public class Program
12 {
13 static void Main(string[] args)
14 {
15 var crawler = new Crawler("http://www.weishangye.com/");
16
17 crawler.DownLoad();
18
19 //show 一下我们爬到的链接
20 foreach (var item in Crawler.visited)
21 {
22 Console.WriteLine(item);
23 }
24 }
25 }
26
27 public class Crawler
28 {
29 //基地址
30 public static Uri baseUri;
31 public static string baseHost = string.Empty;
32
33 /// <summary>
34 /// 工作队列
35 /// </summary>
36 public static Queue<string> todo = new Queue<string>();
37
38 //已访问的队列
39 public static HashSet<string> visited = new HashSet<string>();
40
41 public Crawler(string url)
42 {
43 baseUri = new Uri(url);
44
45 //基域
46 baseHost = baseUri.Host.Substring(baseUri.Host.IndexOf('.'));
47
48 //抓取首地址入队
49 todo.Enqueue(url);
50 }
51
52 public void DownLoad()
53 {
54 while (todo.Count > 0)
55 {
56 var currentUrl = todo.Dequeue();
57
58 //当前url标记为已访问过
59 visited.Add(currentUrl);
60
61 var request = WebRequest.Create(currentUrl) as HttpWebRequest;
62
63 var response = request.GetResponse() as HttpWebResponse;
64
65 var sr = new StreamReader(response.GetResponseStream());
66
67 //提取url,将未访问的放入todo表中
68 RefineUrl(sr.ReadToEnd());
69 }
70 }
71
72 /// <summary>
73 /// 提取Url
74 /// </summary>
75 /// <param name="html"></param>
76 public void RefineUrl(string html)
77 {
78 Regex reg = new Regex(@"(?is)<a[^>]*?href=(['""]?)(?<url>[^'""\s>]+)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>");
79
80 MatchCollection mc = reg.Matches(html);
81
82 foreach (Match m in mc)
83 {
84 var url = m.Groups["url"].Value;
85
86 if (url == "#")
87 continue;
88
89 //相对路径转换为绝对路径
90 Uri uri = new Uri(baseUri, url);
91
92 //剔除外网链接(获取顶级域名)
93 if (!uri.Host.EndsWith(baseHost))
94 continue;
95
96 if (!visited.Contains(uri.ToString()))
97 {
98 todo.Enqueue(uri.ToString());
99 }
100 }
101 }
102 }
103 }
当然还有很多优化的地方,既然是开篇也就这样了,快速入门才是第一位。