scrapy

文章目录

    对于很多简单页面,厌倦了一行一行自己写爬虫,所以尝试一下 Scrapy。重点考察

    • 开发效率
    • 异常处理
    • 防封机制

    使用教程参考官网 scrapy.org, 写得很细致。

    安装 Scrapy

    sudo pip install scrapy
    

    在 Mac OS 10.11 酋长石系统上报错

    build/temp.macosx-10.10-x86_64-2.7/_openssl.c:400:10: fatal error: ‘openssl/aes.h’ file not found

    开始以为是 openssl 没有安装,于是尝试

    $ brew install openssl
    Warning: openssl-1.0.1j already installed
    

    实际上不是,而是因为 Mac OS X 10.11 EL Capital 抛弃了 openssl 的头文件。参考 mac osx 10.11 编译 git 2.6.1 报错

    解决方法是, 执行

    xcode-select -p 
    

    打印 Xcode 的工作目录, 我的是 /Applications/Xcode.app/Contents/Developer, 进入该目录

    $ cd /Applications/Xcode.app/Contents/Developer
    $ find . -name ssl.h
    ./Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-migrator/sdk/MacOSX.sdk/usr/include/openssl/ssl.h
    

    找到 openssl 所在的目录

    cd Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-migrator/sdk/MacOSX.sdk/usr/include/
    cp -R openssl /usr/local/include/
    

    为何要拷贝到 /usr/local/include/ 呢?因为错误日志里,提到 clang 用到了 -I/usr/local/include/

    实际上,上面 scrapy 的安装并没有完成,如何运行报错的话,执行下面的操作。如果没有,则略过

    ImportError: Twisted requires zope.interface 3.6.0 or later: no module named zope.interface.

    sudo pip install zope.interface
    

    ImportError: No module named cryptography.hazmat.bindings.openssl.binding

    sudo pip install pyOpenSSL==0.13
    

    另外,Ubuntu 上也不省心 14.04 上会报错

    fatal error: libxml/xmlversion.h: No such file or directory

    解决方法

    sudo pip uninstall scrapy
    sudo apt-get install libxml2-dev libxslt1-dev python-dev
    

    如果是 VPS 上,还会遇到内存不够的问题

    command ‘x86_64-linux-gnu-gcc’ failed with exit status 4

    问题定位方法参考 dmesg | tail

    解决方法参考 设置交换分区

    Hello world! 爬取单个页面,热热身

    例如爬取 reddit quote 里的名言

    # -*- coding: utf-8 -*-
    
    import scrapy
    
    class RedditQuoteSpider(scrapy.Spider):
        name = 'reddit-quote'
        start_urls = ['https://www.reddit.com/r/quotes/']
    
        def parse(self, response):
            for text in response.css('a.title::text'):
                print text.extract()
    

    很简单,对不对。相比 BeautifulSoup + Requests 的组合,代码更简洁一些,省去了一些重复代码。
    再配上我手撸的 vim snippets 开发效率更是可怕。

    Scrapy shell - 调试神器

    Scrapy shell 是基于 iPython 的,所以提前把 iPython 装好。

    之前经常遇到的问题是,调试爬虫 selector (目标定位),修改一次就要重新运行一次,每运行一次都要重新爬取一次原网页,极其浪费时间。
    Scrapy shell 解决了这个问题,例如,我想调试 sunzhongwei.com 页面的爬取, 在 terminal 里执行

    scrapy shell "http://www.sunzhongwei.com" 
    

    会看到这样的返回

    [s] Available Scrapy objects:
    [s]   crawler    <scrapy.crawler.Crawler object at 0x104c6bb10>
    [s]   item       {}
    [s]   request    <GET http://www.sunzhongwei.com>
    [s]   response   <200 http://www.sunzhongwei.com>
    [s]   settings   <scrapy.settings.Settings object at 0x104c6ba90>
    [s]   spider     <DefaultSpider 'default' at 0x10519f950>
    [s] Useful shortcuts:
    [s]   shelp()           Shell help (print this help)
    [s]   fetch(req_or_url) Fetch request (or URL) and update local objects
    [s]   view(response)    View response in a browser
    

    显而易见 response 就是我们经常要用到的调试对象, 即爬取到的网页对象。例如,我想爬取当前网页的 title

    In [11]: print response.css("title::text")[0].extract()
    大象笔记
    

    这里介绍一下 Scrapy 内置的两种爬取网页内容的 selector, 称为选择器比较好

    • css, 类似 jQuery 里的选择器, 我还是适合用这个
    • xpath

    css, xpath 返回的都是 selector list, 要提取出 unicode list 就需要使用 extract() 或者 re(),
    对于只需要获取一条数据时,用 extract_first() 和 re_first() 比使用 extract()[0] 方便多了,至少不需要处理异常。

    小技巧,每调试一个页面都需要重新开一个 scrapy shell 么?不需要。
    实际上, 还可以这样启动 scrapy shell, 即不带 URL, 在 shell 内更新 URL

    scrapy shell
    In [4]: fetch("http://www.baidu.com") 
    

    比 Scrapy Shell 更方便的调试神器 - Chrome

    获取 XPath 的方法

    Right click on the node => “Copy XPath”

    验证 XPath 的方法

    You can use $x in the Chrome javascript console. No extensions needed.
    
    ex: $x("//img")
    
    Also the search box in the web inspector will accept xpath
    

    参考 Is there a way to get the xpath in google chrome?

    Chrome 真是个神器的工具!

    Scrapy 代码的初始化

    实际上 Hello world 那个示例,可以这样自动生成代码

    scrapy startproject reddit
    cd reddit
    scrapy genspider quote reddit.com 
    cd reddit/reddit/spiders
    vim quote.py
    

    记不住命令没问题,scrapy -h 就能看到提示。进入项目执行可以看到更多的命令。

    Scrapy 对于写入数据库的操作,如何保证不阻塞网页的爬取

    https://segmentfault.com/a/1190000002645467
    http://doc.scrapy.org/en/latest/topics/item-pipeline.html

    防止被 BAN

    设置 USER_AGENTS 之后,从 Nginx 日志中可以明显看到请求的头发生了变化。

    112.249.229.127 - - [24/Apr/2016:17:22:53 +0800] "GET / HTTP/1.1" 200 10725 "-" "Scrapy/1.0.3 (+http://scrapy.org)"
    112.249.229.127 - - [24/Apr/2016:17:25:50 +0800] "GET / HTTP/1.1" 200 10725 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"
    

    对于请求频率不高的爬虫来说,这就足够了。

    参考 如何让你的scrapy爬虫不再被ban

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式