(-)理解原理,如何用集合实现一个简单但是完整的爬虫
## 前言
可能对于绝大多数的同学,或者比如我来说,爬虫就是模拟Http请求去获取网页上想获取的内容,这当然是爬虫最最基本的原理描述。
但是对于一个可以平稳运行、持续运行的爬虫来说,他的原理可以这样描述:现有一个种子站点,通过对种子站点的爬虫,获取到种子站点的a链接,将这时获取的url先进行比对是否为已经访问的链接,如果否就放入待爬任务队列。
这样想来,一个可以持续平稳运行的爬虫大概要具有如下基本特征:
1. 种子站点存储和待爬任务存储;
2. 已访问任务存储,将要作为新产生任务的比对依据;
3. 页面下载器;
4. 页面分析器。
开发工具:Vs2017;
代码地址:[基本架构] (https://gitee.com/gainorloss_259/Zj.BaiduPicSpider/tree/master/src/Zj.BaiduPicSpider/Crawer.ConsoleApp)
## 项目结构
## 开始开发
### 1. 新建项目
建立一个简单的控制台项目,这里选用Framework或者Core都可以,创建普通类BaseAspectSample即可,这里为了更简单地向大家理解原理,就不再定义接口和更多的分层,一切都将在本类内展开;
### 2. 定义爬虫的起点
起点是一个种子站点,这里我们以https://www.baidu.com为例,实际上选取任意一个可访问的链接都不影响本程序的使用;
因为还需要存储待抓任务和已抓任务所以我们需要两个集合来分别存储这两者。
‘
private static List<string> _todo = new List<string>();
private static List<string> _visited = new List<string>();
static string StartPointAddress = “https://www.baidu.com”;
‘
### 3. 开始运行
首先需要通过对种子站点的爬虫和分析添加我们的第一批任务,然后就可通过对这批任务的抓取让我们的爬虫产生一个完美的循环,
这里我们需要一直对待爬任务进行轮询,已确保任务一直进行,并且要把已经爬取得url存储至相应的集合里。
‘
RequestSite(StartPointAddress);
while (_todo.Count > 0)
{
var currentUrl = _todo[0];
RequestSite(currentUrl);
_visited.Add(currentUrl);
_todo.Remove(currentUrl);
}
‘
### 4. 请求url的实现
需要过滤掉不可访问的url,这里采取简单的过滤手段
‘
if (url.StartsWith(“http”))
{
var client = new HttpClient();
string rt = string.Empty;
try
{
rt = client.GetStringAsync(url).Result;
}
catch (Exception ex)
{}
_todo.AddRange(GetLinks(rt));
}
‘
### 5. 分析页面得到url
个人比较喜欢xpath这种写法,感觉蛮强大的,目前使用的是使用比较广泛的HtmlAgilityPack
‘
var links = new List<string>();
HtmlDocument document = new HtmlDocument();
document.LoadHtml(rt);
var aNodes = document.DocumentNode.SelectNodes(“//a”);
if (aNodes != null)
{
foreach (var item in aNodes)
{
var hrefAttr = item.Attributes[“href”];
if (hrefAttr != null)
{
var url = hrefAttr.Value;
if (!_visited.Contains(url))
{
links.Add(url);
}
}
}
}
‘
## 备注
实现过程中需要注意一下三点
* url的过滤,死链导致推出;
* HttpClient访问url异常,导致的推出,异常处理保证程序平稳运行;
* 页面分析a链接相关获取空值判断导致的异常退出。