目录
爬虫分为两种,静态网页爬虫和动态网页爬虫,相比较于动态网页爬虫而言很简单,静态网页的爬取不需要执行如JavaScript类似的代码,只需要获取页面Html代码,并解析目标内容即可,本文介绍了静态网页爬取的基本流程。
确定目标内容和目标站点
明确需求,比如本文中需要爬取北京过去一段时间内的所有天气,首先找一个合适的网站,比如http://www.tianqihoubao.com,在该网站中,可以找到北京历史天气的站点http://www.tianqihoubao.com/lishi/beijing.html。
分析目标站点结构
在浏览器中打开北京历史天气站点后,不能直接获得天气,而是获得了许多超链接,其中有指向不同时间段的天气链接,因此,需要解析这个页面以分析出目标链接。
目标站点中有两类链接,一类是目标链接,另一类是其他链接。接下来在浏览器中按F12查看网页的源代码,检查目标链接的特点,这些特点有可能是Html节点或者Html属性,总之需要找到一个特点来区分目标链接和其他链接。
Html代码中,可以看到链接的两个区别
- 两种链接的节点层次是不一样的,目标链接中有div#content 其他链接中没有,因此可以根据节点的不同来选取链接,即选取有id属性值为content的节点;
- 最后的<a>节点中属性href的取值是不同的,目标链接总是含有"month",而其他链接中没有,因此可以根据最后一个节点的属性href的值含有"month"来选取链接。
网页获取
位于System.Net空间下的WebClient可以方便地获取网页,主要代码如下
public static string GetHtml(string url)
{
string res = "";
WebClient client = new WebClient();
Stream stream = client.OpenRead(url);
StreamReader sr = new StreamReader(stream, Encoding.Default);
res = sr.ReadToEnd();
sr.Close();
client.Dispose();
return res;
}
网页节点解析
这里有一个大名鼎鼎的库HtmlAgilityPack来辅助我们将字符串的网页html代码生成Dom树,并且让我们可以快速方便选取Dom树的节点,以完成在上节中的思路。VS打开工具->NuGet包管理器->管理解决方案的Nuget包,从这里可以搜索HtmlAgilityPack包进行相应的安装。在使用这个库时,需要引入HtmlAgilityPack命名空间。
using HtmlAgilityPack;
接下来,采取上节中的第二种思路来解析出目标链接。
- 选择Dom树中所有含有href属性的<a>节点,这会把所有超链接的节点都选择出来,包括目标链接和其他链接;
- 遍历所有链接节点,检查href属性的值是否以"/lishi/beijing/month"开头,是则加入链接集合
public static List<string> ParseLink(string html)
{
List<string> res = new List<string>();
var doc = new HtmlDocument();
doc.LoadHtml(html);
var linkNodes = doc.DocumentNode.SelectNodes("//a[@href]");
foreach (var linkNode in linkNodes)
{
string link = linkNode.GetAttributeValue("href", "");
if (link.StartsWith("/lishi/beijing/month"))
{
res.Add(link);
}
}
return res;
}
至此已经解析出该站点所有含有过去时间段的链接了,接下来需要分析每一个链接对应的具体网页的结构,从中获取天气信息。
分析天气网页结构
之前的链接http://www.tianqihoubao.com/lishi/beijing.html是一个站点链接,在上节中已经分析了具体的天气网页对应的链接集合,如http://www.tianqihoubao.com/lishi/beijing/month/201101.html。
现在需要从该页面中分析出天气,和解析站点结构中的目标链接一样,在浏览器中观察Html代码,发现天气这个目标内容在一个表格中,因此我们只需要选出<tr>节点就可以,因为一个<tr>节点表示某一天的天气(天气状况、气温和风力方向)。
public static void ParseDailyWeather(string html)
{
var doc = new HtmlDocument();
doc.LoadHtml(html);
var rows = doc.DocumentNode.SelectNodes("//tr");
StringBuilder sb = new StringBuilder();
rows.RemoveAt(0);
foreach (var row in rows)
{
var cols = row.SelectNodes("td");
foreach (var col in cols)
{
string temp = col.InnerText.Replace("\r\n", "").Replace(" ", "").Trim();
sb.Append(temp + ",");
}
sb.Append("\r\n");
}
FileStream fs = new FileStream("output.csv", FileMode.Append, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs, Encoding.GetEncoding("gbk"));
sw.WriteLine(sb);
sw.Close();
fs.Close();
}
最后,为了实现站点的爬取,需要一个循环来实现所有网页的爬取
public static void ParseWebsite(string url)
{
string html = Weather.GetHtml(url);
var links = Weather.ParseLink(html);
foreach (var link in links)
{
url = "http://www.tianqihoubao.com" + link;
html = Weather.GetHtml(url);
Weather.ParseDailyWeather(html);
}
}
总结
静态网页的爬取比较简单,分为以下几个步骤就可以
- 确定内容和站点。
- 分析站点结构。从站点结构中找出目标链接的特点,分析出所有目标链接。
- 分析网页结构。在目标链接的网页中找出目标内容的特点,提取目标内容。