爬虫简介
网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。
算法分析
网页分析算法可以归纳为基于网络拓扑、基于网页内容和基于用户访问行为三种类型。
拓扑分析算法
基于网页之间的链接,通过已知的网页或数据,来对与其有直接或间接链接关系的对象(可以是网页或网站等)作出评价的算法。又分为网页粒度、网站粒度和网页块粒度这三种。
(1)网页(Webpage)粒度的分析算法
PageRank和HITS算法是最常见的链接分析算法,两者都是通过对网页间链接度的递归和规范化计算,得到每个网页的重要度评价。PageRank算法虽然考虑了用户访问行为的随机性和Sink网页的存在,但忽略了绝大多数用户访问时带有目的性,即网页和链接与查询主题的相关性。针对这个问题,HITS算法提出了两个关键的概念:权威型网页(authority)和中心型网页(hub)。
基于链接的抓取的问题是相关页面主题团之间的隧道现象,即很多在抓取路径上偏离主题的网页也指向目标网页,局部评价策略中断了在当前路径上的抓取行为。文献提出了一种基于反向链接(BackLink)的分层式上下文模型(Context Model),用于描述指向目标网页一定物理跳数半径内的网页拓扑图的中心Layer0为目标网页,将网页依据指向目标网页的物理跳数进行层次划分,从外层网页指向内层网页的链接称为反向链接。
(2)网站粒度的分析算法
网站粒度的资源发现和管理策略也比网页粒度的更简单有效。网站粒度的爬虫抓取的关键之处在于站点的划分和站点等级(SiteRank)的计算。SiteRank的计算方法与PageRank类似,但是需要对网站之间的链接作一定程度抽象,并在一定的模型下计算链接的权重。
网站划分情况分为按域名划分和按IP地址划分两种。文献讨论了在分布式情况下,通过对同一个域名下不同主机、服务器的IP地址进行站点划分,构造站点图,利用类似PageRank的方法评价SiteRank。同时,根据不同文件在各个站点上的分布情况,构造文档图,结合SiteRank分布式计算得到DocRank。文献证明,利用分布式的SiteRank计算,不仅大大降低了单机站点的算法代价,而且克服了单独站点对整个网络覆盖率有限的缺点。附带的一个优点是,常见PageRank 造假难以对SiteRank进行欺骗。
(3)网页块粒度的分析算法
在一个页面中,往往含有多个指向其他页面的链接,这些链接中只有一部分是指向主题相关网页的,或根据网页的链接锚文本表明其具有较高重要性。但是,在PageRank和HITS算法中,没有对这些链接作区分,因此常常给网页分析带来广告等噪声链接的干扰。在网页块级别(Block level)进行链接分析的算法的基本思想是通过VIPS网页分割算法将网页分为不同的网页块(page block),然后对这些网页块建立page to block和block to page的链接矩阵,分别记为Z和X。于是,在page to page图上的网页块级别的PageRank为 W§=X×Z;在block to block图上的BlockRank为 W(b)=Z×X。已经有人实现了块级别的PageRank和HITS算法,并通过实验证明,效率和准确率都比传统的对应算法要好。
网页内容分析算法
基于网页内容的分析算法指的是利用网页内容(文本、数据等资源)特征进行的网页评价。网页的内容从原来的以超文本为主,发展到后来动态页面(或称为Hidden Web)数据为主,后者的数据量约为直接可见页面数据(PIW,Publicly Indexable Web)的400~500倍。另一方面,多媒体数据、Web Service等各种网络资源形式也日益丰富。因此,基于网页内容的分析算法也从原来的较为单纯的文本检索方法,发展为涵盖网页数据抽取、机器学习、数据挖掘、语义理解等多种方法的综合应用。本节根据网页数据形式的不同,将基于网页内容的分析算法,归纳以下三类:第一种针对以文本和超链接为主的无结构或结构很简单的网页;第二种针对从结构化的数据源(如RDBMS)动态生成的页面,其数据不能直接批量访问;第三种针对的数据界于第一和第二类数据之间,具有较好的结构,显示遵循一定模式或风格,且可以直接访问。
基于文本的网页分析算法
(1)纯文本分类与聚类算法
很大程度上借用了文本检索的技术。文本分析算法可以快速有效的对网页进行分类和聚类,但是由于忽略了网页间和网页内部的结构信息,很少单独使用。
(2)超文本分类和聚类算法
根据网页链接网页的相关类型对网页进行分类,依靠相关联的网页推测该网页的类型。
HttpClient
HttpClient实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
支持 HTTPS 协议
支持代理服务器(Nginx等)等
支持自动(跳转)转向
由于今天我们主要讲解爬虫,所以不详细讲解HttpClient用法,最基础用法如下
string url = String.Format("https:/##########/");
HttpClient client = new HttpClient();
//结果
string result = client.GetStringAsync(url).Result;
这样 result内就可以拿到请求的值
HtmlAgilityPack
HtmlAgilityPack是.net下的一个HTML解析类库。支持用XPath来解析HTML。这个意义不小,为什么呢?因为对于页面上的元素的xpath某些强大的浏览器能够直接获取得到,并不需要手动写。节约了大半写正则表达式的时间,当然正则表达式有时候在进一步获取的时候还需要写,但是通过xpath解析之后,正则表达式已经要匹配的范围已经非常小了。而且,不用正则表达式在整个页面源代码上匹配,速度也会有提升。总而言之,通过该类库,先通过浏览器获取到xpath获取到节点内容然后再通过正则表达式匹配到所需要的内容,无论是开发速度,还是运行效率都有提升。
HtmlAttribute 对应 Html元素的属性
HtmlAttributeCollection 一个元素属性的集合,实现了IList, ICollection, IEnumerable, IEnumerable,都是集合的那一套东西,没有新东西。
HtmlNode 对应 HTML节点,包括注释,文本,元素等
HtmlNodeCollection 一个HtmlNode节点集合,实现了HtmlNodeCollection : IList, ICollection, IEnumerable, IEnumerable继承了这些东西就没什么需要说的了,都是集合的东西,没有新的东西。完全是集合那一套。
HtmlNodeType 一个枚举 表示节点的类型,文档,注释,元素,文本。
HtmlTextNode 对应Html文本节点,很简单的一个类,继承自HtmlNode。
HtmlEntity 对应实体 实用程序类以替换特殊字符的实体,反之亦然
HtmlParseError 表示文档在解析过程中发现的解析错误。
实战教学
首先先添加 HtmlAgilityPack 的 NuGet 的引用包
网页请求
我们以99医药网为例,网址为https://yyk.99.com.cn/
下面我们尝试爬取南京市的所有医院
首先请求网址 https://yyk.99.com.cn/nanjing/
string url = String.Format("https://yyk.99.com.cn/nanjing/");
HttpClient client = new HttpClient();
//结果
string result = client.GetStringAsync(url).Result;
打开网站后首先对网页进行分析
我们发现所有医院的信息都存放在的标签下,且除了医院之外没有其他标签
所以我们用以下代码筛选出医院信息
string url = String.Format("https://yyk.99.com.cn/nanjing/");
HttpClient client = new HttpClient();
//结果
string result = client.GetStringAsync(url).Result;
var doc = new HtmlDocument();
//加载结果
doc.LoadHtml(result);
//筛选出所有TD标签
HtmlNodeCollection maincontent = doc.DocumentNode.SelectNodes("//td");
if (maincontent != null)
{
foreach (var item in maincontent)
{
//遍历得到医院名称
var name = item.ChildNodes[0].InnerText;
}
}
目前到这一步,就可以拿到所有南京市医院的名称了。是不是很容易。下面我们更上一层楼。拿出所有医院的简介。如下图
我们接着回去分析网站结构,发现,想要进入到详情界面是更具href加上https://yyk.99.com.cn/ +href_+/jianjie.html 组成的请求地址。
所以改动代码为
string url = String.Format("https://yyk.99.com.cn/nanjing/");
HttpClient client = new HttpClient();
//结果
string result = client.GetStringAsync(url).Result;
var doc = new HtmlDocument();
doc.LoadHtml(result);
HtmlNodeCollection maincontent = doc.DocumentNode.SelectNodes("//td");
if (maincontent != null)
{
foreach (var item in maincontent)
{
var name = item.ChildNodes[0].InnerText;
var href = item.ChildNodes[0].Attributes["href"].Value;
string url1 = String.Format("https://yyk.99.com.cn"+href+ "/jianjie.html");
HttpClient client1 = new HttpClient();
//结果
string result1 = client1.GetStringAsync(url1).Result;
var doc1 = new HtmlDocument();
doc1.LoadHtml(result1);
}
}
这样我们就可以请求南京市所有的医院详情页面
下面接着分析详情界面 发现 相关信息存放在</dl class=‘wrap-info’> 和 </divclass=‘wrap-box’> 这两个标签内。
下面完整代码,将爬取到的数据存放在本地文件内。
public class YIYAO99
{
public YIYAO99()
{
}
public string Savepath = @"H:\医院信息";
public void Start()
{
string url = String.Format("https://yyk.99.com.cn/nanjing/");
HttpClient client = new HttpClient();
//结果
string result = client.GetStringAsync(url).Result;
var doc = new HtmlDocument();
doc.LoadHtml(result);
HtmlNodeCollection maincontent = doc.DocumentNode.SelectNodes("//td");
if (maincontent != null)
{
foreach (var item in maincontent)
{
var name = item.ChildNodes[0].InnerText;
var href = item.ChildNodes[0].Attributes[0].Value;
string url1 = String.Format("https://yyk.99.com.cn"+href+ "/jianjie.html");
HttpClient client1 = new HttpClient();
//结果
string result1 = client1.GetStringAsync(url1).Result;
var doc1 = new HtmlDocument();
doc1.LoadHtml(result1);
var maincontent1 = doc1.DocumentNode.SelectNodes("//dl[@class='wrap-info']");
var maincontent2 = doc1.DocumentNode.SelectNodes("//div[@class='wrap-box']");
FileStream fs = new FileStream(Savepath+$"\\{name}.txt", System.IO.FileMode.Create, System.IO.FileAccess.Write);
StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8);
sw.WriteLine(maincontent1[0].InnerText);
sw.WriteLine(maincontent2[0].InnerText);
sw.Close();
}
}
}
}