如何让爬虫一天抓取100万张网页

前一两年抓过某工商信息网站,几三周时间大约抓了过千万多万张页面。那时由于公司没啥经费,报销又拖得很久,不想花钱在很多机器和带宽上,所以当时花了较多精力研究如何让一台爬虫机器达到抓取极限。

本篇偏爬虫技术细节,先周知。

爬虫这两年貌似成为了一项必备技能,无论是搞技术的,做产品的,数据分析的,金融的,初创公司做冷启动的,都想去抓点数据回来玩玩。这里面绝大多数一共都只抓几万或几十万条数据,这个数量级其实大可不必写爬虫,使用 chrome 插件 web scraper 或者让 selenium 驱动 chrome 就好了,会为你节省很多分析网页结构或研究如何登陆的时间。

本篇只关注如何让爬虫的抓取性能最大化上,没有使用scrapy等爬虫框架,就是多线程+Python requests库搞定。

对一个网站定向抓取几十万张页面一般只用解决访问频率限制问题就好了。对机器内存,硬盘空间,URL去重,网络性能,抓取间隙时间调优一般都不会在意。如果要设计一个单台每天抓取上百万张网页,共有一亿张页面的网站时,访问频率限制问题就不是最棘手的问题了,上述每一项都要很好解决才行。硬盘存储,内存,网络性能等问题我们一项项来拆解。

优化硬盘存储

所以千万级网页的抓取是需要先设计的,先来做一个计算题。共要抓取一亿张页面,一般一张网页的大小是400KB左右,一亿张网页就是1亿X200KB=36TB 。这么大的存储需求,一般的电脑和硬盘都是没法存储的。所以肯定要对网页做压缩后存储,可以用zlib压缩,也可以用压缩率更好的bz2或pylzma 。

但是这样还不够,我们拿天眼查的网页来举例。天眼查一张公司详情页的大小是700KB 。

对这张网页zlib压缩后是100KB。

一亿个100KB(9TB)还是太大,要对网页特殊处理一下,可以把网页的头和尾都去掉,只要body部分再压缩。因为一张html页面里<head></head>和<footer></footer>大都是公共的头尾信息和js/css代码,对你以后做正文内容抽取不会影响(也可以以后做内容抽取时把头尾信息补回去就好)。

来看一下去掉头尾后的html页面大小是300KB,压缩后是47KB。

一亿张就是4T,差不多算是能接受了。京东上一个4T硬盘600多元。

优化内存,URL去重

再来说内存占用问题,做爬虫程序为了防止重复抓取URL,一般要把URL都加载进内存里,放在set()里面。拿天眼查的URL举例:

https://www.tianyancha.com/company/23402373

这个完整URL有44个字节,一亿个URL就是4G,一亿个URL就要占用4G内存,这还没有算存这一亿个URL需要的数据结构内存,还有待抓取URL,已抓取URL还保存在内存中的html等等消耗的内存。

所以这样直接用set()保存URL是不建议的,除非你的内存有十几个G。

一个取巧的办法是截断URL。只把URL:

https://www.tianyancha.com/company/23402373

的后缀:23402373放进set()里,23402373只占8个字节,一亿个URL占700多M内存。

但是如果你是用的野云主机,用来不断拨号用的非正规云主机,这700多M内存也是吃不消的,机器会非常卡。

就还需要想办法压缩URL的内存占用,可以使用BloomFilter算法,是一个很经典的算法,非常适用海量数据的排重过滤,占用极少的内存,查询效率也非常的高。它的原理是把一个字符串映射到一个bit上,刚才23402373占8个字节,现在只占用1个bit(1字节=8bit),内存节省了近64倍,以前700M内存,现在只需要10多M了。

BloomFilter调用也非常简单,当然需要先install 安装bloom_filter:

frombloom_filter import BloomFilter# 生成一个装1亿大小的bloombloom = BloomFilter(max_elements=100000000, error_rate=0.1)# 向bloom添加URLbloom.add('https://www.tianyancha.com/company/23402373')#判断URL是否在bloombloom.__contains__('https://www.tianyancha.com/company/23402373')

不过奇怪,bloom里没有公有方法来判断URL是否重复,我用的__contains__()方法,也可能是我没用对,不过判重效果是一样的。

反抓取访问频率限制

单台机器,单个IP大家都明白,短时间内访问一个网站几十次后肯定会被屏蔽的。每个网站对IP的解封策略也不一样,有的1小时候后又能重新访问,有的要一天,有的要几个月去了。突破抓取频率限制有两种方式,一种是研究网站的反爬策略。有的网站不对列表页做频率控制,只对详情页控制。有的针对特定UA,referer,或者微信的H5页面的频率控制要弱很多。我在这两篇文章有讲到《爬虫小偏方:绕开登陆和访问频率控制》《爬虫小偏方二:修改referer后可以不用登录了》。

另一种方式就是多IP抓取,多IP抓取又分IP代理池和adsl拨号两种,我这里说adsl拨号的方式,IP代理池相对于adsl来说,我觉得收费太贵了。要稳定大规模抓取肯定是要用付费的,一个月也就100多块钱。

adsl的特点是可以短时间内重新拨号切换IP,IP被禁止了重新拨号一下就可以了。这样你就可以开足马力疯狂抓取了,但是一天只有24小时合86400秒,要如何一天抓过百万网页,让网络性能最大化也是需要下一些功夫的,后面我再详说。

至于有哪些可以adsl拨号的野云主机,你在百度搜"vps adsl",能选择的厂商很多的。大多宣称有百万级IP资源可拨号,我曾测试过一段时间,把每次拨号的IP记录下来,有真实二三十万IP的就算不错了。

选adsl的一个注意事项是,有的厂商拨号IP只能播出C段和D段IP,110(A段).132(B段).3(C段).2(D段),A和B段都不会变,靠C,D段IP高频次抓取对方网站,有可能对方网站把整个C/D段IP都封掉。

C/D段加一起255X255就是6万多个IP全都报废,所以要选拨号IP范围较宽的厂商。 你要问我哪家好,我也不知道,这些都是野云主机,质量和稳定性本就没那么好。只有多试一试,试的成本也不大,买一台玩玩一个月也就一百多元,还可以按天买。

上面我为什么说不用付费的IP代理池?

因为比adsl拨号贵很多,因为全速抓取时,一个反爬做得可以的网站10秒内就会封掉这个IP,所以10秒就要换一个IP,理想状况下一天86400秒,要换8640个IP。

如果用付费IP代理池的话,一个代理IP收费4分钱,8640个IP一天就要345元。 adsl拨号的主机一个月才100多元。

adsl拨号Python代码

怎么拨号厂商都会提供的,建议是用厂商提供的方式,这里只是示例:

windows下用os调用rasdial拨号:

importos# 拨号断开os.popen('rasdial 网络链接名称 /disconnect')# 拨号os.popen('rasdial 网络链接名称 adsl账号 adsl密码')

linux下拨号:

import os# 拨号断开code = os.system('ifdown 网络链接名称')# 拨号code = os.system('ifup 网络链接名称')

网络性能,抓取技术细节调优

上面步骤做完了,每天能达到抓取五万网页的样子,要达到百万级规模,还需把网络性能和抓取技术细节调优。

1.调试开多少个线程,多长时间拨号切换IP一次最优。

每个网站对短时间内访问次数的屏蔽策略不一样,这需要实际测试,找出抓取效率最大化的时间点。先开一个线程,一直抓取到IP被屏蔽,记录下抓取耗时,总抓取次数,和成功抓取次数。 再开2个线程,重复上面步骤,记录抓取耗时,总的和成功的抓取次数。再开4个线程,重复上面步骤。整理成一个表格如下,下图是我抓天眼查时,统计抓取极限和细节调优的表格:

从上图比较可以看出,当有6个线程时,是比较好的情况。耗时6秒,成功抓取80-110次。虽然8个线程只耗时4秒,但是成功抓取次数已经在下降了。所以线程数可以设定为开6个。

开多少个线程调试出来了,那多久拨号一次呢?

从上面的图片看到,貌似每隔6秒拨号是一个不错的选择。可以这样做,但是我选了另一个度量单位,就是每总抓取120次就重新拨号。为什么这样选呢?从上图也能看到,基本抓到120次左右就会被屏蔽,每隔6秒拨号其实误差比较大,因为网络延迟等各种问题,导致6秒内可能抓100次,也可能抓120次。

2.requests请求优化

要优化requests.get(timeout=1.5)的超时时间,不设置超时的话,有可能get()请求会一直挂起等待。而且野云主机本身性能就不稳定,长时间不回请求很正常。如果要追求抓取效率,超时时间设置短一点,设置10秒超时完全没有意义。对于超时请求失败的,大不了以后再二次请求,也比设置10秒的抓取效率高很多。

3.优化adsl拨号等待时间

上面步骤已算把单台机器的抓取技术问题优化到一个高度了,还剩一个优化野云主机的问题。就是每次断开拨号后,要等待几秒钟再拨号,太短时间内再拨号有可能又拨到上一个IP,还有可能拨号失败,所以要等待6秒钟(测试值)。所以要把拨号代码改一下:

importos# 断开拨号os.popen('rasdial 网络名称 /disconnect')time.sleep(6)# 拨号os.popen('rasdial 网络名称 adsl账号名 adsl密码')

而且 os.popen('rasdial 网络名称 adsl账号名 adsl密码') 拨号完成后,你还不能马上使用,那时外网还是不可用的,你需要检测一下外网是否联通。

我使用 ping 功能来检测外网连通性:

importoscode = os.system('ping www.baidu.com')

code为0时表示联通,不为0时还要重新拨号。而ping也很耗时间的,一个ping命令会ping 4次,就要耗时4秒。

上面拨号等待6秒加上 ping 的4秒,消耗了10秒钟。上面猿人学Python说了,抓120次才用6秒,每拨号一次要消耗10秒,而且是每抓120次就要重拨号,想下这个时间太可惜了,每天8万多秒有一半时间都消耗在拨号上面了,但是也没办法。

当然好点的野云主机,除了上面说的IP范围的差异,就是拨号质量差异。好的拨号等待时间更短一点,拨号出错的概率要小一点。

通过上面我们可以轻松计算出一组抓取的耗时是6秒,拨号耗时10秒,总耗时16秒。一天86400秒,就是5400组抓取,上面说了一组抓取是120次。一天就可以抓取5400X120=64万张网页。

按照上述的设计就可以做到一天抓60多万张页面,如果你把adsl拨号耗时再优化一点,每次再节约2-3秒,就趋近于百万抓取量级了。

另外野云主机一个月才100多,很便宜,所以你可以再开一台adsl拨号主机,用两台一起抓取,一天就能抓一百多万张网页。几天时间就能镜像一个过千万网页的网站。

知识Tips:

1.为什么不用异步抓取?

没必要,这里的整个抓取关键是网络性能,而不是程序性能。用异步把程序性能提高了,单位时间的抓取次数是提高了,但是这样反而会击中对方网站的访问频率控制策略。

2.要计算对方的带宽压力,不要抓取太过分了

抓取归抓取,但不要影响对方网站,把对方网站带宽都打满了。

一个中小型网站的带宽在5M以内,大一点的网站带宽可能10-30M,超大型的另算。

一张网页300KB,对方一般会压缩后传输给浏览器,就按压缩后30KB算,你的爬虫一秒请求20次,带宽就是600KB。可能一个网站每天都有几十个爬虫都在爬,我们按有10个爬虫在同时抓取,就是这些爬虫一秒内就要消耗600KBX10=6M带宽。

再加上还有正规爬虫,人家网站上的正常用户访问这些,算下来可能一共要消耗10M带宽。一般的大中型网站都是吃不消的

源网络,版权归原创者所有。如有侵权烦请告知,我们会立即删除并表示歉意。


更多技术,欢迎关注下方公众号

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 简要[1] 优化存储硬盘[2] 优化内存,URL去重[3] 网络性能,抓取技术细节调优[4] 反抓取访问频率限制[...
    是东东阅读 542评论 0 0
  • 有个段子说:“产品经理是CEO的学前班”,那下一步就是,“升职加薪,当上总经理、出任CEO、赢取白富美、走上人生巅...
    几米进化ing阅读 368评论 1 1
  • 做手机适配的时候,不能像做web端一样直接写固定值,有几种方法可以让页面在不同宽度屏幕上自适应。下面的都是以750...
    杉虎阅读 1,935评论 0 0
  • 10 月 29 独自轮滑夜刷了二环路,全程不到 3 小时,共 40 公里。 这次准备 1.6 日夜刷三环,如果有想...
    啤酒代码阅读 705评论 2 47
  • (十)我与汉中的约定 2,汉中市场的初步发展 (2013年3月——2016年12月) 速冻公司从当年的三月...
    张勇A阅读 222评论 0 9