爬取搜索引擎之寻你千百度

与天斗,其乐无穷;与地斗,其乐无穷

  自从Google退出中国市场,Baidu就成了国内搜索引擎巨头,所谓树大招风,一直以来百度成为国内众多黑客攻击的对象。又因为其本身作为一款搜索引擎,拥有很多的网络资源,因此借助baidu来获取海量数据,成为了一种便捷有效的信息收集途径。虽然baidu在爬虫算法上没有google那么优秀,但对中文搜索的支持并不会很差(小小吐槽一番百度),然而在通过百度爬取数据时,我们往往会遇到百度自身的反爬虫措施,如何解决这些反爬虫措施,将会是本文的重点。

  关于反爬虫的技术,网上有很多资源,方法不外乎(代理、识别验证码、分布式架构、模拟浏览器、ADSL切换ip等),这些不是本文的重点,本文只针对爬取百度搜索引擎时遇到的反爬虫措施,以及一些解决方案。

为甚么要爬取百度

  • 百度没有提供APi
  • 百度拥有丰富的资源可供查询
  • 百度反爬虫没有那么变态

百度反爬虫措施

  一般来说,单线程的爬虫时间间隔设置为>2s,短时间内应当不会被屏蔽,当然长时间爬取还是不行;如果使多线程无时间间隔爬取,那么大概30分钟肯定就会屏蔽了。
  我曾尝试过添加headers,甚至使用phantomjs模拟浏览器等方式,均以失败告终。我想百度作为一家搜索引擎公司,爬虫技术本就是其核心技术之一,因此跟它玩反爬虫技术应当是以卵击石(类似模拟浏览器,修改headers等方法应该无效)。
  然而我们可以换个思路,百度也不是不允许爬虫访问,只是限制了爬取频率。而对于访问的headers等信息并没有做明显的限制。那么也就是说,百度的反爬虫实际上是控制单ip访问的频率,那么我们就可以通过分布式架构或者切换ip等方式去解决。

被屏蔽现象

  在探讨如何解决被屏蔽问题前,我们先来研究下被百度屏蔽时的现象。一般来说,当百度检测到某ip访问流量特别大时,会先进行源码提示,如果还没停止访问,那么就会直接屏蔽访问。

源码提示网络异常

网页源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>百度--您的访问出错了</title>
<style>
body{text-align:center;margin-top:3px}
#wrap{width:650px;text-align:left;margin:auto}
#logo{float:left;margin:0 3px 0 0}
#logo img{border:0}
#title{float:left;width:510px}
#intitle{margin:20px 0 0 0;background-color:#e5ecf9;width:100%;font-weight:bold;
font-size:14px;padding:3px 0 4px 10px}
#content{clear:left;padding-top:60px;line-height:200%}
#vf{margin-top:10px}
#vf img{float:left;border:1px solid #000}
#kw{font:16px Verdana;height:1.78em;padding-top:2px}
#vf form{float:left;margin:12px 0 0 5px;padding:0}
#ft{text-align:center}
#ft,#ft a{color:#666;font-size:14px}
</style>
</head>
<body>
<div id="wrap">
<div id="logo"><a href="http://www.baidu.com"><img alt="到百度首页" title="到百
度首页" src="http://www.baidu.com/img/logo-yy.gif" width="137" height="46"></a><
/div>
<div id="title"><div id="intitle">您的访问出错了</div></div>
<div id="content">很抱歉,您的电脑或所在的“”“”的访问,此刻我们无法响应
您的请求。 <br>请输入以下验证码,即可恢复使用。</div>
<div id="vf">
<img src="http://verify.baidu.com/cgi-bin/genimg?6D8B74BFF43F7AE5457E1E8DA8C6335
5C8F00514C99AC6AD0182FCD695A4FED003A2592509E05792FF7A137E4184B4D9D9F5366F" width
="120" height="40">
<form action="http://verify.baidu.com/verify">
<input type="hidden" name="url" value="http://www.baidu.com/s?wd=.gov.cn&pn=0&vi
f=1">
<input type="hidden" name="vcode" value="6D8B74BFF43F7AE5457E1E8DA8C63355C8F0051
4C99AC6AD0182FCD695A4FED003A2592509E05792FF7A137E4184B4D9D9F5366F">
<input type="hidden" name="id" value="1488861310">
<input type="hidden" name="di" value="ad617386491a359a">
<input type="text" size="6" maxlength="10" name="verifycode" id="kw">
<input type="submit" value="提交">
</form>
</div>
<div style="clear:left;height:90px"></div>
<div id="ft">&copy;2014 Baidu <a href="http://www.baidu.com/duty/index.html">免
责声明</a></div>
</div>
<script>
(function(){
var rfr = window.document.location.href,
p = encodeURIComponent(rfr),
img = new Image(),
imgzd = new Image(),
re = /\/vcode\?http:\/\/(\S+)\.baidu/ig,r="";
img.src = "http://nsclick.baidu.com/v.gif?pid=201&pj=vcode&path="+p+"&t="+ne
w Date().getTime();
r = re.exec(rfr);
if(r&&r[1]){imgzd.src = "http://"+r[1]+".baidu.com/v.gif?fr=vcode&url="+p+"&
t="+new Date().getTime();}
})();
</script>
</body>
</html>

直接屏蔽Ip地址

此种情况访问会报错。

常规解决方案

  基于百度反爬虫的特点,我们可以通过分布式部署爬虫服务器,来采集资源,当然个人觉得ADSL服务器效果会更佳。但是分布式部署,尤其是ADSL服务器部署,成本会变得非常高,而且需要维护。那么有没有只用一台服务器就可以解决被屏蔽的问题呢?
  答案是肯定,那就是单机+多线程+ip代理,这种方式比较实惠,但比较考验ip代理的稳定性。经个人测试,感觉国内绝大部分代理(收费、免费、动态等)都不是很稳定,因此这是一种折中的方式,那么有没有更好的方式呢?

另类解决方案

  作为一家搜索引擎公司,百度的爬虫一定是分布式部署;又因为百度在国内的占有率很高,因此其提供搜索服务的服务器也应当是分布式部署的,也就是说全国各地部署了很多百度的服务器。
  那么当我们打开浏览器,访问百度时,提供搜索服务的服务器往往是离我们最近的那台,因此可以想见屏蔽我们的也就是那台服务器。大胆想象一下,如果我们能自由切换去访问不同地区的百度服务器,那么是否可以绕过被单一服务器屏蔽的问题呢?

当然这一解决方案的前提是:

  • 我们必须拥有大量的百度服务器的ip地址
  • 百度允许用ip地址访问(实在不行就更改host)

  可喜的是,以上2点都不难办到。网上有百度服务器的资源可以获取,当然也可以通过在不同地区的服务器ping百度获取ip;至于直接通过ip地址访问百度,这默认便是可行的(不知道百度为何这样设置)

百度的大招

c通过以上几种方式,应该可以绕过百度的反爬虫机制,但是百度也不是吃素的,它也有自己独特的反爬虫杀招,或许称之为”搜索限制”或者是”资源保护”措施更合适一点。

搜索结果数设上限

通过百度搜索引擎搜索关键词,计算出来的结果数设有上限。

此数量最高显示上限是1亿,其实远远不止,因此数据是不真实的。

搜索页面数设上限

再看搜索的结果页面数:

最多只显示76页,而这只是所有结果中的冰山一角。

cookies影响搜索结果

在几次爬取过程中,我无意发现在headers中加不加cookies会影响最终的搜索结果(主要影响搜索结果的多少)。

以上几点严格意义上来说,并不算反爬虫技术,只是一种保护自身资源的方式,其意不言而喻

Baidu_link问题

  通过获取百度搜索结果源码,以及通过正则匹配,我们能够得到一些搜索结果链接,然后这些链接并不是网站原链接,有以下2种形式:

1
2
3
http://www.baidu.com/link?url=1qIAIIh_2N7LUQpI0AARembLK2en4QpGjaRqKZ3BxYtzoZYevC5jA2jq6XMwgEKF&wd=&eqid=9581fbec0007eae00000000458200ad4
http://www.baidu.com/link?url=1qIAIIh_2N7LUQpI0AARembLK2en4QpGjaRqKZ3BxYtzoZYevC5jA2jq6XMwgEKF

  我暂且称它为”百度链接”,其基本就是以上2种形式。第一种是通过点击右键复制链接地址获取到的,通常带有eqid参数,用来表示referer;第二种是通过页面源代码获取到的,这种是不带wd与eqid参数的。而eqid参数的值在每次刷新页面后,都会改变,这可能是百度限制黑帽SEO所设置的一个参数。
  那么我们比较两者之差异,当我们分别取访问这2条连接时,返回的数据包是不一样的。

带eqid参数

第一种带eqid参数的会返回200,在body里面会有网站真实的链接,可以通过正则匹配:

1
res_baidu=r"window\.location\.replace\(\"([^\"]*)\"\)"

不带eqid参数

第二种不带参数的会返回一个302跳转,并且在header会有location字段,可以通过requests模块(head模式)去访问获取。

解析baidu_link模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#! -*- coding:utf-8 -*-
'''
@解析baidu_link
'''
__author__="nMask"
__Blog__="http://thief.one"
__Date__="20170301"
import requests
import re
res_baidu=r"window\.location\.replace\(\"([^\"]*)\"\)"
class anbaidulink:
headers={'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6',
'Referer':'http://www.baidu.com/link?url='}
def __init__(self):
pass
def run(self,url,one_proxy=""):
'''
入口函数,接受baidu_link以及代理地址,默认为"",代理地址要是http://xx.xx.xx.xx:xx格式
'''
if "&eqid=" in url:
url=self.have_eqid(url,one_proxy)
else:
url=self.noeqid(url,one_proxy)
return url
def noeqid(self,url,one_proxy):
'''
针对baidu_link中没有eqid参数
'''
try:
h=requests.head(url,proxies={'http':one_proxy},headers=anbaidulink.headers,timeout=5).headers #
except Exception,e:
print e
else:
url=h["location"]
return url
def have_eqid(self,url,one_proxy):
'''
针对baidu_link中存在eqid参数
'''
try:
body=requests.get(url,proxies={'http':one_proxy},headers=anbaidulink.headers,timeout=5).content #
except Exception,e:
print e
else:
p=re.compile(res_baidu)
url=p.findall(body)
if len(url)>0:
url=url[0]
return url
if __name__=="__main__":
cur=anbaidulink()
url=cur.run(url='https://www.baidu.com/link?url=1qIAIIh_2N7LUQpI0AARembLK2en4QpGjaRqKZ3BxYtzoZYevC5jA2jq6XMwgEKF&wd=&eqid=9581fbec0007eae00000000458200ad4',one_proxy="")
#url=cur.run(url='http://www.baidu.com/link?url=1qIAIIh_2N7LUQpI0AARembLK2en4QpGjaRqKZ3BxYtzoZYevC5jA2jq6XMwgEKF',one_proxy="")
print url

申明:本文只是列举了我在爬取百度资源时遇到的问题,不代表百度本身所有的反爬虫技术,本文提供的解决方案具有时效性,具体还需自己动手实验,如有更好的解决方案可留言交流哦

本文地址:http://thief.one/2017/03/17/爬搜索引擎之寻你千百度/
转载请说明来自:nMask’Blog

传送门

爬取搜索引擎之搜狗
爬取搜索引擎之寻你千百度

本文标题:爬取搜索引擎之寻你千百度

文章作者:nMask

发布时间:2017年03月17日 - 16:03

最后更新:2017年07月25日 - 20:07

原始链接:http://thief.one/2017/03/17/爬搜索引擎之寻你千百度/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

nMask wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!