网络爬虫之投票
目录
一、前言
在你心中什么是网络爬虫?在网线里钻来钻去的小虫?先看一下百度百科的解释:
网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。
看完之后有何感想,其实百度、Bing、Google等都是大网络爬虫。当然我们不可能去做一个像百度一样的搜索引擎,那么爬虫对我们普通人有什么用?用处很大,小到刷你的博客流量、大到获取商业机密(何为商业机密?进入大数据时代,一个公司的数据就是其最大商业机密,所以要想获取途牛、淘宝、京东的商业机密很简单————爬吧)。本文不介绍这么高大上的(其实实现方法都一样),介绍一个所有人都深受其害的(是否有被人邀请投票的经历?)————投票。
二、起因
这几天百忙中用网络爬虫做了一个网络自动投票器,结果很简单,过程较艰难。又想到很多同胞跟我一样深受“拉票之害”,简单记录之,用于总结网络爬虫的功能,希望每个人看完都能自己写个投票器自己刷票(投票不求人),哈哈。
三、主要技术点
- 网络抓包
- dom树分析
- winform网络请求
- 模拟登录
- 字符串处理
3.1 网络抓包
写网络爬虫最重要的就是抓包,抓包在百度百科中的解释如下:
抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。
理论上凡是通过网卡的数据都能通过抓包的方式进行分析,在网络爬虫中主要抓的是HTTP/HTTPS
协议的数据,这两种数据也就是通过浏览器能够正常访问的数据。HTTP/HTTPS
协议抓包工具有很多,个人觉得比较好的是Fiddler,关于其下载和用法网上很多,不做这里赘述。
比如我们要写一个投票工具,那么主要考虑抓这么几个数据:
- 最重要的是投票地址。点击你要投票的对象的投票按钮,这时候浏览器会自动向投票地址发请求,这当然逃不过Fiddler的火眼金睛。我们就可以把这个网址记录下来用于写投票器。当然如果你的投票没有登录、没有其他环节,就是点一下投票二字即可,那么你走运了,到这一步你的工作已经结束了。
- 如果投票有登录也不要慌,在登录页面输入用户名密码,点击登录,同样会在Fiddler中留下登录的链接地址。
- 其他具体情况具体分析。
这里需要说明的是HTTP请求分为POST、GET等方式(具体可以百度之),凡是通过浏览器直接输入地址显示出来的页面肯定都是GET请求,像投票请求两种方式均有可能,登录请求基本上可以肯定是POST。POST和GET的不同就在于POST能够提交表单数据,所以需要我们根据自己的用户名密码等拼接表单数据。
3.2 dom树分析
简单投票器一般不需要进行DOM树分析,只有极个别的情况需要(恰好此次被我碰到),其他网络爬虫如数据获取等是肯定需要进行此项分析的。我在这里将其宽泛化,把JS脚本解析等都归入此类中,这需要我们掌握一些HTML的基础知识,包括HTML标签,像<input>、<a>、<span>、<div>等以及javascript语法、CSS语法等。
3.3 winform网络请求
进入到今天的关键阶段,通过上述步骤我们获取到了投票地址,比如(http://www.****.cn/vote.php?id=100)。那么我们如何通过程序发送请求呢?总不能让我们在浏览器输入链接然后狂按回车吧?这样就起不到投票器的作用了。
其实也很简单,很多语言中都已经内置了网络请求模块,我们以C#为例,可以采用下述方法进行请求。
public string GetContent(string method, string url, string postData = "", CookieContainer cookie = null)
{
HttpWebResponse response = null;
HttpWebRequest request = null;
if (cookie == null)
cookie = new CookieContainer();
// 准备请求...
try
{
// 设置参数
request = WebRequest.Create(url) as HttpWebRequest;
request.CookieContainer = cookie;
request.AllowAutoRedirect = true;
request.Method = method.ToUpper();
request.ContentType = "application/x-www-form-urlencoded";
string userAgent = string.Format("Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.8670)");
request.UserAgent = userAgent;
request.ContentLength = postData.Length;
if (method.ToUpper() == "POST")
{
if (!string.IsNullOrEmpty(postData))
{
byte[] data = Encoding.Default.GetBytes(postData);
request.ContentLength = data.Length;
using (Stream outstream = request.GetRequestStream())
{
outstream.Write(data, 0, data.Length);
}
}
}
//发送请求并获取相应回应数据
response = request.GetResponse() as HttpWebResponse;
//直到request.GetResponse()程序才开始向目标网页发送Post请求
using (Stream instream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(instream, encoding))
{
//返回结果网页(html)代码
string content = sr.ReadToEnd();
return content;
}
}
}
catch (Exception ex)
{
string err = ex.Message;
return err;
}
finally
{
if (response != null)
response.Close();
}
}
上述代码实现了GET和POST两种请求方式,其中method即为上述两种方法,url就是你找到的请求。postData是你拼接的表单数据。该方法返回的是页面的HTML代码。
所以是不是很容易,如果你的投票就是点一下就能投的类型,恭喜你,通过上面的代码加一个循环你就可以开启疯狂刷票模式。但是事与愿违,很多投票都不是那么简单,最大的障碍就是有些投票要先登录。
3.4 模拟登录
其实WEB端的登录就是向服务器获取一个标识,这个标识学名Cookie,当我们点击登录的时候向服务器发送一个通知,通知服务器我来了,服务器收到后给你一个腰牌(Cookie)说:我知道了,你拿着这个腰牌以后见牌如见君。这样我们后面再点投票的时候服务器就知道是你投的票,不是其他冒牌货。所以关键问题就在如何获取Cookie。
其实上面的代码已经包含了这个功能,你只需要在使用GetContent函数发送登录请求的时候接受其中的Cookie即可。所以登录请求和投票请求都可以使用上述代码,上述代码也就是Winform程序发送网络请求的核心代码。
3.5 字符串处理
在这讲述伟大的爬虫的时候我为什么要再讲字符串处理呢,其实这次爬虫投票器差点就在字符串处理这个问题上前功尽弃,几乎已经开始构思更牛逼的方法。这次抓包发现发送投票请求的时候向服务器多发送了一个参数,名称为hc
,我一看小意思这应该是一个时间标识一样的东西,将其置之不理,可是始终得不到正确的投票结果,一直说投票无效,猜想难道这个投票网站这么先进?没有道理啊,不存在比人脑还先进的电脑(除非你是阿法狗)。然后将程序的JS代码以及DOM树认认真真的分析了一遍,发现其hc其值取于一个类型为"hidden"的input域,每次请求该值都会发生变化,所以我一直投票不成功,然后想这就简单了,每次请求一下这个网页,取出该值就好,代码如下:
string GetHc(CookieContainer cookie)
{
var url = "http://......";
var res= GetContent("GET", url, cookie: cookie);
var index = res.IndexOf("<input type="hidden" id="hc" value="");
if (index >= 0)
return res.Substring(index, 8);
return "";
}
代码很容易,因为取的值简单也没有用正则表达式,只是使用了Substring。满心欢喜以为大功告成,谁知依然是悲剧,百思不得其解,无论怎样都不行。就在我正准备放弃,使用更牛逼的武器的时候。详情请见(使用selenium+phantomJS实现网页爬取)。突然发现得到的hc根本就不是正确的hc,不知道各位看官是否瞧出?因为IndexOf获取的是匹配字符的开头的序号,所以此处根本没有获取到hc。正确的方法应该是res.Substring(index + "<input type="hidden" id="hc" value="".Length, 8)
。改完之后立马投票成功。
四、总结
本文简单介绍了爬虫所涉及到的相关知识,以及如何解析出一个投票器的请求链接。冰冻三尺非一日之寒,任何学问都涉及到方方面面的知识,唯有沉淀下来融入进去方能收获最好的自己。尤其还需要注意细节,以及坚持,如果不是多实验了一次发现Substring的这个问题,恐怕也就没有这篇文章了。
五、后记
各位看官写个投票器是不是很容易?其实这里面牵扯到的问题很多,比如如果有验证码,投票器写不了,比如手机登录、微信里面投票什么的写不了。再者说网络刷票没有逃避监管的方式,凡是想追踪都能通过后台数据分析出来。所以各位看官,刷票有风险,操作需谨慎,各种利弊自己权衡。