玩玩小爬虫——入门
前段时间做一个产品,盈利方式也就是卖数据给用户,用wpf包装一下,当然数据提供方是由公司定向爬虫采集的,虽然在实际工作
中没有接触这一块,不过私下可以玩一玩,研究研究。
既然要抓取网页的内容,肯定我们会有一个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 }
当然还有很多优化的地方,既然是开篇也就这样了,快速入门才是第一位。