scrapy 概述

用了一段时间的scrapy了,比直接Requests、Urllib确实是好用不少,框架仍是不错的,偶然看到这篇帖子,确实是深有体会,copy下做为记录php

在编程语言的世界里,python彷佛被贴上了作爬虫的一个标签,强而有力。而scrapy作为另外一个老牌的开源项目,更是大规模抓取不可或缺的一个重要力量。纵使scrapy依旧有一些长期没法解决的诟病,可是他在抓取过程帮程序员解决的一系列的细节问题,仍是有无以伦比的优点。html

缺点

1. 重量级

scrapy依赖于twisted,而twisted是python世界的出名的重量级框架。要读懂scrapy的源码,必须先了解twisted(deffered,reactor)的工做原理,读懂twisted的源码,这样一切看起都很是困难。python

2. 内存

为了防止重复对同一个url抓取,避免无限递归的遍历url,scrapy把全部处理过的request(包括method,url,headers)放到内存当中,若是spider处理的request够多的话,spider占用的内存是惊人的。react

解决方法程序员

  1. 必要的时,将一个大型spider(可预计抓取的url过多)拆分红多个spider。ajax

  2. bloom,以空间换取准确性的一种去重算法。settings.py里配置DUPEFILTER_CLASS = ‘scrapy.dupefilter.RFPDupeFilter’,用本身实现的bloom算法覆盖便可。要注意的是,bloom是牺牲了准确性开换取空间开支小的算法,在内存可以胜任,就显得画蛇添足。redis

3. xpath的容错(并非scrapy独有)

常常会遇到解析一些非标准的html,在浏览器会对他们进行容错处理,而不会影响到页面展示。可是用xpath就要了命了,并且一般还不容易察觉。解决办法算法

  1. 将整个html转化成标准的xml,在用xpath解析。好比数据库

    xx
    ,这个时候就要想办法把第二个去掉。

  2. 用re解析抽取结构化信息。不推荐用re,不是由于在这种场景下效果很差,而是由于用正则会增长spider的维护成本,具体的下面会提到。编程


优势

理论只是实践基础和指导原则,要知道理论到实践仍是有不少路要走的。本人比较反对将重复造轮子的东西轻易的拿到生产上去实践。spider不只仅是把内容结构化,而是在各个细节处理上都很全面。

1. 智能编码

scrapy会从meta中自动提取里涉及到的编码,若是没有则尝试gb2312,utf-8编码,最后还不行的话,就用自定义的编码DEFAULT_RESPONSE_ENCODING。虽然这种方式不能完成正确,也能保证90%(我实践中获得的结果)以上的正确性。

2. 灵活的pipeline

pipeline以管道的方式处理item,好比说item再加工,过滤,持久化均可以再这里处理。能够定义多个pipeline,对item作不一样的处理。若是在item中有个image字段,能够先用MediaPipeline处理过,将图片下载到本地,再讲item插入到数据库。

3. 强大的xpath

scrapy是xpath做为解析工具,以前提到的也能够用正则可是不推荐,一个很重要的缘由是,xpath维护,可读性要比正则强太多。spider的维护一项持久而耗时的工做,特别是一些静态网站,都是经过cms系统发布,这样会致使网站的结构,样式调整的比较频繁,用re会陷入无尽的烦恼当中。而xpath当然也会有种问题,可是良好的可读性,会让维护成本成倍的下降。

4. 处理http请求的各类细节

  1. 设置抓取时间间隔,在spider中设置download_delay=x(单位是秒)

  2. 配置代理,settings中增长HttpProxyMiddleware(默认),设置系统代理

  3. 配置自定义代理

1 import base64 
2 
3 class ProxyMiddleware(object):
4     def process_request(self, request, spider):
5         request.meta['proxy'] = "http://YOUR_PROXY_IP:PORT"
6         proxy_user_pass = "USERNAME:PASSWORD"
7         encoded_user_pass = base64.encodestring(proxy_user_pass)
8         request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_pass

也能够将代理配置成随机的,只须要在上面代码中稍做处理。

  1. http code的处理,正常状况下,scrapy的response只处理20x,设置handle_httpstatus_list = [301,302,204,206,404,500]

  2. retry机制,因为网络或者对方服务器的缘由,对url重复处理是很是有必有,spider中设置RETRY_TIMES,RETRY_HTTP_CODES

  3. 模拟浏览器行为,设置user-agent

  4. 设置默认headers, DEFAULT_REQUEST_HEADERS

  5. cookies处理,开启COOKIES_DEBUG=True后,能够再Request中带上cookies

yield Request(url=’http://www.douban.com‘,cookies={‘session’:’xxxx’})
  等等吧,太多了。

5. 对https,ftp等协议支持,使用过程和http同样。

6. 强大的监控,日志系统

默认开启TELNETCONSOLE_ENABLED = 1,WEBCONSOLE_ENABLED = 1具体的能够看文档。通常的状况下,会查看spider close的日志,整个spider的运行状态都查看到

**重点内容**7. 支持json,jsonlines,csv,xml,marshal,pickle导出

下面接着写:

体会

断断续续的使用scrapy已经很长时间,在各类问题也算是有所领悟(所需工具firefox,firebug,firefox的xpath插件)。

1. 非标准的html解析问题(上一篇提到过)

这个问题蛋疼不是问题有多难解决,而是很难定位到错误的缘由。在firefox中确保xpath没有写错的状况下,找不到相应的数据,这个时候就要考虑是不是这个缘由,直接查看页面源码,若是有不标准的html,在firefox中会有红色标识。

selector = XPathSelector(text=response.body.replace('<div class="left-clear"/>','<div class="left-clear">'))

2. 反扒

一般的状况下,一些流量比较大的网站都有反爬虫的机制,避免恶意的访问,减轻服务器的压力。通常的状况的下调整抓取的间隔,更换代理。2种方式都有缺点。

抓取间隔设置download_delay=1,一般设置这个属性,整个spider的性能变得很是低。

更换代理,能够到http://pachong.org/去找免费的代理,通常速度也不快,并且不稳定。

3. http code欺骗(我本身取得名字)

通常状况数据的正常返回都是20x,scrapy会自动忽略掉50x的http code。这个(http://www.travelplus.cn/plus/list.php?tid=8

)用浏览器打开的时候,你看不出任何问题。用firebug就能清晰的看到,其实它是将500页面,当作正常页面显示。

  在spider里添加handle_httpsatstus_code = [500],一切照旧便可。

4. 模拟浏览器请模拟完全。

若是你有服务器点开发的经验,就不难理解,服务器能够经过任何一个headers或者参数来屏蔽你的request。参数天然不用说,能带上都带上,至于一些参数加密,之后再提。主要是针对headers中的几个经常使用的。

  Cookie,这个最多见用的,平心而论scrapy对cookie的支持只是基础的支持,用起来不太好用。

  User-Agent, 这个主要仍是服务器为了区分request是来自pc端仍是手机端,会致使response不同。

  Referer, 防止伪造的跨网站请求。

  X-Requested-With=XMLHttpRequest, 这个很好理解,对于同一个url,ajax request和普通request的response结果不同很正常

  Content-Length, 一些网站没有这个header会返回411

  以上5个header基本上能解决问题,万一遇到顽固份子,那只好完全的模仿吧。

5. 不要忽略firebug里监控到任何http request

部分网站在提交数据的时候,会先跳转到一个页面,这个页面是空白的页,只是包含一些隐藏的表单,最后用js带上表单跳转到其余的页面。所以在firebug的监控的request中,会莫名其妙的多出来一些参数。

6. xpath表达式

能简单尽可能简单,尽可能class和id来表达。千万别将xpath的表达式依赖不少属性,这样难以维护不说,并且极不稳定。

7. xpath表达式

xpath表达式能在浏览器中找到元素,在spider里确不能,有可能性js动态加载,还有table元素,有的会自动添加th等。这个时候请直接对着html源码写你的表达式。

8. ajax request和json,这应该是如今网站的主流。

要关注的真正获得数据的request,一般咱们要抓取的数据也都在ajax request中,拿到数据后用json.loads(response.body)

9. 要抓取的内容分布在不一样的request中,这个时候request meta就能很好的胜任。

def parse_city_item(self, response):
        x = HtmlXPathSelector(response)
        item = Item()
        //给item赋值
        item['title'] = ''.join(x.select('//div[@class="title"]/text()').extract())
        data = {}
        req = FormRequest(url=url,formdata=data,callback=self.parse_comment)
        req.meta['item'] = item
        //带上返回req
        return req
def parse_comment(self, response):
        item = response.request.meta['item']
        x = HtmlXPathSelector(response)
        item['content'] = ''.join(x.select('//div[@class="content"]/text()').extract())
        return item

10. 增量抓取

不少状况下须要抓取网站更新的内容。咱们知道在一次抓取的过程是能避免重复抓取,scrapy默认提供文件存储的方式,只须要再settings里设置JOBDIR=”path”。我在使用scrapy仍是0.9,没有这个特性,使用redis做为url存储。我的感受对于大规模抓取用redis仍是比文件的方式要好不少。redis里能够设置key的过时时间,确定会有人说,这样能保证不重复的抓取吗,固然不能绝对,可是能够调整抓取深度,对于抓取较为频繁网站,抓取到相同的几率就很低。好比说抓取sina的体育新闻,将url作md5加密存储到redis里,过时时间设置为1天,抓取体育新闻滚动页面前3页 点击连接,15分钟抓取一次。而单纯用文件方式存储的话,文件只大不小,多了天然影响性能。

11. 编码

上一篇也提到过。scrapy为咱们作了编码,可是若是这样好错了,就须要特殊处理了。

参考自:http://www.cnblogs.com/twelfthing/articles/4620533.html
http://www.cnblogs.com/twelfthing/articles/4620761.html

相关文章
相关标签/搜索