最简单的.NET爬虫
我的另一篇音视频编码转换:https://blog.csdn.net/jinglell/article/details/99677371
我给自己写的这个爬虫叫“最简单的.NET爬虫”,因为它实在是太过简单,当然最后的实现是简单的,前期的搜集资料和尝试其实也花了不少时间。
首先,我先到网上找了几个帖子,尤其是这位专门搞携程的老哥:https://www.cnblogs.com/jjg0519/p/6702747.html
直接百度“爬虫 携程”就可以找到这位大神了,我之前的爬虫都是没有接口需求的,就是数据分析课程需要爬取一些数据,直接用Python脚本就可以,反正最后只需要表格数据,而通过这次的查找,我对.NET爬虫印象.挺好的
说是速度快,什么什么的,反正看不太懂<_<,不过我对这个速度没什么太强的感受,因为我自己就是一次爬取一个页面而已,反而因为.NET没有beautifulsoup的存在,在gbk文本编码问题上卡了很长一段时间。
下面说我遇到的问题:
1.发起request请求
这里.NET里一般有两种,webclient
和httpclient
,httpclient在我的4.6.1版本需要添加引用,引用新的System.NET.HTTP,webclient不需要,下面是我一开始用的httpclient:
//HttpClient httpClient = new HttpClient();
//HttpResponseMessage res = httpClient.GetAsync(url).Result;
//if (res.IsSuccessStatusCode)
//{
//Task<string> t = res.Content.ReadAsStringAsync();
//string content = t.Result;
//content = EnCodeCovert(content);
这个形式我弄到最后是失败的,我记得应该是因为最后一行的原因导致乱码,所以最后舍弃了。
我现在采用的是WebClient,
System.Net.WebClient client = new System.Net.WebClient();
byte[] page = client.DownloadData(url);
string content = System.Text.Encoding.GetEncoding("GBK").GetString(page);
我自己感觉在request没有那么多需求,比如异步,而且request的网站并没有做一些拦截处理和一些验证时,WebClient更为简洁
2.文本编码问题
这个问题比上一个更耗费时间,我爬取的网站是gbk
格式的,爬过来是乱码,然后我上网找了采用了如下方式进行转码:
private static string EnCodeCovert(string value)
{
System.Text.Encoding srcEncode = System.Text.Encoding.GetEncoding("gb2312");
System.Text.Encoding convToEncode = System.Text.Encoding.Default;
byte[] bytes = srcEncode.GetBytes(value);
System.Text.Encoding.Convert(srcEncode, convToEncode, bytes, 0, bytes.Length);
return convToEncode.GetString(bytes);
}
private static string EnCodeCovert2(string str)
{
byte[] buffer = Encoding.Default.GetBytes(str);
byte[] buffers = Encoding.Convert(Encoding.Default, Encoding.UTF8, buffer);
return Encoding.UTF8.GetString(buffers);
}
事实又一次证明这玩意多么难搞,我当时还是用的httpclient,然后已经对数据通过正则表达式提取了乱码的gbk字符串,通过上述却怎么也转换不出来中文,最后从网上找了上述的webclient方式后,再对网页html数据直接解码再正则提取文本,最后实现了我的需求。
现在分析,我的总结就是,request返回的数据不要是字符串,而是字节流最好,然后直接对返回的字节流依据GBK格式转化成字符串就可以了。我对HttpClient了解少,我想应该也是有方法接收字节流的,那样就可以转码了。
3.正则表达式匹配
参考上面那位搞携程的前辈的写法:
var linkChapter = Regex.Matches
(
content,
@"<div[^>]+class=""nr_title""\sid=""nr_title"">(?<text>.*?)</div>",
//@"""><a[^>]+href=""*(?<href>/hotel/[^>\s]+)""\s*data-dopost[^>]*><span[^>]+>.*?</span>(?<text>.*?)</a>"
//"<a href= <href>/hotel/[ ] > <text> </a>",
//<div class="nr_title" id="nr_title"> 第一千八百二十三章 封界十万年!(终)</div>
RegexOptions.IgnoreCase
);
foreach (Match match in linkChapter)
{
novel.Chapter += match.Groups["text"].Value;
}
4.文本换行、空格
这个问题可以在上述正则匹配时进行处理:插入一个特殊字符串("ENTER"
)做间隔符
foreach (Match match in linksContext)
{
novel.Context += match.Groups["text"].Value+ "ENTER";
}
在小程序端可以对文本切割或是替换间隔符为“\n  ”
这里有一个小问题:空格在不同场景下的书写:
1’正则表达式:
\s
2’HTML:
一个中文字符| | 
半个中文字符| | 
正常的空格| |
最后附上我的整个function
public static string GetContext(string url)
{
var novel = new Novel();
System.Net.WebClient client = new System.Net.WebClient();
byte[] page = client.DownloadData(url);
string content = System.Text.Encoding.GetEncoding("GBK").GetString(page);
//HttpClient httpClient = new HttpClient();
//HttpResponseMessage res = httpClient.GetAsync(url).Result;
//if (res.IsSuccessStatusCode)
//{
//Task<string> t = res.Content.ReadAsStringAsync();
//string content = t.Result;
//content = EnCodeCovert(content);
var linkChapter = Regex.Matches
(
content,
@"<div[^>]+class=""nr_title""\sid=""nr_title"">(?<text>.*?)</div>",
//@"""><a[^>]+href=""*(?<href>/hotel/[^>\s]+)""\s*data-dopost[^>]*><span[^>]+>.*?</span>(?<text>.*?)</a>"
//"<a href= <href>/hotel/[ ] > <text> </a>",
//<div class="nr_title" id="nr_title"> 第一千八百二十三章 封界十万年!(终)</div>
RegexOptions.IgnoreCase
);
var linkBook = Regex.Matches
(
content,
@"<title>(?<text>.*?)</title>",
//@"<h1><a\s*id=""bookname"">(?<text>.*?)</a>",
//@"""><a[^>]+href=""*(?<href>/hotel/[^>\s]+)""\s*data-dopost[^>]*><span[^>]+>.*?</span>(?<text>.*?)</a>"
//"<a href= <href>/hotel/[ ] > <text> </a>",
//<h1><a href="/info/78738/" id="bookname">超级武神</a></h1>
RegexOptions.IgnoreCase
);
var linksContext = Regex.Matches
(
content,
@"( ){4}(?<text>.*?)<br />",
//"<a href= <href>/hotel/[ ] > <text> </a>",
// “聂天!”<br />
RegexOptions.IgnoreCase
);
foreach (Match match in linkBook)
{
novel.Book += match.Groups["text"].Value.Split('-')[1];
}
foreach (Match match in linkChapter)
{
novel.Chapter += match.Groups["text"].Value;
}
foreach (Match match in linksContext)
{
novel.Context += match.Groups["text"].Value+ "ENTER";
}
//novel.Context = System.Web.HttpUtility.UrlEncode(novel.Context, System.Text.Encoding.GetEncoding("gb2312"));
//var bytes = System.Text.Encoding.GetEncoding("GBK").GetBytes(novel.Context);
//novel.Context = System.Text.Encoding.UTF8.GetString(bytes);
//novel.Context = EnCodeCovert(novel.Context);
return Newtonsoft.Json.JsonConvert.SerializeObject(novel);
//}
//return string.Empty;
}