基于Python,scrapy,redis的分布式爬虫实现框架

时间:2022-06-12 20:20:13

搬运自本人博客:http://www.xgezhang.com/python_scrapy_red://is_crawler.html


爬虫技术,无论是在学术领域,还是在工程领域,都扮演者非常重要的角色。相比于其他技术,爬虫技术虽然在实现上比较简单,没有那么多深奥的技术难点,但想要构建一套稳定、高效、自动化的爬虫框架,也并不是一件容易的事情。这里笔者打算就个人经验,介绍一种分布式爬虫框架的实现方法和工作原理,来给刚刚入门爬虫的同学们一点启发和提示。同时也希望大牛们能发表一些看法。

注:本文是对框架的宏观介绍,没有涉及具体代码,适合接触过爬虫和scrapy的读者。如果您对爬虫完全不了解,或者是刚刚入门,可以先看一些简单的爬虫教程和示例,这里贴几个链接:

人人网爬虫(针对老版本,目前可能没法用,但里面的技术是通用的):http://www.xgezhang.com/python_renren_spider_1.html

京东登陆爬虫:http://www.xgezhang.com/python_crawler_jingdong.html

scrapy中文教程:http://scrapy-chs.readthedocs.org/zh_CN/latest/intro/tutorial.html

爬虫,网页Xpath辅助分析工具:http://www.xgezhang.com/xpath_helper.html


框架的几个关键技术点:

scrapy : 实现爬虫的主体。scrapy是目前非常热门的一种爬虫框架,它把整个爬虫过程分为了多个独立的模块,并提供了多个基类可以供我们去*扩展,让爬虫编写变得简单而有逻辑性。并且scrapy自带的多线程、异常处理、以及强大的自定义Settings也让整个数据抓取过程变得高效而稳定。

scrapy-redis:一个三方的基于redis的分布式爬虫框架,配合scrapy使用,让爬虫具有了分布式爬取的功能。github地址:https://github.com/darkrho/scrapy-redis

mongodb 、mysql 或其他数据库:针对不同类型数据可以根据具体需求来选择不同的数据库存储。结构化数据可以使用mysql节省空间,非结构化、文本等数据可以采用mongodb等非关系型数据提高访问速度。具体选择可以自行百度谷歌,有很多关于sql和nosql的对比文章。

其他:一台高性能的服务器。

分布式原理:

scrapy-redis实现分布式,其实从原理上来说很简单,这里为描述方便,我们把自己的核心服务器称为master,而把用于跑爬虫程序的机器称为slave。

我们知道,采用scrapy框架抓取网页,我们需要首先给定它一些start_urls,爬虫首先访问start_urls里面的url,再根据我们的具体逻辑,对里面的元素、或者是其他的二级、三级页面进行抓取。而要实现分布式,我们只需要在这个starts_urls里面做文章就行了。

我们在master上搭建一个redis数据库(注意这个数据库只用作url的存储,不关心爬取的具体数据,不要和后面的mongodb或者mysql混淆),并对每一个需要爬取的网站类型,都开辟一个单独的列表字段。通过设置slave上scrapy-redis获取url的地址为master地址。这样的结果就是,尽管有多个slave,然而大家获取url的地方只有一个,那就是服务器master上的redis数据库。

并且,由于scrapy-redis自身的队列机制,slave获取的链接不会相互冲突。这样各个slave在完成抓取任务之后,再把获取的结果汇总到服务器上(这时的数据存储不再在是redis,而是mongodb或者 mysql等存放具体内容的数据库了)

这种方法的还有好处就是程序移植性强,只要处理好路径问题,把slave上的程序移植到另一台机器上运行,基本上就是复制粘贴的事情。

url的生成:

看到这里,有同学应该已经想到了,上文只介绍了slave如何去读取url,那么这些url是怎么出现的呢?

首先明确一点,url是在master而不是slave上生成的。

对于每一个门类的urls(每一个门类对应redis下的一个字段,表示一个url的列表),我们可以单独写一个生成url的脚本。这个脚本要做的事很简单,就是按照我们需要的格式,构造除url并添加到redis里面。

对于slave,我们知道,scrapy可以通过Settings来让爬取结束之后不自动关闭,而是不断的去询问队列里有没有新的url,如果有新的url,那么继续获取url并进行爬取。利用这一特性,我们就可以采用控制url的生成的方法,来控制slave爬虫程序的爬取。

定时抓取:

有了上面的介绍,定时抓取的实现就变得简单了,我们只需要定时的去执行url生成的脚本即可。这里推荐linux下的crontab指令,能够非常方便的制定定时任务,具体的介绍大家可以自行查看文档。

程序化管理、web管理:

上述方法虽然能够实现一套完整的流程,但在具体操作过程中还是比较麻烦,可能的话还可以架构web服务器,通过web端来实现url的添加、爬虫状态的监控等,能够减轻非常大的工作量。这些内容如果要展开实在太多,这里就只提一下。


总结:

其实对于已有的scrapy程序,对其扩展成分布式程序还是比较容易的。总的来说就是以下几步:

  • 找一台高性能服务器,用于redis队列的维护以及数据的存储。
  • 扩展scrapy程序,让其通过服务器的redis来获取start_urls,并改写pipeline里数据存储部分,把存储地址改为服务器地址。
  • 在服务器上写一些生成url的脚本,并定期执行。



介绍完整个流程,再来介绍一些常见的防抓取屏蔽的方法:

  • 设置download_delay,这个方法基本上属于万能的,理论上只要你的delay足够长,网站服务器都没办法判断你是正常浏览还是爬虫。但它带来的副作用也是显然的:大量降低爬取效率。因此这个我们可能需要进行多次测试来得到一个合适的值。有时候download_delay可以设为一个范围随机值。
  • 随机生成User-agent:更改User-agent能够防止一些403或者400的错误,基本上属于每个爬虫都会写的。这里我们可以重写scrapy 里的middleware,让程序每次请求都随机获取一个User-agent,增大隐蔽性。具体实现可以参考http://www.sharejs.com/codes/python/8310
  • 设置代理IP池:网上有很多免费或收费的代理池,可以借由他们作为中介来爬。一个问题是速度不能保证,第二个问题是,这些代理很多可能本来就没办法用。因此如果要用这个方法,比较靠谱的做法是先用程序筛选一些好用的代理,再在这些代理里面去随机、或者顺序访问。
  • 设置好header里面的domian和host,有些网站,比如雪球网会根据这两项来判断请求来源,因此也是要注意的地方。

暂时想到这么多,以后有新的发现再补充,也欢迎各位来讨论。

转载请注明出处。