Scrapy 1.5 文档

这篇文档包含了所有你需要了解的关于Scrapy的知识。

获得帮助

遇到麻烦? 我们能提供帮助!

第一步

初窥Scrapy

Scrapy是一种用于抓取网站和提取结构化数据的应用程序框架,可用于广泛的有用应用程序,如数据挖掘,信息处理或历史存档。

尽管Scrapy最初是为web scraping设计的,但它也可以用于使用API​​(如Amazon Associates Web Services)提取数据或用作通用目的的网页抓取工具。

Spider示例演示

为了向您展示Scrapy带来的东西,我们将通过一个Scrapy爬虫示例来向您演示运行Spider的最简单方式。

这里是一个爬虫的代码,它可以在分页之后从网站http://quotes.toscrape.com中抓取名人名言。

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/tag/humor/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.xpath('span/small/text()').extract_first(),
            }

        next_page = response.css('li.next a::attr("href")').extract_first()
        if next_page is not None:
            yield response.follow(next_page, self.parse)

把它放在一个文本文件中,命名为quotes_spider.py,然后使用runspider命令运行Spider:

scrapy runspider quotes_spider.py -o quotes.json

完成后,您将得到包含了文本和作者的JSON格式引号列表的quotes.json文件,看起来像这样(为了更好的可读性在这里重新格式化):

[{
    "author": "Jane Austen",
    "text": "\u201cThe person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.\u201d"
},
{
    "author": "Groucho Marx",
    "text": "\u201cOutside of a dog, a book is man's best friend. Inside of a dog it's too dark to read.\u201d"
},
{
    "author": "Steve Martin",
    "text": "\u201cA day without sunshine is like, you know, night.\u201d"
},
...]
刚刚发生了什么?

当您运行scrapy runspider quotes_spider.py命令时,Scrapy会从内部查找Spider的定义并通过它的爬虫引擎运行Spider。

爬虫通过向start_urls属性中定义的URL发送请求(本例中,仅在humor类别中引用的URL),然后调用默认回调方法 parse,将响应对象作为参数传递。 parse回调中,我们CSS选择器循环引用元素,产生一个被提取引用的文本和作者的Python字典,查找下一页的链接并对另一个请求使用同样的parse方法作为回调。

在这里您会注意到Scrapy的一个主要优势:请求被scheduled and processed asynchronously 这意味着Scrapy不需要等待请求处理完成,它可以同时发送另一个请求或做其他事情。 这也意味着即使某些请求失败或处理时发生错误也可以继续执行其他请求。

这不仅使您能够快速执行爬取(在容错方式下同时发送多个并发请求),Scrapy还可以使你通过a few settings来优雅的控制爬虫。 您可以执行诸如在每个请求之间设置下载延迟,限制每个域或每个IP的并发请求数量,甚至using an auto-throttling extension尝试自动调节这些延迟。

Note

这里使用了feed exports生成JSON文件,您可以轻易更改导出格式(例如XML或CSV)或存储后端(例如FTP或Amazon S3)。 您还可以编写item pipeline将项目存储在数据库中。

还有什么?

您已经看过如何使用Scrapy从网站中提取和存储信息,但这只是表面。 Scrapy提供了许多强大的功能,可以使抓取变得简单高效,例如:

  • 内置支持使用扩展CSS选择器和XPath表达式从HTML/XML源selecting and extracting数据,使用正则表达式作为辅助方法进行提取。
  • 一个交互式shell控制台(IPython软件)用于尝试使用CSS和XPath表达式来抓取数据,这在编写或调试蜘蛛程序时非常有用。
  • 内置支持导出多种格式(JSON,CSV,XML),也可将它们存储在多种后端(FTP,S3,本地文件系统)。
  • 强大的编码支持和自动检测,用于处理外部的,非标准的和破损的编码声明。
  • 强大的可扩展性支持,允许您使用signals和定义良好的API(middlewares,extensionspipelines)。
  • 广泛的内置扩展和中间件处理:
    • cookies和会话处理
    • HTTP特性,如压缩,认证,缓存
    • 用户代理欺骗
    • robots.txt
    • 爬行深度限制
    • 更多
  • 一个Telnet控制台,用于连接Scrapy进程中运行的Python控制台,以反编译和调试您的爬虫程序
  • 还有其他有用的东西,如可重复使用的用于抓取站点地图和XML/CSV中网站的spider,与抓取的信息关联的自动下载图片(或任何其他媒体)的媒体管道,缓存DNS解析器,等等!

接下来是什么?

下一步是安装Scrapy 跟随教程学习如何创建一个全面的Scrapy项目和加入社区 感谢您的关注!

安装指南

安装Scrapy

Scrapy在Python 2.7和Python 3.4以上运行,在CPython(默认Python实现)和PyPy(从PyPy 5.9开始)下运行。

如果您使用AnacondaMiniconda,可以从conda-forge渠道安装软件包,该软件包具有最新的软件包适用于Linux,Windows和OS X.

要使用conda安装Scrapy,请运行:

conda install -c conda-forge scrapy

或者,如果您已经熟悉Python包的安装,则可以使用PyPI安装Scrapy及其依赖项:

pip install Scrapy

请注意,有时这可能需要根据您的操作系统解决某些Scrapy依赖项的编译问题,因此请务必检查平台特定安装说明

我们强烈建议您在专用的virtualenv中安装Scrapy,以避免与系统软件包冲突。

有关更详细的平台特定说明,请继续阅读。

有用的知识

Scrapy是用纯Python编写的,并且依赖于几个关键的Python包:

  • lxml,一种高效的XML和HTML解析器
  • parsel,一个基于lxml的HTML/XML数据提取库
  • w3lib,一款用于处理网址和网页编码的多用途帮手
  • twisted,一个异步网络框架
  • cryptographypyOpenSSL,用来处理各种网络级安全需求

已测试过Scrapy的最低版本是:

  • Twisted 14.0
  • lxml 3.4
  • pyOpenSSL 0.14

Scrapy可能会使用这些软件包的旧版本,但不能保证它会继续工作,因为它没有经过测试。

其中一些软件包本身依赖于非Python包,这可能需要额外的安装步骤,具体取决于您的平台。 请查阅平台特性指南

如果出现依赖关系相关的任何问题,请参阅其各自的安装说明:

平台特定的安装说明

Windows

尽管可以使用pip在Windows上安装Scrapy,但我们建议您安装AnacondaMiniconda,并使用conda-forge渠道中的软件包,这将避免大多数安装问题。

当你安装AnacondaMiniconda完成之后,安装Scrapy:

conda install -c conda-forge scrapy
Ubuntu 14.04或更高版本

Scrapy is currently tested with recent-enough versions of lxml, twisted and pyOpenSSL, and is compatible with recent Ubuntu distributions. But it should support older versions of Ubuntu too, like Ubuntu 14.04, albeit with potential issues with TLS connections.

Don’t use the python-scrapy package provided by Ubuntu, they are typically too old and slow to catch up with latest Scrapy.

To install scrapy on Ubuntu (or Ubuntu-based) systems, you need to install these dependencies:

sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
  • python-dev, zlib1g-dev, libxml2-dev and libxslt1-dev are required for lxml
  • libssl-dev and libffi-dev are required for cryptography

If you want to install scrapy on Python 3, you’ll also need Python 3 development headers:

sudo apt-get install python3 python3-dev

Inside a virtualenv, you can install Scrapy with pip after that:

pip install scrapy

Note

The same non-Python dependencies can be used to install Scrapy in Debian Jessie (8.0) and above.

Mac OS X

构建Scrapy的依赖需要存在C编译器和开发头文件。 在OS X上,这通常由Apple的Xcode开发工具提供。 要安装Xcode命令行工具,请打开一个终端窗口并运行:

xcode-select --install

已知问题可能会阻止pip更新系统包。 必须解决这个问题才能成功安装Scrapy及其依赖项。 以下是一些建议的解决方案

  • (推荐) 不要使用系统python,安装一个不会与系统其余部分冲突的新的更新版本。 下面介绍了如何使用 homebrew 软件包管理器:

    • 按照https://brew.sh/中的说明安装homebrew

    • 更新你的PATH变量​​,应该在系统包之前声明homebrew包(如果你使用 zsh作为默认shell,将.bashrc 改为 .zshrc):

      echo "export PATH=/usr/local/bin:/usr/local/sbin:$PATH" >> ~/.bashrc
      
    • 重新加载.bashrc以确保更改发生:

      source ~/.bashrc
      
    • 安装python:

      brew install python
      
    • Python的最新版本已经捆绑了pip,所以你不需要单独安装它。 如果情况并非如此,请升级python:

      brew update; brew upgrade python
      
  • (可选)在隔离的python环境中安装Scrapy。

    此方法是上述OS X问题的一种解决方法,也是管理依赖关系的良好实践,可以作为第一种方法的补充。

    virtualenv是一个可用于在python中创建虚拟环境的工具。 我们建议阅读http://docs.python-guide.org/en/latest/dev/virtualenvs/这个教程入门。

在以上任一方法完成后,您应该能够安装Scrapy:

pip install Scrapy
PyPy

我们建议使用最新的PyPy版本。 测试版本是5.9.0。 对于PyPy3,仅测试了Linux安装。

大多数scrapy依赖现在都有用于CPython的二进制转义,但不适用于PyPy。 这意味着这些依赖将在安装过程中建立。 在OS X上,您可能会遇到构建密码依赖性的问题,此问题的解决方案在这里描述,即brew install openssl,然后导出此命令推荐的标志(仅在安装scrapy时需要)。 除了安装构建依赖关系之外,在Linux上安装没有特殊问题。 在Windows上使用PyPy安装scrapy未经测试。

You can check that scrapy is installed correctly by running scrapy bench. If this command gives errors such as TypeError: ... got 2 unexpected keyword arguments, this means that setuptools was unable to pick up one PyPy-specific dependency. To fix this issue, run pip install 'PyPyDispatcher>=2.1.0'.

Scrapy教程

在本教程中,我们假定您的系统上已经安装了Scrapy。 如果不是这种情况,请参阅安装指南

我们将爬取quota.toscrape.com,一个列出著名作家语录的网站。

本教程将引导您完成这些任务:

  1. 创建一个新的Scrapy项目
  2. 编写一个spider来抓取网站并提取数据
  3. 使用命令行导出爬取的数据
  4. 更改spider递归地跟随链接
  5. 使用spider参数

Scrapy是用Python编写的。 如果您对语言很陌生,您可能想先了解语言是什么样子,以充分利用Scrapy。

如果您已经熟悉其他语言,希望快速学习Python,我们建议您阅读Dive Into Python 3 或者,您可以按照Python教程进行操作。

如果您是编程新手,想从Python开始,那么在线书籍Learn Python The Hard Way将对您非常有用。 你也可以看看非程序员的Python资源列表

创建一个项目

在开始抓取之前,您不得不建立一个新的Scrapy项目。 进入您想要存储代码并运行的目录:

scrapy startproject tutorial

这将创建一个包含以下内容的tutorial目录:

tutorial/
    scrapy.cfg            # 部署配置文件

    tutorial/             # 项目的Python模块,你将在这里输入你的代码
        __init__.py

        items.py          # 项目的items定义文件

        middlewares.py    # 项目中间件文件

        pipelines.py      # 项目管道文件

        settings.py       # 项目设置文件

        spiders/          # 稍后放置spider的文件夹
            __init__.py

我们的第一个Spider

Spider是你定义的类,并且Scrapy用它从网站(或一组网站)爬取信息。 它们继承自scrapy.Spider,定义初始请求,可选择如何跟随页面中的链接,以及如何解析下载的页面内容以提取数据。

这是我们第一个Spider的代码。 将它保存在项目中tutorial/spiders目录下名为quotes_spider.py的文件中:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"

    def start_requests(self):
        urls = [
            'http://quotes.toscrape.com/page/1/',
            'http://quotes.toscrape.com/page/2/',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'quotes-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)

正如你所看到的,我们的Spider继承自scrapy.Spider,定义了一些属性和方法:

  • name:标识Spider。 它在项目中必须是唯一的,也就是说,不能为不同的Spider设置相同的名称。

  • start_requests():必须提供一个Spider开始抓取的迭代请求(你可以返回一个请求列表或者编写一个生成器函数)。 随后的请求将从这些初始请求中接连生成。

  • parse():一个用来处理每个请求下载的响应的方法。 response参数是TextResponse的一个实例,它包含了页面内容以便进一步处理。

    parse()方法通常会解析response,将抓到的数据提取为字典,同时找出接下来新的URL创建新的请求(Request)。

如何运行我们的spider

为了让我们的spider工作,请转到项目的顶层目录并运行:

scrapy crawl quotes

这个命令运行我们刚刚添加名为quotes的spider,它将发送一些针对quotes.toscrape.com域的请求。 你会得到类似于这样的输出:

... (omitted for brevity)
2016-12-16 21:24:05 [scrapy.core.engine] INFO: Spider opened
2016-12-16 21:24:05 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2016-12-16 21:24:05 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-12-16 21:24:05 [scrapy.core.engine] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None)
2016-12-16 21:24:05 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
2016-12-16 21:24:05 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/2/> (referer: None)
2016-12-16 21:24:05 [quotes] DEBUG: Saved file quotes-1.html
2016-12-16 21:24:05 [quotes] DEBUG: Saved file quotes-2.html
2016-12-16 21:24:05 [scrapy.core.engine] INFO: Closing spider (finished)
...

现在,检查当前目录中的文件。 您应该注意到创建了两个新文件:quotes-1.htmlquotes-2.html,其中包含各个网址的内容,就像parse方法指示的那样。

Note

如果您想知道为什么我们还没有解析HTML,请坚持下去,我们很快就会涉及。

发生了什么?

Scrapy调度Spider的start_requests方法返回的scrapy.Request对象。 在收到每个响应后,它会实例化Response对象并调用与请求相关的回调方法(本例中为parse方法)将响应作为参数传递。

start_requests方法的快捷方式

除了实现从网址生成scrapy.Request对象的start_requests()方法外,您还可以定义一个包含网址列表的start_urls类属性。 这个列表将被默认的start_requests()用来为你的spider创建初始请求:

import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
        'http://quotes.toscrape.com/page/2/',
    ]

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'quotes-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)

parse()方法将会被调用来处理这些URL的每个请求,即使我们没有明确告诉Scrapy这样做。 发生这种情况是因为在没有为request明确分配回调方法时,parse()是Scrapy的默认回调方法。

提取数据

学习如何使用Scrapy提取数据的最佳方式是尝试使用shell选择器Scrapy shell 运行:

scrapy shell 'http://quotes.toscrape.com/page/1/'

注意

请记住,从命令行运行Scrapy shell时应该将url用引号括起来,否则包含参数的url(例如 & 字符) 将出现问题。

在Windows上,请使用双引号:

scrapy shell "http://quotes.toscrape.com/page/1/"

你会看到类似于:

[ ... Scrapy log here ... ]
2016-09-19 12:09:27 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x7fa91d888c90>
[s]   item       {}
[s]   request    <GET http://quotes.toscrape