一天抓一百多万张网页

  • 简要[1]
  • 优化存储硬盘[2]
  • 优化内存,URL去重[3]
  • 网络性能,抓取技术细节调优[4]
  • 反抓取访问频率限制[5]
  • 总结[6]
  • 问题答解[7]

原文


  1. 本篇只关注如何让爬虫的抓取性能最大化上,没有使用scrapy等爬虫框架,就是多线程+Python requests库搞定。
    对一个网站定向抓取几十万张页面一般只用解决访问频率限制问题就好了。对机器内存,硬盘空间,URL去重,网络性能,抓取间隙时间调优一般都不会在意。如果要设计一个单台每天抓取上百万张网页,共有一亿张页面的网站时,访问频率限制问题就不是最棘手的问题了,上述每一项都要很好解决才行。硬盘存储,内存,网络性能等问题我们一项项来拆解。 ↩

  2. 优化存储硬盘
    千万级网页的抓取是需要先设计的,先来做一个计算题。共要抓取一亿张页面,一般一张网页的大小是400KB左右,一亿张网页就是1亿X200KB=36TB。这么大的存储需求,一般的电脑和硬盘都是没法存储的。所以肯定要对网页做压缩后存储,可以用zlib压缩,也可以用压缩率更好的bz2或pylzma 。但是这样还不够,要对网页特殊处理一下,可以把网页的头和尾都去掉,只要body部分再压缩。因为一张html页面里和

    大都是公共的头尾信息和js/css代码,对你以后做正文内容抽取不会影响。 ↩

  3. 优化内存,URL去重
    1.放在set()里面,一亿个URL就要占用4G内存;
    2.https://www.tianyancha.com/company/23402373的后缀:23402373放进set()里23402373只占8个字节,一亿个URL占700多M内存;
    3.使用BloomFilter算法,非常适用海量数据的排重过滤,占用极少的内存,查询效率也非常的高,它的原理是把一个字符串映射到一个bit上,刚才23402373占8个字节,现在只占用1个bit(1字节=8bit),内存节省了近64倍,以前700M内存,现在只需要10多M了。
    安装
    pip install bloom_filter
    from bloom_filter import BloomFilter
    生成一个装1亿大小的
    bloombloom = BloomFilter(max_elements=100000000, error_rate=0.1)
    向bloom添加URL
    bloom.add('https://www.tianyancha.com/company/23402373')
    判断URL是否在
    'https://www.tianyancha.com/company/23402373' in bloombloom:
    bloombloom.contains('https://www.tianyancha.com/company/23402373') ↩

  4. 网络性能,抓取技术细节调优
    1.《爬虫小偏方:绕开登陆和访问频率控制》
    2.《 爬虫小偏方二:修改referer后可以不用登录了》。
    3.多IP抓取,多IP抓取又分IP代理池和adsl拨号两种
    adsl拨号的方式
    IP代理池相对于adsl来说,我觉得收费太贵了(一个代理IP收费4分钱,8640个IP一天就要345元)。要稳定大规模抓取肯定是要用付费的,adsl一个月也就100多块钱。
    adsl的特点
    是可以短时间内重新拨号切换IP,IP被禁止了重新拨号一下就可以了。
    有哪些可以adsl拨号的野云主机?
    百度搜"vps adsl",每次拨号的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范围较宽的厂商。
    adsl拨号Python代码
    怎么拨号厂商都会提供的,建议是用厂商提供的方式,以下为示例
    windows下用os调用rasdial拨号:
    import os
    拨号断开
    os.popen('rasdial 网络链接名称 /disconnect')
    每次断开拨号后,要等待几秒钟再拨号,太短时间内再拨号有可能又拨到上一个IP,还有可能拨号失败
    time.sleep(6)
    拨号
    os.popen('rasdial 网络链接名称 adsl账号 adsl密码')
    linux下拨号:
    import os
    拨号断开
    code = os.system('ifdown 网络链接名称')
    每次断开拨号后,要等待几秒钟再拨号,太短时间内再拨号有可能又拨到上一个IP,还有可能拨号失败
    time.sleep(6)
    拨号
    code = os.system('ifup 网络链接名称') ↩

  5. 反抓取访问频率限制
    上面步骤做完了,每天能达到抓取五万网页的样子,要达到百万级规模,还需把网络性能和抓取技术细节调优。
    1.调试开多少个线程,多长时间拨号切换IP一次最优。
    先开一个线程,一直抓取到IP被屏蔽,记录下抓取耗时,总抓取次数,和成功抓取次数,如图:

    一天抓一百多万张网页_第1张图片

    分析上述图片得出,当有6个线程时,是比较好的情况。耗时6秒,成功抓取80-110次。虽然8个线程只耗时4秒,成功抓取次数已经在下降了。so线程数可以设定为开6个,最后每总抓取120次就重新拨号。注意:尽量不要使用时间重新拨号,因为网络延迟等各类问题导致6秒内抓取次数不定。
    2.requests请求优化
    设置超时时间
    野云主机本身性能就不稳定,追求抓取效率设置短一点的时间如timeout=1.5
    3.adsl拨号等待时间
    上面步骤已算把单台机器的抓取技术问题优化到一个高度了,还剩一个优化野云主机的问题。就是每次断开拨号后,要等待几秒钟再拨号,太短时间内再拨号有可能又拨到上一个IP,还有可能拨号失败,所以要等待6秒钟(测试值)。所以要把拨号代码改一下:
    import os
    断开拨号
    os.popen('rasdial 网络名称 /disconnect')
    time.sleep(6)
    拨号
    os.popen('rasdial 网络名称 adsl账号名 adsl密码')
    4.检测是否连通(拨号完成,不能马上使用)
    用ping功能检测:
    import os
    code = os.system('ping www.baidu.com') ↩

  6. 总结
    一组抓取的耗时是6秒,拨号耗时10秒,总耗时16秒。一天86400秒,就是5400组抓取,上面说了一组抓取是120次。一天就可以抓取5400X120=64万张网页。
    如果你把adsl拨号耗时再优化一点,每次再节约2-3秒,就趋近于百万抓取量级了。
    另外野云主机一个月才100多,很便宜,所以你可以再开一台adsl拨号主机,用两台一起抓取,一天就能抓一百多万张网页。几天时间就能镜像一个过千万网页的网站。 ↩

  7. 问题答解
    1.为什么不用异步抓取?
    没必要,这里的整个抓取关键是网络性能,而不是程序性能。用异步把程序性能提高了,单位时间的抓取次数是提高了,但是这样反而会击中对方网站的访问频率控制策略。
    2.要计算对方的带宽压力,不要抓取太过分了
    抓取归抓取,但不要影响对方网站,把对方网站带宽都打满了。
    一个中小型网站的带宽在5M以内,大一点的网站带宽可能10-30M,超大型的另算。
    一张网页300KB,对方一般会压缩后传输给浏览器,就按压缩后30KB算,你的爬虫一秒请求20次,带宽就是600KB。可能一个网站每天都有几十个爬虫都在爬,我们按有10个爬虫在同时抓取,就是这些爬虫一秒内就要消耗600KBX10=6M带宽。
    再加上还有正规爬虫,人家网站上的正常用户访问这些,算下来可能一共要消耗10M带宽。一般的大中型网站都是吃不消的。 ↩

你可能感兴趣的:(一天抓一百多万张网页)