Scrapy 探索之路


1 scrapy 是啥?

  • scray是python的一个网络爬虫框架,爬虫是啥?请百度。爬虫可以干嘛?请百度。爬虫可以做哪些有趣的事, 可以上知乎看关于网络爬虫的帖子。可以上srapy的官网的描述。
  • (版本:scrapy 0.24.6)

2 怎么学习

 
2.1 看手册

scrapy官网有自带的入门手册,有pdf和online两种格式。

2.2 安装

手册中有,自己折腾。

2.3 入门
  • 手册
    对于入门来说,手册中已经给了详细的例子,至于网上的大部分文章,基本都是从手册中摘出来的,然后翻译一下。 我前前后后也看过好几遍手册了。80%以上的问题都可以在手册中得到答案。所以,建议通读手册。
  • xpath,lxml和beautifulsoup,
    可以参考w3c上的内容。然后试着自己抓取几个网页提取一下。下面是我 当时学习lxml时写的一段代码,方便一开始熟悉xpath。
    from lxml import etree
    from StringIO import StringIO
    
    xmlfile="""<?xml version="1.0" encoding="ISO-8859-1"?>
    <bookstore>
    
    <book category="COOKING">
      <title lang="en">Everyday Italian</title>
      <author>Giada De Laurentiis</author>
      <year>2005</year>
      <price>30.00</price>
    </book>
    
    <book category="CHILDREN">
      <title lang="en">Harry Potter</title>
      <author>J K. Rowling</author>
      <year>2005</year>
      <price>29.99</price>
    </book>
    
    <book category="WEB">
      <title lang="en">XQuery Kick Start</title>
      <author>James McGovern</author>
      <author>Per Bothner</author>
      <author>Kurt Cagle</author>
      <author>James Linn</author>
      <author>Vaidyanathan Nagarajan</author>
      <year>2003</year>
      <price>49.99</price>
    </book>
    
    <book category="WEB">
      <title lang="ch">Learning XML</title>
      <author>Erik T. Ray</author>
      <year>2003</year>
      <price>39.95</price>
    </book>
    
    </bookstore>
    """
    
    f=StringIO(xmlfile)
    tree=etree.parse(f)
    
    tree.xpath('//title')[0].tag
    tree.xpath('//title')[0].text
    
    ##  closure for query
    def QResF(tree):
        def QResFunction(query):
            res=tree.xpath(query)
            try:
                reslsT=[q.tag for q in res]
                resls=[q.text for q in res]
                print zip(reslsT,resls)
            except AttributeError:
                resls='\n'.join([q for q in res])
                print resls
            return len(res)
        return QResFunction
    
    Q=QResF(tree)
    
    Q('//title')
    Q('//author')
    Q('/bookstore/book/title')
    Q('/bookstore/book[price > 25]')
    Q('//book/title[@*]')
    Q('//@*')
    Q('//*')
    Q('//@lang')
    Q('//book/title| //book/price')
    Q('//bookstore/child::year')
    Q('//nothing')
    Q('/book/child::year')
    Q('//book/child::*')
    Q('//book/title[@lang]')
    
    Q('//text()')
    
  • 学习python的一些网络库
    像urllib和urllib2。最基本的了解也是很有帮助的。比如像如何将参数添加到url中,如何对中文参数进行转换。
  • 学习twisted
    scrapy就是基于这个实现的。可以了解,我看过基本的tutorial,有一定的了解,但一般用不到。
  • 学习javascript
    至少要能读懂js文件,因为大部分网页都是用异步回传的方法get到数据的,也就是说你的第一次 网页访问往往得不到你想要的内容,这个时候就必须通过分析后台的js代码来get到数据。也有模拟浏览器的 行为进行访问的插件,但个人感觉太繁杂,太笨重,直接分析后台是最简单快捷的方式(当然,这个就得看你阅读 代码的耐心和功力了)。
  • 学http协议
    对于互联网,http协议是很重要很基础的协议,懂了协议,你玩起爬虫来会更加轻松。 这里推荐HTTP The Definitive Guide(http权威指南,e文的,看e文资料容易找,还比较直接, 不懂就开有道吧,不过你也可以试试我写的字典,在我的github上,就是用lxml+xpath实现的)。
  • 动手写自己的爬虫
    可以在网上搜索别人的爬虫项目,大部分是以抓知乎上的帖作为例子,也有抓什么美女图片的(嘻嘻)。 看他们的源代码,看高手是怎么实现的。
  • 不断的去读手册
    手册中的基本原理是解决问题的基石。
  • 研究scrapy本身的源代码
    这个是求助无门的时候必须要懂的。而且,看源代码还有助于你理解手册中的内容。我就试过一边对着源代码看手册 (还没研究透,只是看过一些),对手册的理解有很大的帮助(下面会有例子)。
  • 做一些感兴趣的小项目
    练练手,加深对概念的理解(比如说,抓抓论坛上的帖子,写写字典啥的)。 这个时候自然就会发现很多问题。而且,实现东西,还是蛮有成就感的。
2.4 一些工具

手册中有提到一些,可以参考。 很多浏览器有自带的网络监视器。 比如说google页面右键就有inspect element,打开就可以监视当前网页的情况, 可以查看网页发送的请求,可以查看xpath,可以编辑元素,研究页面的结构等等。 firefox上可以安装firebug。 google上推荐使用postman插件,可以模拟浏览器发送请求。

3 碰到的一些问题

下面是探索过程中碰到的一些问题,虽然大部分认真看手册都还是能够解决,但不是那么容易注意到。

3.1 request和response的拼接关系,

当在response返回的item需要拼接发送请求的request中的数据时,可以通过request的meta属性传送,看看手册:

class scrapy.http.Request(url [ , callback, method=’GET’, headers, body, cookies, meta,
encoding=’utf-8’, priority=0, dont_filter=False, errback ] )
...
meta
A dict that contains arbitrary metadata for this request. This dict is empty for new Requests, and is usually
populated by different Scrapy components (extensions, middlewares, etc). So the data contained in this
dict depends on the extensions you have enabled.
See Request.meta special keys for a list of special meta keys recognized by Scrapy.
This dict is shallow copied when the request is cloned using the copy() or replace() methods, and
can also be accessed, in your spider, from the response.meta attribute.

最后一句描述中,该meta会被复制到在response的meta。

3.2 如何post数据

还看request类的参数,有method一项,默认是get,那么理应就有post了,然后,数据在哪post,从http协议中 可以知道应该在body中post数据。这样,就可以构造post请求了。更方便的做法是构造FormRequest,手册中 有例子。

return [FormRequest(url="http://www.example.com/post/action",
                    formdata={’name’: ’John Doe’, ’age’: ’27’},
                    callback=self.after_post)]
3.3 request被scrapy过滤

问题是这样的,我发送一个请求,获得了初步的数据,该数据包含后续页数据的头描述和当前页的数据,可是 对页面的数据我想放到同个函数中统一处理,所以,我相当于要对当前页面的请求发送两次,但因为srapy会 自动过滤重复页,所以,重复发送被过滤掉了。解决方法同样可以在手册中找到。

class scrapy.http.Request(url [ , callback, method=’GET’, headers, body, cookies, meta,
                          encoding=’utf-8’, priority=0, dont_filter=False, errback ] )

看到有dont_filter一项,那么就可以指定该request不被scrapy过滤掉。看来设计scrapy的人考虑的是相当周全。

3.4 scrapy的item是什么?
3.2.2 Item Fields
Field objects are used to specify metadata for each field.
last_updated field illustrated in the example above.
For example, the serializer function for the
You can specify any kind of metadata for each field. There is no restriction on the values accepted by Field objects.
For this same reason, there is no reference list of all available metadata keys. Each key defined in Field objects
could be used by a different components, and only those components know about it. You can also define and use any
other Field key in your project too, for your own needs. The main goal of Field objects is to provide a way to
define all field metadata in one place. Typically, those components whose behaviour depends on each field use certain
field keys to configure that behaviour. You must refer to their documentation to see which metadata keys are used by
each component.
It’s important to note that the Field objects used to declare the item do not stay assigned as class attributes. Instead,
they can be accessed through the Item.fields attribute.
And that’s all you need to know about declaring items.

一大段的看的云里雾里的,直接看item的源代码。

class BaseItem(object_ref):
    """Base class for all scraped items."""
    pass


class Field(dict):
    """Container of field metadata"""


class ItemMeta(type):

    def __new__(mcs, class_name, bases, attrs):
        fields = {}
        new_attrs = {}
        for n, v in attrs.iteritems():
            if isinstance(v, Field):
                fields[n] = v
            else:
                new_attrs[n] = v

        cls = super(ItemMeta, mcs).__new__(mcs, class_name, bases, new_attrs)
        cls.fields = cls.fields.copy()
        cls.fields.update(fields)
        return cls


class DictItem(DictMixin, BaseItem):

    fields = {}

    def __init__(self, *args, **kwargs):
        self._values = {}
        if args or kwargs:  # avoid creating dict for most common case
            for k, v in dict(*args, **kwargs).iteritems():
                self[k] = v

    ## ...

class Item(DictItem):

    __metaclass__ = ItemMeta

一下子就豁然开朗了。

3.5 中文的显示问题

scrapy默认就是以utf格式保存的。不过,假如你是在命令行这样导出的

scrapy crawl dmoz -o items.json

你得到的将是utf8的内存表示字符串,

"\u5c97\u4f4d\u804c\u8d23\uff1a"

我试过用pipeline的形式导出,可以解决中文的问题。不过,对于嵌套的字典或列表,还是无能为力。 另一方面,直接存入到数据库却没有中文的问题。

3.6 复杂的start_ruls

这个问题是:需要根据配置文件来构造多个请求地址,而不是直接从start_urls中。 这个可以在spider中覆盖start_requests方法。手册有提到,源代码也可以看得很清楚。

class Spider(object_ref):
    """Base class for scrapy spiders. All spiders must inherit from this
    class.
    """

    name = None

    def __init__(self, name=None, **kwargs):
        if name is not None:
            self.name = name
        elif not getattr(self, 'name', None):
            raise ValueError("%s must have a name" % type(self).__name__)
        self.__dict__.update(kwargs)
        if not hasattr(self, 'start_urls'):
            self.start_urls = []
    ## ...

    def start_requests(self):
        for url in self.start_urls:
            yield self.make_requests_from_url(url)

    def make_requests_from_url(self, url):
        return Request(url, dont_filter=True)
3.7 小爬虫不想建项目

对于小的简单的爬虫,一个文件就可以搞掂。scrapy中已经提供了这样的指令的

runspider
    Syntax: scrapy runspider <spider_file.py>
    Requires project: no
Run a spider self-contained in a Python file, without having to create a project
3.8 其他

实践的过程中还碰到过很多其它方面的问题,篇幅时间限制,具体的问题以后发现解决方法再分享出来。

4 后记

  • 半年前接触了scrapy,后来便在空闲的时间探索它。自己也算写了几个简单的小项目练过手,对scrapy也算 比较清楚了(当然还是有很多待解决的问题),故写下此文,一是对这个过程进行自我总结, 二是分享自己的一些探索方法,相信跟我一样的新手也会碰到类似的问题。 其实,大部分问题的问题都可以通过scrapy自带的手册解决。 所以,建议还是读手册,一定要通读。然后是学习网络基础知识。再上网找答案。最后,建议读源码。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值