1、什么是网络爬虫
关于爬虫百度百科这样定义的:网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。从搜索引擎开始,爬虫应该就出现了,爬虫所做的事情就是分析URL、下载WebServer返回的HTML、分析HTML内容、构建HTTP请求的模拟、在爬虫过程中存储有用的信息等等。简单点说,就是把别人网站上的东西爬下来,至于爬做什么用就看你自己了。
写网络爬虫很多语言都可以写,比如众所周知的Python以及、PHP、C、Java等等。今天我就基于.Net中的HtmlAgilityPack类写一个简单的爬虫。
2、HtmlAgilityPack类
HtmlAgilityPack 是 .NET 下的一个 HTML 解析类库。支持用 XPath 来解析 HTML 。命名空间: HtmlAgilityPack,下载地址:http://htmlagilitypack.codeplex.com/releases/view/90925
2.1基本属性
Attributes 获取节点的属性集合
ChildNodes 获取子节点集合(包括文本节点)
Closed 该节点是否已关闭(</xxx>)
ClosingAttributes 在关闭标签的属性集合
FirstChild 获取第一个子节点
HasAttributes 判断该节点是否含有属性
HasChildNodes 判断该节点是否含有子节点
HasClosingAttributes 判断该节点的关闭标签是否含有属性(</xxx class="xxx">)
Id 获取该节点的Id属性
InnerHtml 获取该节点的Html代码
InnerText 获取该节点的内容,与InnerHtml不同的地方在于它会过滤掉Html代码,而InnerHtml是连Html代码一起输出
LastChild 获取最后一个子节点
Line 获取该节点的开始标签或开始代码位于整个HTML源代码的第几行(行号)
LinePosition 获取该节点位于第几列
Name Html元素名
NextSibling 获取下一个兄弟节点
NodeType 获取该节点的节点类型
OriginalName 获取原始的未经更改的元素名
OuterHtml 整个节点的代码
OwnerDocument 节点所在的HtmlDocument文档
ParentNode 获取该节点的父节点
PreviousSibling 获取前一个兄弟节点
StreamPosition 该节点位于整个Html文档的字符位置
XPath 根据节点返回该节点的XPath
2.2方法
IEnumerable<HtmlNode> Ancestors(); 返回此元素的所有上级节点的集合。
IEnumerable<HtmlNode> Ancestors(string name); 返回此元素参数名字匹配的所有上级节点的集合。
IEnumerable<HtmlNode> AncestorsAndSelf(); 返回此元素的所有上级节点和自身的集合。
IEnumerable<HtmlNode> AncestorsAndSelf(string name); 返回此元素的名字匹配的所有上级节点和自身的集合。
HtmlNode AppendChild(HtmlNode newChild); 将参数元素追加到为调用元素的子元素(追加在最后)
void AppendChildren(HtmlNodeCollection newChildren); 将参数集合中的元素追加为调用元素的子元素(追加在最后)
HtmlNode PrependChild(HtmlNode newChild); 将参数中的元素作为子元素,放在调用元素的最前面
void PrependChildren(HtmlNodeCollection newChildren); 将参数集合中的所有元素作为子元素,放在调用元素前面
static bool CanOverlapElement(string name); 确定是否可以保存重复的元素
IEnumerable<HtmlAttribute> ChildAttributes(string name); 获取所有子元素的属性(参数名要与元素名匹配)
HtmlNode Clone(); 本节点克隆到一个新的节点
HtmlNode CloneNode(bool deep); 节点克隆到一个新的几点,参数确定是否连子元素一起克隆
HtmlNode CloneNode(string newName); 克隆的同时更改元素名
HtmlNode CloneNode(string newName, bool deep); 克隆的同时更改元素名。参数确定是否连子元素一起克隆
void CopyFrom(HtmlNode node); 创建重复的节点和其下的子树。
void CopyFrom(HtmlNode node, bool deep); 创建节点的副本。
XPathNavigator CreateNavigator(); 返回的一个对于此文档的XPathNavigator
static HtmlNode CreateNode(string html); 静态方法,允许用字符串创建一个新节点
XPathNavigator CreateRootNavigator(); 创建一个根路径的XPathNavigator
IEnumerable<HtmlNode> DescendantNodes(); 获取所有子代节点
IEnumerable<HtmlNode> DescendantNodesAndSelf(); 获取所有的子代节点以及自身
IEnumerable<HtmlNode> Descendants(); 获取枚举列表中的所有子代节点
IEnumerable<HtmlNode> Descendants(string name); 获取枚举列表中的所有子代节点,注意元素名要与参数匹配
IEnumerable<HtmlNode> DescendantsAndSelf(); 获取枚举列表中的所有子代节点以及自身
IEnumerable<HtmlNode> DescendantsAndSelf(string name); 获取枚举列表中的所有子代节点以及自身,注意元素名要与参数匹配
HtmlNode Element(string name); 根据参数名获取一个元素
IEnumerable<HtmlNode> Elements(string name); 根据参数名获取匹配的元素集合
bool GetAttributeValue(string name, bool def); 帮助方法,用来获取此节点的属性的值(布尔类型)。如果未找到该属性,则将返回默认值。
int GetAttributeValue(string name, int def); 帮助方法,用来获取此节点的属性的值(整型)。如果未找到该属性,则将返回默认值。
string GetAttributeValue(string name, string def); 帮助方法,用来获取此节点的属性的值(字符串类型)。如果未找到该属性,则将返回默认值。
HtmlNode InsertAfter(HtmlNode newChild, HtmlNode refChild); 将一个节点插入到第二个参数节点的后面,与第二个参数是兄弟关系
HtmlNode InsertBefore(HtmlNode newChild, HtmlNode refChild); 讲一个节点插入到第二个参数节点的后面,与第二个参数是兄弟关系
static bool IsCDataElement(string name); 确定是否一个元素节点是一个 CDATA 元素节点。
static bool IsClosedElement(string name); 确定是否封闭的元素节点
static bool IsEmptyElement(string name); 确定是否一个空的元素节点。
static bool IsOverlappedClosingElement(string text); 确定是否文本对应于一个节点可以保留重叠的结束标记。
void Remove(); 从父集合中移除调用节点
void RemoveAll(); 移除调用节点的所有子节点以及属性
void RemoveAllChildren(); 移除调用节点的所有子节点
HtmlNode RemoveChild(HtmlNode oldChild); 移除调用节点的指定名字的子节点
HtmlNode RemoveChild(HtmlNode oldChild, bool keepGrandChildren);移除调用节点调用名字的子节点,第二个参数确定是否连孙子节点一起移除
HtmlNode ReplaceChild(HtmlNode newChild, HtmlNode oldChild); 将调用节点原有的一个子节点替换为一个新的节点,第二个参数是旧节点
HtmlNodeCollection SelectNodes(string xpath); 根据XPath获取一个节点集合
HtmlNode SelectSingleNode(string xpath); 根据XPath获取唯一的一个节点
HtmlAttribute SetAttributeValue(string name, string value); 设置调用节点的属性
string WriteContentTo(); 将该节点的所有子级都保存到一个字符串中。
void WriteContentTo(TextWriter outText); 将该节点的所有子级都保存到指定的 TextWriter。
string WriteTo(); 将当前节点保存到一个字符串中。
void WriteTo(TextWriter outText); 将当前节点保存到指定的 TextWriter。
void WriteTo(XmlWriter writer);
3、第一个爬虫程序
3.1在VS2017中建立一个web项目拖个服务器控件按钮上去(我VS是用2017的)
3.2后台代码及解释
/// <summary> /// 博客园精华单机按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btntwo_Click(object sender, EventArgs e) { int sum = 0; for (int i = 1; i < 2; i++) { SqlDB sb = new SqlDB(); HtmlWeb wb = new HtmlWeb(); string webaddress= ""; if (i==1) { webaddress="http://www.cnblogs.com/pick/";//为啥http://www.cnblogs.com/pick/#p10进去还是第一页 string webbbb = GetHTML(webaddress); } else { webaddress = string.Format("http://www.cnblogs.com/pick/{0}", ("#p"+i.ToString()).ToString()); string webbbb = GetHTML(webaddress); } try { HtmlDocument doc = wb.Load(webaddress); HtmlNode node = doc.GetElementbyId("post_list"); if (node != null) { foreach (HtmlNode hnode in node.ChildNodes) { if (hnode.Attributes["class"] == null || hnode.Attributes["class"].Value != "post_item") continue; HtmlNode hn = HtmlNode.CreateNode(hnode.OuterHtml); //推荐 int recommend = Convert.ToInt32((hn.SelectSingleNode("//*[@class=\"diggnum\"]").InnerText).Trim()); //标题 string title = hn.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText; //网址 string webhref = hn.SelectSingleNode("//*[@class=\"titlelnk\"]").Attributes["href"].Value.ToString(); //介绍 string introduce = hn.SelectSingleNode("//*[@class=\"post_item_summary\"]").InnerText; string articletimetest = hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText; //发表时间(陈树义 发布于 2017 - 11 - 15 10:13 评论(41)阅读(7372) string articletime = ((hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText).Trim()).Replace("\r\n", "+"); //分割字符串 string[] st = articletime.Split('+'); //取出(发布于 2017 - 11 - 15 10:13) string pp = (st[1].ToString()).Trim(); //分割字符串 string[] qq = pp.Split(' '); //取出(2017/11/15 10 :13) DateTime gg = Convert.ToDateTime(qq[1].ToString() + " " + qq[2].ToString()); try { string sql = string.Format(@"insert into CnblogsList( Recommend, Title,Contents, Introduce, WebHref, ArticleTime) values({0},'{1}','{2}','{3}','{4}','{5}')", recommend, title, GetContentsString(webhref), introduce, webhref, gg); sb.ExecuteNonQuery(sql); sum++; } catch (Exception ex) { Response.Write(ex.Message); } Response.Write(GetContentsString(webhref)+"<hr /> <hr />"); // 陈树义 发布于 2017 - 11 - 15 10:13 评论(41)阅读(7372) Response.Write("推荐:" + (hn.SelectSingleNode("//*[@class=\"diggnum\"]").InnerText)+"<br />"); Response.Write("标题:" + (hn.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText) + "<br />"); Response.Write("标题对应的网址:" + (hn.SelectSingleNode("//*[@class=\"titlelnk\"]").Attributes["href"].Value.ToString()) + "<br />"); Response.Write("介绍:" + (hn.SelectSingleNode("//*[@class=\"post_item_summary\"]").InnerText) + "<br />"); Response.Write("时间:" + (hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText) + "<br /><hr />"); } } else { Response.Write("节点为空 +++++ 出错节点Iiiii是:" + i.ToString() + "<br /> 网址:" + webaddress.ToString() + "<br /> 插入数据:"+ sum +"条"); return; } } catch (Exception esz) { Response.Write(esz.Message); } } }
这是获取网页源码的代码
/// <summary> /// 获取网页源码 /// </summary> /// <param name="url"></param> /// <returns></returns> public string GetHTML(string url) { WebClient web = new WebClient(); web.Encoding = Encoding.UTF8; string buffer = web.DownloadString(url); return buffer; }
上面的for循环目的是获取精华区后面页数的内容,但是效果并不是这样的
我早谷歌上F12调试发现并不是这样的额 请求地址不对。为以为在后面加?PageIndex= XX就可以实现 还是不可以 因为这是经过处理的,在Header上面有地址https://www.cnblogs.com/mvc/AggSite/PostList.aspx你们仔细就可以看到。这样做了如果页数写多了就会重复出现第一页的内容 数据库中是保存的 数据库的表结构我上图
我一弄才发现原来这个地址是博客园里面的内容时刻更新的。经过处理取出精华区的。一次去可以取出2天的内容 不管是发布在博客园首页的还是没有发布在首页的都有。我就爬虫爬了一次 试试看 这是第二个按钮的源码
/// <summary> /// 博客园数据最近大约2天 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void BtnNowTowDay_Click(object sender, EventArgs e) { int sum = 0; for (int i = 1; i < 200; i++) { //这是SqlDB类 SqlDB sb = new SqlDB(); //实例HtmlWdb HtmlWeb wb = new HtmlWeb(); //网址 string webaddress = ""; webaddress = string.Format("https://www.cnblogs.com/mvc/AggSite/PostList.aspx?PageIndex={0}", + i); //网页源码 有html的 string webbbb = GetHTML(webaddress); try { HtmlDocument doc = wb.Load(webaddress); //获取div[@class='post_item的上级目录 //这个可以根据节点 ID选择 建议多看一下Xpath 就好理解了 HtmlNode node = doc.DocumentNode.SelectSingleNode("//div[@class='post_item']").SelectSingleNode(".."); //HtmlNode node = doc.GetElementbyId("#id"); if (node != null) { foreach (HtmlNode hnode in node.ChildNodes) { if (hnode.Attributes["class"] == null || hnode.Attributes["class"].Value != "post_item") continue; HtmlNode hn = HtmlNode.CreateNode(hnode.OuterHtml); //推荐 int recommend = Convert.ToInt32((hn.SelectSingleNode("//*[@class=\"diggnum\"]").InnerText).Trim()); //标题 string title = hn.SelectSingleNode("//*[@class=\"titlelnk\"]").InnerText; //网址 string webhref = hn.SelectSingleNode("//*[@class=\"titlelnk\"]").Attributes["href"].Value.ToString(); //介绍 string introduce = hn.SelectSingleNode("//*[@class=\"post_item_summary\"]").InnerText; string articletimetest = hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText; //发表时间(陈树义 发布于 2017 - 11 - 15 10:13 评论(41)阅读(7372) string articletime = ((hn.SelectSingleNode("//*[@class=\"post_item_foot\"]").InnerText).Trim()).Replace("\r\n", "+"); //分割字符串 string[] st = articletime.Split('+'); //取出(发布于 2017 - 11 - 15 10:13) string pp = (st[1].ToString()).Trim(); //分割字符串 string[] qq = pp.Split(' '); //取出(2017/11/15 10 :13) DateTime gg = Convert.ToDateTime(qq[1].ToString() + " " + qq[2].ToString()); try { string sql = string.Format(@"insert into CnblogsList( Recommend, Title,Contents, Introduce, WebHref, ArticleTime) values({0},'{1}','{2}','{3}','{4}','{5}')", recommend, title, GetContentsString(webhref), introduce, webhref, gg); sb.ExecuteNonQuery(sql); sum++; } catch (Exception ex) { Response.Write(ex.Message); } } } else { Response.Write("节点为空 +++++ 出错节点Iiiii是:" + i.ToString() + "<br /> 网址:" + webaddress.ToString() + "<br /> 插入数据:" + sum + "条"); return; } } catch (Exception esz) { Response.Write(esz.Message); } } }
下面是住区自己博客园的后台
/// <summary> /// 抓取自己博客园的 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void btnWY_Click(object sender, EventArgs e) { SqlDB sb = new SqlDB(); HtmlWeb wh = new HtmlWeb(); HtmlDocument doc = wh.Load("http://www.cnblogs.com/w5942066/"); HtmlNode node = doc.DocumentNode.SelectSingleNode("//div[@class='day']").SelectSingleNode(".."); if(node !=null) { foreach(HtmlNode hn in node.ChildNodes) { if(hn.Attributes["class"] == null || hn.Attributes["class"].Value!="day") { continue; } HtmlNode hnn = HtmlNode.CreateNode(hn.OuterHtml); //shijian string recommend = hnn.SelectSingleNode("//*[@class=\"dayTitle\"]").InnerText; //标题 string title = hnn.SelectSingleNode("//*[@class=\"postTitle\"]").InnerText; //网址 string webhref = hnn.SelectSingleNode("//*[@class=\"postTitle2\"]").Attributes["href"].Value.ToString(); //介绍 string introduce = hnn.SelectSingleNode("//*[@class=\"c_b_p_desc\"]").InnerText; //时间 // string articletimetest = hn.SelectSingleNode("//*[@class=\"postDesc\"]").InnerText; //发表时间(陈树义 发布于 2017 - 11 - 15 10:13 评论(41)阅读(7372) // string articletime = ((hn.SelectSingleNode("//*[@class=\"postDesc\"]").InnerText).Trim()).Replace("\r\n", "+"); //分割字符串 //string[] st = articletime.Split('+'); //取出(发布于 2017 - 11 - 15 10:13) //string pp = (st[1].ToString()).Trim(); //分割字符串 //string[] qq = pp.Split(' '); //取出(2017/11/15 10 :13) DateTimeFormatInfo dtFormat = new DateTimeFormatInfo(); dtFormat.ShortDatePattern = "yyyy年MM月dd日"; DateTime dt = Convert.ToDateTime(recommend, dtFormat); try { string sql = string.Format(@"insert into CnblogsList( Recommend, Title,Contents, Introduce, WebHref, ArticleTime) values({0},'{1}','{2}','{3}','{4}','{5}')", 520, title, GetContentsString(webhref), introduce, webhref, dt); sb.ExecuteNonQuery(sql); } catch (Exception ex) { Response.Write(ex.Message); }注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。