接下来一段时间,我会花些时间研究C#玩转爬虫
的方法及其实践。
话不多说,开始吧~
一般来说:
设计并实现一个爬虫
的步骤是:
模拟登录 -> 模拟发送request请求 -> 取回response数据 -> 提取所需信息并将其进行重新组织 -> 存入DB或文件中 -> 后期处理或展示
当然,有时还需要适当地应对所抓取目标站点的反爬虫策略
,也就是大家常说的反反爬
!
下文以抓取豆瓣音乐
为例来具体说明:
比如,我们在豆瓣音乐中搜索"摇滚"后爬取相关内容,然后写入csv文件中。
目标网址是:
https://music.douban.com/tag/摇滚?start=0&type=T
抓取大概分为如下几个步骤:
每一页中都可以看到总的页数,直接取第1页
的就好。
byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=0&type=T");
// utf-8, gb2312, gbk, utf-1......
string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);
借助以上两行代码,就可以看到该网址Response的HTML字符串(debug时上面一行代码中html
的值)为:
我们选用模式串 ">[0-9][0-9]{0,}</a>"
进行匹配,由于下面的页码是从第一页算起的,选最大页数就是总页数。
相应的函数GetTotalCount
如下:
private static int GetTotalCount(System.Net.WebClient webclient, int startIdx)
{
//html下载
/* https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=0&type=T */
byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=" + startIdx + "&type=T");
// utf-8, gb2312, gbk, utf-1......
string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);
MatchCollection pageCount_matches = new Regex(">[0-9][0-9]{0,}</a>").Matches(html);
if (pageCount_matches.Count == 0)
return 0;
string tempPageNum = pageCount_matches[pageCount_matches.Count - 1].Value;
int.TryParse(tempPageNum.Replace("</a>", string.Empty).Substring(1), out int lastPageNum);
return lastPageNum;
}
用与上一步相同的方法
byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=0&type=T");
// utf-8, gb2312, gbk, utf-1......
string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);
debug时可得到当前页对应的HTML字符串:
我们选用模式串 "<p class=\"pl\">([\\s\\S]*?)</p>"
进行匹配,可得到如下格式的内容:
刺猬 / 2018-01-08 / 单曲 / 数字(Digital) / 摇滚
,
接着进行split可得到一个记录的各个属性。
具体的函数GetCurrentPageRecords
如下:
private static void GetCurrentPageRecords(System.Net.WebClient webclient, int startIdx)
{
byte[] buffer = webclient.DownloadData("https://music.douban.com/tag/%E6%91%87%E6%BB%9A?start=" + startIdx + "&type=T");
// utf-8, gb2312, gbk, utf-1......
string html = System.Text.Encoding.GetEncoding("utf-8").GetString(buffer);
// Console.WriteLine(html);
// html分析
// 通过正则获取到需要的数据
// <p class="pl">刺猬 / 2018-01-08 / 单曲 / 数字(Digital) / 摇滚</p>
MatchCollection musicItem_matches = new Regex("<p class=\"pl\">([\\s\\S]*?)</p>").Matches(html);
int count = musicItem_matches.Count;
for (int i = 0; i < count; i++)
{
string item = musicItem_matches[i].Result("$1");
string[] strArr = item.Trim().Split('/');
Record record = new Record
{
SongName = strArr[0],
Date = strArr.ElementAtOrDefault(1),
Album = strArr.ElementAtOrDefault(2),
Type = strArr.ElementAtOrDefault(3),
Topic = strArr.ElementAtOrDefault(4)
};
_records.Add(record);
}
}
https://music.douban.com/tag/摇滚?start=0&type=T
第一页的start=0,每页20条,于是后面每一页的start是前一页start的值+20
CSVHelper
.Record
Record.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace CsSpider
{
public class Record
{
public string SongName { get; set; }
public string Date { get; set; }
public string Album { get; set; }
public string Type { get; set; }
public string Topic { get; set; }
}
}
// 举例: 万能青年旅店 / 2010-11-12 / 专辑 / CD / 摇滚
if (_records.Count > 0)
{
var writer = new StreamWriter(_filename);
using (var csv = new CsvWriter(writer))
{
csv.WriteRecords(_records);
Console.WriteLine("导出完成.");
}
}
解决乱码问题
只需要给上述writer
设置Encoding即可~
if (_records.Count > 0)
{
var writer = new StreamWriter(_filename, false, System.Text.Encoding.UTF8); // 设置Encoding,防止乱码
using (var csv = new CsvWriter(writer))
{
csv.WriteRecords(_records);
Console.WriteLine("导出完成.");
}
}
最后得到的CSV文件如下:
接下来,会发现一个问题:
如果多执行几次,前面的HTML字符串会变成下面这种内容:
<script>var d=[navigator.platform,navigator.userAgent,navigator.vendor].join("|");window.location.href="https://sec.douban.com/a?c=3d001f&d="+d+"&r=https%3A%2F%2Fmovie.douban.com%2Fsubject%2F26590960%2F";</script>
说明此时,你触发了豆瓣的反作弊功能。如果再多执行几次,会出现 403 Error.
解决办法是模拟登录,这个留在下一篇文章再说,敬请关注!