什么是爬虫
爬虫即网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。
我们日常使用的搜索引擎、比价网站等都是通过爬虫技术获取数据,而后经数据提取、分析、优化后供用户使用。当然日常生活中,我们也可以使用爬虫技术,获取我们感兴趣的网站进行数据解析分析,做出来一些有意思的功能或小工具,比如新闻提醒、事项通知、资料整理等。
这方面我们需要了解的技术主要是:Http请求、XPath、正则等。以下是一个简单的例子,用于获取京东商城的数据,例子仅供学习。
抓取京东所有商品类别
流程
实现
获取数据
使用 System.Net.WebRequest/System.Net.HttpWebRequest
1 2 3 4 5 6 7 8 9 10 11 12 13
| HtmlAgilityPack.HtmlDocument document = null; WebRequest request = WebRequest.Create("https://www.jd.com/allsort.aspx"); using (WebResponse response = request.GetResponse()) { using (Stream stream = response.GetResponseStream()) { document = new HtmlAgilityPack.HtmlDocument(); document.Load(stream, Encoding.UTF8); } }
|
使用 System.Net.Client
1 2 3 4 5 6 7 8 9
| HtmlAgilityPack.HtmlDocument document = null; using (WebClient client = new WebClient()) { using (Stream stream = client.OpenRead("https://www.jd.com/allsort.aspx")) { document = new HtmlAgilityPack.HtmlDocument(); document.Load(stream, Encoding.UTF8); } }
|
使用 HtmlAgilityPack.HtmlWeb
1 2 3 4 5
| HtmlWeb web = new HtmlWeb { OverrideEncoding = Encoding.UTF8 }; HtmlAgilityPack.HtmlDocument document = web.Load("https://www.jd.com/allsort.aspx");
|
解析数据
实体
1 2 3 4 5 6 7
| public class Category { public int Id { get; set; } public string JDId { get; set; } public string Name { get; set; } public string URL { get; set; } }
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| List<Category> listCategory = new List<Category>(); if (document != null) { int iId = 1; Regex regex = new Regex("cat=(\\d+(\\,\\d+)*)"); HtmlNodeCollection collection = document.DocumentNode.SelectNodes("//a[@href]"); foreach (var item in collection) { Match match = regex.Match(item.Attributes["href"].Value.ToLower()); if (match.Success) { listCategory.Add(new Category() { Id = iId++, JDId = match.Groups[1].Value, Name = item.InnerText, URL = item.Attributes["href"].Value.ToLower().StartsWith("http") ? item.Attributes["href"].Value : "https:" + item.Attributes["href"].Value }); } } }
|
抓取京东商品信息
流程
实现
获取数据
见类别获取部分实现。
解析数据
实体
1 2 3 4 5 6 7 8 9
| public class Product { public int Id { get; set; } public string JDId { get; set; } public string Name { get; set; } public string Price { get; set; } public string ImageURL { get; set; } public string URL { get; set; } }
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| if (document != null) { string sCurrentPager = string.Empty; string sAllPager = string.Empty; HtmlNodeCollection currentPagerNode = document.DocumentNode.SelectNodes("//*[@id=\"J_topPage\"]/span/b"); if (currentPagerNode.Count != 1) { throw new Exception("加载当前页码失败!"); } else { sCurrentPager = currentPagerNode[0].InnerText; } HtmlNodeCollection allPagersNode = document.DocumentNode.SelectNodes("//*[@id=\"J_topPage\"]/span/i"); if (allPagersNode.Count != 1) { throw new Exception("加载总页数失败!"); } else { sAllPager = allPagersNode[0].InnerText; } int iId = 1; HtmlNodeCollection productNode = document.DocumentNode.SelectNodes("//*[@id=\"plist\"]/ul/li"); foreach (var item in productNode) { Product product = new Product(); HtmlAgilityPack.HtmlDocument tempDocument = new HtmlAgilityPack.HtmlDocument(); tempDocument.LoadHtml(item.OuterHtml); HtmlNodeCollection imageNode = tempDocument.DocumentNode.SelectNodes("//*[@class=\"p-img\"]/a/img"); if (imageNode.Count > 0 && imageNode[0].Attributes["src"] != null && !string.IsNullOrEmpty(imageNode[0].Attributes ["src"].Value)) { product.ImageURL = imageNode[0].Attributes["src"].Value.ToLower().StartsWith("http") ? imageNode[0].Attributes ["src"].Value : "https:" + imageNode[0].Attributes["src"].Value; } else if (imageNode.Count > 0 && imageNode[0].Attributes["data-lazy-img"] != null && !string.IsNullOrEmpty(imageNode [0].Attributes ["data-lazy-img"].Value)) { product.ImageURL = imageNode[0].Attributes["data-lazy-img"].Value.ToLower().StartsWith("http") ? imageNode[0].Attributes ["data- lazy-img"].Value : "https:" + imageNode[0].Attributes["data-lazy-img"].Value; } else { continue; } HtmlNodeCollection idNode = tempDocument.DocumentNode.SelectNodes("//li/div"); if (idNode.Count > 0 && idNode[0].Attributes["data-sku"] != null && !string.IsNullOrEmpty(idNode[0].Attributes["data- sku"].Value)) { product.JDId = idNode[0].Attributes["data-sku"].Value; } else { continue; } HtmlNodeCollection nameNode = tempDocument.DocumentNode.SelectNodes("//*[@class=\"p-name\"]/a/em"); if (nameNode.Count > 0 && !string.IsNullOrEmpty(nameNode[0].InnerText.Trim())) { product.Name = nameNode[0].InnerText.Trim(); } else { continue; } HtmlNodeCollection urlNode = tempDocument.DocumentNode.SelectNodes("//*[@class=\"p-name\"]/a"); if (urlNode.Count > 0 && urlNode[0].Attributes["href"] != null && !string.IsNullOrEmpty(urlNode[0].Attributes["href"].Value)) { product.URL = urlNode[0].Attributes["href"].Value.ToLower().StartsWith("http") ? urlNode[0].Attributes["href"].Value : "https:" + urlNode[0].Attributes["href"].Value; } else { continue; } product.Id = iId++; listProduct.Add(product); } }
|
注意事项
- 图片懒加载,如果解析src属性可能解析不到,需要调试查看懒加载图片属性。
- 价格信息抓取会发现为空,观察会发现其calss属性为“J_price”,怀疑为ajax请求获取,下一部分说明获取方法。
抓取京东商品价格信息
流程
实现
获取数据
见类别获取部分实现。
解析数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
private Dictionary<string, string> GetPrice(List<string> listJDId) { Dictionary<string, string> dicPrice = new Dictionary<string, string>(); if (listJDId.Count > 0) { for (int i = 0; i < listJDId.Count; i++) { listJDId[i] = "J_" + listJDId[i]; } for (int i = 0; i < (listJDId.Count + 9) / 10; i++) { HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument(); WebRequest request = WebRequest.Create("https://p.3.cn/prices/mgets?skuIds=" + string.Join("%2C", listJDId.Skip(i * 10).Take(10))); using (Stream stream = request.GetResponse().GetResponseStream()) { document.Load(stream, Encoding.UTF8); } if (!string.IsNullOrEmpty(document.Text)) { object oPrice = Newtonsoft.Json.JsonConvert.DeserializeObject(document.Text); if (oPrice is JArray) { foreach (var item in oPrice as JArray) { if (item is JObject && (item as JObject).ContainsKey("id") && (item as JObject).ContainsKey("p")) { if ((item as JObject)["id"] is JValue jId && (item as JObject)["p"] is JValue jPrice && listJDId.Contains(jId.Value.ToString()) && !dicPrice.ContainsKey(jId.Value.ToString().TrimStart('J', '_'))) { dicPrice.Add(jId.Value.ToString().TrimStart('J', '_'), jPrice.Value.ToString()); } } } } } } } return dicPrice; }
|
若想使用jQuery选择器可以结合Fizzlerex使用:https://archive.codeplex.com/?p=fizzlerex