Scrapy 1.2 ドキュメント

このドキュメントには、Scrapyについて知っておくべきすべてが含まれています。

ヘルプ

トラブルですか? 私達が助けます!

最初のステップ

Scrapy について

Scrapyは, Webサイトのクロール, データマイニング, 情報処理, アーカイブなどの幅広い有用なアプリケーションに使用できる構造化データを抽出するためのアプリケーションフレームワークです.

Scrapyはもともと Webスクレイピング 用に設計されていましたが, API( Amazon Associates Web Services のような)または汎用Webクローラーとしてデータを抽出するためにも使用できます.

スパイダーの作成例

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:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

quotes_spider.py のような名前をつけ, 上記のコードをテキストファイルに保存し, runspider コマンドで実行してください:

scrapy runspider quotes_spider.py -o quotes.json

実行完了後, quotes.json ファイルに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定義を探して, クローラ・エンジンを通して実行しました.

クロールが開始されると start_urls で定義されたURL(この場合はユーモアカテゴリの引用符のURLのみ)にリクエストを行い, デフォルトのコールバックメソッドである parse に, Response オブジェクトを引数として渡します. parse コールバックの内部では, CSSセレクタを使用して引用要素をループし, 抽出されたテキストと作成者でPythonディクテーションを生成し, 次のページへのリンクを探し, コールバックと同じ parse メソッドを使用して次のリクエストをスケジュールします.

ここで、Scrapyの主な利点を1つ: リクエストはスケジュールされ, 非同期に処理されます. つまり, Scrapyはリクエストが処理されるのを待つ必要はなく, その間に別のリクエストを送信したり, 他の処理を行うことができます. これは, リクエストが失敗した場合や, 処理中にエラーが発生した場合でも, 他のリクエストが続行できることを意味します.

これにより、非常に高速なクロールが可能になります(同時に複数の同時要求をフォールトトレラントな方法で送信できます) また, Scrapyを使用すると, いくつかの設定 でクロールの公平性を制御できます. ドメインごとまたはIPごとに並行要求の量を制限し, 自動的にこれらを把握しようとする 自動調整拡張機能を使用する など, 各要求のダウンロード遅延を設定するなどの作業を行うことができます.

注釈

これは フィードのエクスポート を使用してJSONファイルを生成し, エクスポート形式(XMLやCSVなど)やストレージバックエンド (例えば, FTP または Amazon S3) を簡単に変更できます. アイテムパイプライン 作成してアイテムをデータベースに格納することもできます.

他には?

YScrapyを使用してウェブサイトからアイテムを抽出して保存する方法を見てきましたが, これはごく表面的なものです. Scrapyは, スクレイピングを簡単かつ効率的にするための多くの強力な機能を提供します:

  • 拡張CSSセレクタとXPath式を使用してHTML / XMLソースからデータを 選択して抽出する 組み込みサポート, 正規表現を使用して抽出するヘルパーメソッド.
  • CSSやXPath式を試してデータを集める インタラクティブシェルコンソール (IPython対応). スパイダーの作成やデバッグに非常に便利です.
  • 複数の形式(JSON、CSV、XML)で フィードのエクスポートを生成 し, 複数のバックエンド(FTP, S3, ローカルファイルシステム)に格納するための組み込みサポート.
  • 外国語、非標準、壊れたエンコーディング宣言を処理するための強力なエンコーディングサポートと自動検出.
  • 強力な拡張性サポートにより,  シグナル と明確に定義されたAPI (ミドルウェア, 拡張機能, そして パイプライン) を使用して独自の機能を作成することができます.
  • さまざまな組み込み拡張機能とハンドリング用ミドルウェア:
    • cookie と session の操作
    • 圧縮, 認証, キャッシングなどのHTTP機能
    • user-agent の操作
    • robots.txt
    • クロールする深度制限
    • などなど
  • Scrapyプロセス内で動作するPythonコンソールにフックするための Telnet コンソール , クローラのイントロスペクションとデバッグ.
  • さらに,  サイトマップ やXML / CSVフィードからサイトをクロールするための再利用可能なスパイダー, スクラップしたアイテムに関連付けられた画像を(またはその他のメディア) 自動的にダウンロードするメディアパイプライン, キャッシングDNSリゾルバなど, 他にも機能がたくさんあります!

次は?

次のステップは, Scrapy をインストール し, チュートリアルに従って 本格的なScrapyプロジェクトを作成し, コミュニティに参加する 方法を学びます. あなたの興味に感謝!

インストールガイド

Scrapy のインストール

Scrapy は Python 2.7 と Python 3.3 以上で実行できます (Python 3 がまだサポートされていない Windows を除いて).

Pythonパッケージのインストールに慣れている場合は, PyPIからScrapyとその依存関係をインストールすることができます:

pip install Scrapy

専用のvirtualenv にScrapyをインストールして、システムパッケージとの衝突を避けることを強くお勧めします

詳細およびプラットフォームの詳細については, 以下を参照してください.

知りたいこと

crapyは純粋なPythonで書かれており、いくつかの主要なPythonパッケージ(他のものの中でも)に依存しています:

  • lxml, 効率的なXMLとHTMLパーサー.
  • parsel, lxmlの上に書かれたHTML / XMLデータ抽出ライブラリ.
  • w3lib, URLやWebページのエンコーディングを扱うための多目的ヘルパー.
  • twisted, 非同期ネットワーキングフレームワーク.
  • cryptographypyOpenSSL, さまざまなネットワークレベルのセキュリティニーズに対処する.

Scrapyがテストされる最小バージョンは:

  • Twisted 14.0
  • lxml 3.4
  • pyOpenSSL 0.14

Scrapyはこれらのパッケージの古いバージョンで動作するかもしれませんが, テストされていないため動作し続けることは保証されません.

これらのパッケージ自体は, プラットフォームに応じて追加のインストール手順が必要な非Pythonパッケージに依存しています. 以下の プラットフォーム固有のガイド を確認してください.

これらの依存関係に関連する問題が発生した場合は, それぞれのインストール手順を参照してください:

仮想環境を使用する(推奨)

TL;DR: すべてのプラットフォームで仮想環境内にScrapyをインストールすることをお勧めします.

Pythonパッケージは、グローバル(a.k.aシステム全体)またはユーザスペースにインストールできます。私たちは、Scrapyをシステム全体にインストールすることはお勧めしません.

代わりに、いわゆる “仮想環境” (virtualenv) 内にScrapyをインストールすることをお勧めします. Virtualenvsを使用すると, 既にインストールされているPythonシステムパッケージと競合することなく(システムツールやスクリプトの一部が壊れる可能性があります), pipsudo などはありません) でパッケージをインストールできます

仮想環境を使い始めるには, virtualenvのインストール手順 を参照してください. グローバルにインストールするには(グローバルにインストールすると実際に役立ちます), 以下を実行してください:

$ [sudo] pip install virtualenv

`ユーザーガイド`_を確認し virtualenv 環境を作成してください.

注釈

もし Linux または OS X を使用している場合, virtualenvwrapper という virtualenv をかんたんに作成できるツールがあります.

一度 virtualenv を作成すれば, 他の Python パッケージと同様に pip でインストールすることができます. (あらかじめインストールする必要のあるPython以外の依存関係は platform-specific guides を参照してください).

Python virtualenvsはデフォルトでPython 2を使用するように、またはデフォルトでPython 3を使用するように作成できます.

  • Python 3で Scrapy をインストールしたい場合は, Python 3 の virtualenv にインストールしてください.
  • また, Python 2 で Scrapy をインストールしたい場合は, Python 2 の virtualenv にインストールしてください.

プラットフォーム別インストール手順

Windows
  • https://www.python.org/downloads/ から, Python 2.7 をインストールします

    Python実行可能ファイルと追加のスクリプトへのパスを含めるには, 環境変数の PATH を調整する必要があります. PATH に Python のディレクトリパスを追加してください:

    C:\Python27\;C:\Python27\Scripts\;
    

    PATH PATHを更新するにはコマンドプロンプトを開き, 以下を実行します:

    c:\python27\python.exe c:\python27\tools\scripts\win_add2path.py
    

    コマンドプロンプトウィンドウを閉じてから再度開いて変更を有効にし, 次のコマンドを実行して Python のバージョンを確認します:

    python --version
    
  • pywin32http://sourceforge.net/projects/pywin32/ からインストールしてください.

    環境に合ったアーキテクチャ(win32またはamd64)をダウンロードしてください.

  • (バージョン 2.7.9 以下の Python が必要な限り) piphttps://pip.pypa.io/en/latest/installing/ からインストールしてください.

    pip が正しくインストールされていることを確認するために, コマンドプロンプトを開き, 以下を実行します:

    pip --version
    
  • この時点でPython 2.7と pip パッケージマネージャが動作しているはずです. Scrapyをインストールしましょう:

    pip install Scrapy
    

注釈

Python 3はWindowsではサポートされていません. これは、Scrapyのコア要件である Twisted が Windows 上での Python 3 をサポートしていないためです.

Ubuntu 12.04 以上

Scrapyは現在, lxml, twisted, pyOpenSSLの最近の十分なバージョンでテストされており, 最近のUbuntuディストリビューションと互換性があります. しかし, Ubuntuの以前のバージョンもサポートしてはいますが, Ubuntu 12.04 のように, TLS接続の潜在的な問題があります.

注釈

Ubuntuで提供されている python-scrapy パッケージは使用しないでください. 更新が遅く, 最新の Scrapy に追いつくのが遅くなります.

Ubuntu(またはUbuntuベース)システムにscrapyをインストールするには, これらの依存関係をインストールする必要があります:

sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
  • python-dev, zlib1g-dev, libxml2-devlibxslt1-devlxml に必要です.
  • libssl-dev と libffi-dev は cryptography に必要です.

Python 3 に Scrapy をインストールする場合は, Python 3 開発ヘッダーも必要です:

sudo apt-get install python3 python3-dev

これらをインストールした後に, virtualenv の中で, pip で Scrapy をインストールすることができます:

pip install scrapy

注釈

同じ non-python 依存関係を使って Debian Wheezy(7.0)以上で Scrapy をインストールすることができます.

Mac OS X

Scrapy の依存関係をビルドするのには、Cコンパイラと開発ヘッダーが必要です. OS X では, これらは通常, Apple の Xcode 開発ツールによって提供されます. Xcode コマンドラインツールをインストールするには, ターミナルウィンドウを開き, 以下を実行します:

xcode-select --install

pip がシステムパッケージを更新しない 既知の問題 があります. Scrapy とその依存関係を正常にインストールするために, この問題に対処する必要があります. これに対するいくつかの解決策があります:

  • (推奨) システムの Python を 使用しないでください . システムの残りの部分と競合しない新しいバージョンをインストールしてください.  homebrew のパッケージマネージャを使ってインストールを行う方法は次のとおりです:

    • http://brew.sh/ の指示に従って, homebrew をインストールします.

    • PATH 変数を更新して, システムパッケージを使用する前に homebrew パッケージを使用するようにしてください (デフォルトのシェルとして zsh を使用している場合は .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
Anaconda

Anacondaを使用することは、virtualenvを使用して pip でインストールする代わりの方法です.

注釈

Windowsユーザーの場合、または pip でインストールする際に問題が発生した場合は, この方法で Scrapy をインストールすることをお勧めします.

もし, Anaconda または Miniconda がすでにインストールされている場合, conda-forge コミュニティには Linux, Windows そして OS X のための最新パッケージが有ります.

conda を用いてインストールするには, 以下を実行してください:

conda install -c conda-forge scrapy

Scrapy チュートリアル

このチュートリアルでは, Scrapyが既にシステムにインストールされていると仮定します. もしまだインストールしていない場合は,  インストールガイド を参照してください.

ここでは, 有名な著者からの引用を掲載しているウェブサイト quotes.toscrape.com からデータを集めてきます.

このチュートリアルでは, これらのタスクについて説明します:

  1. 新しい Scrapy を作成する.
  2. サイトをクロールし, データを集めるための スパイダー を作成する.
  3. コマンドラインを使用してスクラップしたデータをエクスポートする.
  4. 再帰的にリンクをたどるためにスパイダーを更新する.
  5. スパイダーの引数を使用する

Scrapy は Python で書かれています. 言語に慣れていない場合は, 言語がどのようなものかを知ることで, Scrapyを最大限に活用することができるかもしれません.

でに他の言語に精通していて, Pythonを素早く学びたい場合は Dive Into Python 3 を読むことをお勧めします. あるいは, Python Tutorial を読むのもおすすめです.

あなたがプログラミングに慣れていなくてもPythonを使いたいのであれば, オンラインの本 Learn Python The Hard Way が役立ちます. また,  非プログラマーのためのこのPythonリソースのリスト も役立ちます.

プロジェクトの作成

スクレイピングを開始する前に, 新しいScrapyプロジェクトをセットアップする必要があります. コードを保存して実行するディレクトリ下に移動し, 以下を実行します:

scrapy startproject tutorial

これにより、次の内容の tutorial ディレクトリが作成されます:

tutorial/
    scrapy.cfg            # deploy configuration file

    tutorial/             # project's Python module, you'll import your code from here
        __init__.py

        items.py          # project items definition file

        pipelines.py      # project pipelines file

        settings.py       # project settings file

        spiders/          # a directory where you'll later put your spiders
            __init__.py

最初のスパイダー

Spiders are classes that you define and that Scrapy uses to scrape information from a website (or a group of websites). They must subclass scrapy.Spider and define the initial requests to make, optionally how to follow links in the pages, and how to parse the downloaded page content to extract data.

This is the code for our first Spider. Save it in a file named quotes_spider.py under the tutorial/spiders directory in your project:

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)

As you can see, our Spider subclasses scrapy.Spider and defines some attributes and methods:

  • name: identifies the Spider. It must be unique within a project, that is, you can’t set the same name for different Spiders.

  • start_requests(): must return an iterable of Requests (you can return a list of requests or write a generator function) which the Spider will begin to crawl from. Subsequent requests will be generated successively from these initial requests.

  • parse(): a method that will be called to handle the response downloaded for each of the requests made. The response parameter is an instance of TextResponse that holds the page content and has further helpful methods to handle it.

    The parse() method usually parses the response, extracting the scraped data as dicts and also finding new URLs to follow and creating new requests (Request) from them.

スパイダーの実行方法

To put our spider to work, go to the project’s top level directory and run:

scrapy crawl quotes

This command runs the spider with name quotes that we’ve just added, that will send some requests for the quotes.toscrape.com domain. You will get an output similar to this:

... (omitted for brevity)
2016-09-20 14:48:00 [scrapy] INFO: Spider opened
2016-09-20 14:48:00 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2016-09-20 14:48:00 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-09-20 14:48:00 [scrapy] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None)
2016-09-20 14:48:00 [scrapy] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
2016-09-20 14:48:01 [quotes] DEBUG: Saved file quotes-1.html
2016-09-20 14:48:01 [scrapy] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/2/> (referer: None)
2016-09-20 14:48:01 [quotes] DEBUG: Saved file quotes-2.html
2016-09-20 14:48:01 [scrapy] INFO: Closing spider (finished)
...

Now, check the files in the current directory. You should notice that two new files have been created: quotes-1.html and quotes-2.html, with the content for the respective URLs, as our parse method instructs.

注釈

If you are wondering why we haven’t parsed the HTML yet, hold on, we will cover that soon.

何が起こったのですか?

Scrapy schedules the scrapy.Request objects returned by the start_requests method of the Spider. Upon receiving a response for each one, it instantiates Response objects and calls the callback method associated with the request (in this case, the parse method) passing the response as argument.

start_requests メソッドを省略する

Instead of implementing a start_requests() method that generates scrapy.Request objects from URLs, you can just define a start_urls class attribute with a list of URLs. This list will then be used by the default implementation of start_requests() to create the initial requests for your 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)

The parse() method will be called to handle each of the requests for those URLs, even though we haven’t explicitly told Scrapy to do so. This happens because parse() is Scrapy’s default callback method, which is called for requests without an explicitly assigned callback.

データの抽出

The best way to learn how to extract data with Scrapy is trying selectors using the shell Scrapy shell. Run:

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

注釈

Remember to always enclose urls in quotes when running Scrapy shell from command-line, otherwise urls containing arguments (ie. & character) will not work.

On Windows, use double quotes instead:

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

You will see something like:

[ ... Scrapy log here ... ]
2016-09-19 12:09:27 [scrapy] 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.com/page/1/>
[s]   response   <200 http://quotes.toscrape.com/page/1/>
[s]   settings   <scrapy.settings.Settings object at 0x7fa91d888c10>
[s]   spider     <DefaultSpider 'default' at 0x7fa91c8af990>
[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
>>>

Using the shell, you can try selecting elements using CSS with the response object:

>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]

The result of running response.css('title') is a list-like object called SelectorList, which represents a list of Selector objects that wrap around XML/HTML elements and allow you to run further queries to fine-grain the selection or extract the data.

To extract the text from the title above, you can do:

>>> response.css('title::text').extract()
['Quotes to Scrape']

There are two things to note here: one is that we’ve added ::text to the CSS query, to mean we want to select only the text elements directly inside <title> element. If we don’t specify ::text, we’d get the full title element, including its tags:

>>> response.css('title').extract()
['<title>Quotes to Scrape</title>']

The other thing is that the result of calling .extract() is a list, because we’re dealing with an instance of SelectorList. When you know you just want the first result, as in this case, you can do:

>>> response.css('title::text').extract_first()
'Quotes to Scrape'

As an alternative, you could’ve written:

>>> response.css('title::text')[0].extract()
'Quotes to Scrape'

However, using .extract_first() avoids an IndexError and returns None when it doesn’t find any element matching the selection.

There’s a lesson here: for most scraping code, you want it to be resilient to errors due to things not being found on a page, so that even if some parts fail to be scraped, you can at least get some data.

Besides the extract() and extract_first() methods, you can also use the re() method to extract using regular expressions:

>>> response.css('title::text').re(r'Quotes.*')
['Quotes to Scrape']
>>> response.css('title::text').re(r'Q\w+')
['Quotes']
>>> response.css('title::text').re(r'(\w+) to (\w+)')
['Quotes', 'Scrape']

In order to find the proper CSS selectors to use, you might find useful opening the response page from the shell in your web browser using view(response). You can use your browser developer tools or extensions like Firebug (see sections about スクレイピングにFirebugを使用する and スクレイピングにFireFoxを使用する).

Selector Gadget is also a nice tool to quickly find CSS selector for visually selected elements, which works in many browsers.

XPath: かんたんな紹介

Besides CSS, Scrapy selectors also support using XPath expressions:

>>> response.xpath('//title')
[<Selector xpath='//title' data='<title>Quotes to Scrape</title>'>]
>>> response.xpath('//title/text()').extract_first()
'Quotes to Scrape'

XPath expressions are very powerful, and are the foundation of Scrapy Selectors. In fact, CSS selectors are converted to XPath under-the-hood. You can see that if you read closely the text representation of the selector objects in the shell.

While perhaps not as popular as CSS selectors, XPath expressions offer more power because besides navigating the structure, it can also look at the content. Using XPath, you’re able to select things like: select the link that contains the text “Next Page”. This makes XPath very fitting to the task of scraping, and we encourage you to learn XPath even if you already know how to construct CSS selectors, it will make scraping much easier.

We won’t cover much of XPath here, but you can read more about using XPath with Scrapy Selectors here. To learn more about XPath, we recommend this tutorial to learn XPath through examples, and this tutorial to learn “how to think in XPath”.

引用と著者の抽出

Now that you know a bit about selection and extraction, let’s complete our spider by writing the code to extract the quotes from the web page.

Each quote in http://quotes.toscrape.com is represented by HTML elements that look like this:

<div class="quote">
    <span class="text">“The world as we have created it is a process of our
    thinking. It cannot be changed without changing our thinking.”</span>
    <span>
        by <small class="author">Albert Einstein</small>
        <a href="/author/Albert-Einstein">(about)</a>
    </span>
    <div class="tags">
        Tags:
        <a class="tag" href="/tag/change/page/1/">change</a>
        <a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>
        <a class="tag" href="/tag/thinking/page/1/">thinking</a>
        <a class="tag" href="/tag/world/page/1/">world</a>
    </div>
</div>

Let’s open up scrapy shell and play a bit to find out how to extract the data we want:

$ scrapy shell 'http://quotes.toscrape.com'

We get a list of selectors for the quote HTML elements with:

>>> response.css("div.quote")

Each of the selectors returned by the query above allows us to run further queries over their sub-elements. Let’s assign the first selector to a variable, so that we can run our CSS selectors directly on a particular quote:

>>> quote = response.css("div.quote")[0]

Now, let’s extract title, author and the tags from that quote using the quote object we just created:

>>> title = quote.css("span.text::text").extract_first()
>>> title
'“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'
>>> author = quote.css("small.author::text").extract_first()
>>> author
'Albert Einstein'

Given that the tags are a list of strings, we can use the .extract() method to get all of them:

>>> tags = quote.css("div.tags a.tag::text").extract()
>>> tags
['change', 'deep-thoughts', 'thinking', 'world']

Having figured out how to extract each bit, we can now iterate over all the quotes elements and put them together into a Python dictionary:

>>> for quote in response.css("div.quote"):
...     text = quote.css("span.text::text").extract_first()
...     author = quote.css("small.author::text").extract_first()
...     tags = quote.css("div.tags a.tag::text").extract()
...     print(dict(text=text, author=author, tags=tags))
{'tags': ['change', 'deep-thoughts', 'thinking', 'world'], 'author': 'Albert Einstein', 'text': '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'}
{'tags': ['abilities', 'choices'], 'author': 'J.K. Rowling', 'text': '“It is our choices, Harry, that show what we truly are, far more than our abilities.”'}
    ... a few more of these, omitted for brevity
>>>
スパイダーのデータを抽出する

Let’s get back to our spider. Until now, it doesn’t extract any data in particular, just saves the whole HTML page to a local file. Let’s integrate the extraction logic above into our spider.

A Scrapy spider typically generates many dictionaries containing the data extracted from the page. To do that, we use the yield Python keyword in the callback, as you can see below:

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):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('span small::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

If you run this spider, it will output the extracted data with the log:

2016-09-19 18:57:19 [scrapy] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['life', 'love'], 'author': 'André Gide', 'text': '“It is better to be hated for what you are than to be loved for what you are not.”'}
2016-09-19 18:57:19 [scrapy] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['edison', 'failure', 'inspirational', 'paraphrased'], 'author': 'Thomas A. Edison', 'text': "“I have not failed. I've just found 10,000 ways that won't work.”"}

スクレイピングしたデータの保存

The simplest way to store the scraped data is by using Feed exports, with the following command:

scrapy crawl quotes -o quotes.json

That will generate an quotes.json file containing all scraped items, serialized in JSON.

For historic reasons, Scrapy appends to a given file instead of overwriting its contents. If you run this command twice without removing the file before the second time, you’ll end up with a broken JSON file.

You can also used other formats, like JSON Lines:

scrapy crawl quotes -o quotes.jl

The JSON Lines format is useful because it’s stream-like, you can easily append new records to it. It doesn’t have the same problem of JSON when you run twice. Also, as each record is a separate line, you can process big files without having to fit everything in memory, there are tools like JQ to help doing that at the command-line.

In small projects (like the one in this tutorial), that should be enough. However, if you want to perform more complex things with the scraped items, you can write an Item Pipeline. A placeholder file for Item Pipelines has been set up for you when the project is created, in tutorial/pipelines.py. Though you don’t need to implement any item pipelines if you just want to store the scraped items.

リンクを追う

Let’s say, instead of just scraping the stuff from the first two pages from http://quotes.toscrape.com, you want quotes from all the pages in the website.

Now that you know how to extract data from pages, let’s see how to follow links from them.

First thing is to extract the link to the page we want to follow. Examining our page, we can see there is a link to the next page with the following markup:

<ul class="pager">
    <li class="next">
        <a href="/page/2/">Next <span aria-hidden="true">&rarr;</span></a>
    </li>
</ul>

We can try extracting it in the shell:

>>> response.css('li.next a').extract_first()
'<a href="/page/2/">Next <span aria-hidden="true">→</span></a>'

This gets the anchor element, but we want the attribute href. For that, Scrapy supports a CSS extension that let’s you select the attribute contents, like this:

>>> response.css('li.next a::attr(href)').extract_first()
'/page/2/'

Let’s see now our spider modified to recursively follow the link to the next page, extracting data from it:

import scrapy


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

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('span small::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

Now, after extracting the data, the parse() method looks for the link to the next page, builds a full absolute URL using the urljoin() method (since the links can be relative) and yields a new request to the next page, registering itself as callback to handle the data extraction for the next page and to keep the crawling going through all the pages.

What you see here is Scrapy’s mechanism of following links: when you yield a Request in a callback method, Scrapy will schedule that request to be sent and register a callback method to be executed when that request finishes.

Using this, you can build complex crawlers that follow links according to rules you define, and extract different kinds of data depending on the page it’s visiting.

In our example, it creates a sort of loop, following all the links to the next page until it doesn’t find one – handy for crawling blogs, forums and other sites with pagination.

より多くの例とパターン

ここにコールバックを明示し, リンクをたどる別のスパイダーがあります. 今回は, 著者情報を集めます:

import scrapy


class AuthorSpider(scrapy.Spider):
    name = 'author'

    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        # follow links to author pages
        for href in response.css('.author+a::attr(href)').extract():
            yield scrapy.Request(response.urljoin(href),
                                 callback=self.parse_author)

        # follow pagination links
        next_page = response.css('li.next a::attr(href)').extract_first()
        if next_page is not None:
            next_page = response.urljoin(next_page)
            yield scrapy.Request(next_page, callback=self.parse)

    def parse_author(self, response):
        def extract_with_css(query):
            return response.css(query).extract_first().strip()

        yield {
            'name': extract_with_css('h3.author-title::text'),
            'birthdate': extract_with_css('.author-born-date::text'),
            'bio': extract_with_css('.author-description::text'),
        }

This spider will start from the main page, it will follow all the links to the authors pages calling the parse_author callback for each of them, and also the pagination links with the parse callback as we saw before.

The parse_author callback defines a helper function to extract and cleanup the data from a CSS query and yields the Python dict with the author data.

Another interesting thing this spider demonstrates is that, even if there are many quotes from the same author, we don’t need to worry about visiting the same author page multiple times. By default, Scrapy filters out duplicated requests to URLs already visited, avoiding the problem of hitting servers too much because of a programming mistake. This can be configured by the setting DUPEFILTER_CLASS.

Hopefully by now you have a good understanding of how to use the mechanism of following links and callbacks with Scrapy.

As yet another example spider that leverages the mechanism of following links, check out the CrawlSpider class for a generic spider that implements a small rules engine that you can use to write your crawlers on top of it.

Also, a common pattern is to build an item with data from more than one page, using a trick to pass additional data to the callbacks.

スパイダー引数の使用

You can provide command line arguments to your spiders by using the -a option when running them:

scrapy crawl quotes -o quotes-humor.json -a tag=humor

These arguments are passed to the Spider’s __init__ method and become spider attributes by default.

In this example, the value provided for the tag argument will be available via self.tag. You can use this to make your spider fetch only quotes with a specific tag, building the URL based on the argument:

import scrapy


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

    def start_requests(self):
        url = 'http://quotes.toscrape.com/'
        tag = getattr(self, 'tag', None)
        if tag is not None:
            url = url + 'tag/' + tag
        yield scrapy.Request(url, self.parse)

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

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

If you pass the tag=humor argument to this spider, you’ll notice that it will only visit URLs from the humor tag, such as http://quotes.toscrape.com/tag/humor.

You can learn more about handling spider arguments here.

次のステップ

This tutorial covered only the basics of Scrapy, but there’s a lot of other features not mentioned here. Check the 他には? section in Scrapy について chapter for a quick overview of the most important ones.

You can continue from the section 基本概念 to know more about the command-line tool, spiders, selectors and other things the tutorial hasn’t covered like modeling the scraped data. If you prefer to play with an example project, check the section.

学習する最善の方法は例を真似ることであり、Scrapyも例外ではありません. この理由から, quotesbot という名前のScrapyプロジェクトの例があります. こののプロジェクトを使用して、Scrapyについての詳細を学び、学ぶことができます. http://quotes.toscrape.com には2つのスパイダーがあり, 1つはCSSセレクターを使用し, もう1つはXPath式を使用します.

quotesbot プロジェクトは: https://github.com/scrapy/quotesbot で入手できます. 詳細はプロジェクトのREADMEにあります.

gitに精通している場合は, コードをチェックアウトすることができます. それ以外の場合は, ここ をクリックしてzipファイルとしてプロジェクトをダウンロードできます.

Scrapy について
Scrapy がどのようにしてあなたを手助けするかを理解する.
インストールガイド
コンピューターに Scraoy をインストールする方法.
Scrapy チュートリアル
最初の Scrapy プロジェクトを作成する.
あらかじめ作成された Scrapy プロジェクトで遊ぶことでさらに学ぶ.

基本概念

コマンドラインツール

バージョン 0.10 で追加.

ここでは, 「コマンド」または「Scrapyコマンド」と呼ばれるサブコマンドと区別するために, 「Scrapy ツール」と呼ばれる scrapy コマンドラインツールを使用して Scrapy を制御します.

Scrapyツールは, 複数の目的で複数のコマンドを提供し, それぞれが異なる引数とオプションのセットを受け入れます.

( scrapy deploy コマンドは 1.0 で廃止され,  scrapyd-deploy が採用されました. Deploying your project を参照してください.)

環境設定

Scrapy は標準的な以下の場所から scrapy.cfg ファイルの設定パラメータを探します:

  1. /etc/scrapy.cfg または c:\scrapy\scrapy.cfg (system-wide),
  2. グローバルセッティングのための ~/.config/scrapy.cfg ($XDG_CONFIG_HOME) と ~/.scrapy.cfg ($HOME) (user-wide),
  3. プロジェクトルート内にある scrapy.cfg (次のセクションを参照してください).

これらのファイルからの設定は, 表示された優先順位でマージされます. ユーザ定義の値はシステム全体のデフォルトよりも優先され, プロジェクト全体の設定は定義されている場合は他のすべての設定を上書きします.

また, Scrapy はいくつかの環境変数を理解し, 設定することができます. 現在, 以下のものが存在します:

Scrapyプロジェクトのデフォルト構造

コマンドラインツールとそのサブコマンドについて解説する前に, まずScrapyプロジェクトのディレクトリ構造について理解しておきましょう.

変更することはできますが, すべての Scrapy プロジェクトはデフォルトで同じファイル構造, もしくはこれに似た構造になっています:

scrapy.cfg
myproject/
    __init__.py
    items.py
    pipelines.py
    settings.py
    spiders/
        __init__.py
        spider1.py
        spider2.py
        ...

scrapy.cfg ファイルが存在するディレクトリは, プロジェクトルートディレクトリ と呼ばれます. このファイルには、プロジェクト設定を定義するpythonモジュールの名前が含まれています。次に例を示します:

[settings]
default = myproject.settings

scrapy ツールを使う

引数を指定せずにScrapyツールを実行すると, いくつかの使用方法のヘルプと使用可能なコマンドが表示されます:

Scrapy X.Y - no active project

Usage:
  scrapy <command> [options] [args]

Available commands:
  crawl         Run a spider
  fetch         Fetch a URL using the Scrapy downloader
[...]

あなたがScrapyプロジェクトの中にいる場合, 最初の行は現在アクティブなプロジェクトを出力します. 上記の例では, プロジェクトの外から実行されました. プロジェクトの中から実行すると、次のような内容が出力されます:

Scrapy X.Y - project: myproject

Usage:
  scrapy <command> [options] [args]

[...]
プロジェクトの作成

まず, scrapy ツールで最初に行うことは, あなたのScrapyプロジェクトを作成することです:

scrapy startproject myproject [project_dir]

これにより, project_dir ディレクトリの下に Scrapy プロジェクトが作成されます. project_dir が指定されていない場合, project_dirmyproject と同じになります.

次に, 新しいプロジェクトディレクトリ中に移動します:

cd project_dir

これで, scrapy コマンドを使用してそこからプロジェクトを管理および制御する準備が整いました.

プロジェクトの制御

プロジェクトの中から scrapy ツールを使用して, プロジェクトを制御および管理します.

例えば, 新しいスパイダーを作成するには:

scrapy genspider mydomain mydomain.com

一部のScrapyコマンド ( crawl など) は, Scrapyプロジェクト内から実行する必要があります. どのコマンドをプロジェクト内から実行する必要があるかについての詳細は, 以下の コマンドリファレンス を参照してください.

また、いくつかのコマンドは、プロジェクトの中から実行する際に, 少し違う振る舞いをすることがあります. たとえば、フェッチされたURLが特定のスパイダーに関連付けられている場合, fetch コマンドは spider-overridden ビヘイビア(user-agent 属性を上書きする user_agent など)を使用します. fetch コマンドは, スパイダーがページをどのようにダウンロードしているかを確認するために使用されるため, 意図的に行っています.

利用可能なコマンド

このセクションでは, 使用可能な組み込みコマンドのリストと, 使用例を示します. それぞれのコマンドについての詳細は, 以下のコマンドでいつでも確認できます:

scrapy <command> -h

または, 使用可能なすべてのコマンドは, 以下で確認できます:

scrapy -h

コマンドは, アクティブなScrapyプロジェクトなしでのみ動作するコマンド(グローバルコマンド)と, プロジェクト内から実行するコマンドの動作が若干異なる場合があります(プロジェクトオーバーライド設定を使用するため).

グローバルコマンド:

プロジェクト下でのみ使用可能なコマンド:

startproject
  • シンタックス: scrapy startproject <project_name> [project_dir]
  • プロジェクトに必要か: no

 project_dir ディレクトリ下に project_name という名前の新しい Scrapy プロジェクトを作成します. もし,  project_dir が指定されていない場合, プロジェクト名と同じ名前の project_dir が作成されます.

使用例:

$ scrapy startproject myproject
genspider
  • シンタックス: scrapy genspider [-t template] <name> <domain>
  • プロジェクトに必要か: no

プロジェクト内から呼び出された場合は, 現在のフォルダまたは現在のプロジェクトの``spiders`` フォルダに新しいスパイダーを作成します. <name> パラメータはスパイダの名前として設定され,  <domain> はスパイダーの allowed_domains および``start_urls`` 属性を生成するために使用されます.

使用例:

$ scrapy genspider -l
Available templates:
  basic
  crawl
  csvfeed
  xmlfeed

$ scrapy genspider example example.com
Created spider 'example' using template 'basic'

$ scrapy genspider -t crawl scrapyorg scrapy.org
Created spider 'scrapyorg' using template 'crawl'

これはあらかじめ定義されたテンプレートに基づいてスパイダーを作成する便利なショートカットコマンドですが, スパイダーを作成する唯一の方法ではありません. このコマンドを使用する代わりに, スパイダーのソースコードファイルを自分で作成することもできます.

crawl
  • シンタックス: scrapy crawl <spider>
  • プロジェクトに必要か: yes

スパイダーを使用してクロールを始める.

使用例:

$ scrapy crawl myspider
[ ... myspider starts crawling ... ]
check
  • シンタックス: scrapy check [-l] <spider>
  • プロジェクトに必要か: yes

コントラクトチェックを実行する.

使用例:

$ scrapy check -l
first_spider
  * parse
  * parse_item
second_spider
  * parse
  * parse_item

$ scrapy check
[FAILED] first_spider:parse_item
>>> 'RetailPricex' field is missing

[FAILED] first_spider:parse
>>> Returned 92 requests, expected 0..4
list
  • シンタックス: scrapy list
  • プロジェクトに必要か: yes

現在のプロジェクトで使用可能なすべてのスパイダーを一覧表示します. 出力は, 1行に1つのスパイダーです.

使用例:

$ scrapy list
spider1
spider2
edit
  • シンタックス: scrapy edit <spider>
  • プロジェクトに必要か: yes

EDITOR 設定で定義されたエディタを使用して, 指定されたスパイダーを編集します.

このコマンドは, 便利なショートカットとしてのみ提供されています. 開発者はもちろん, ツールやIDEを自由に選択して, スパイダーを作成・デバッグできます.

使用例:

$ scrapy edit spider1
fetch
  • シンタックス: scrapy fetch <url>
  • プロジェクトに必要か: no

Scrapy ダウンローダーを使用してURLからダウンロードし, その内容を標準出力に書き出します.

このコマンドの興味深い点は, スパイダーがどのようにダウンロードするかをページから取得することです. たとえば, スパイダーがUser Agentを上書きする USER_AGENT 属性を持っている場合は, それを使用します.

このコマンドは, あなたのスパイダーが特定のページをどのようにフェッチするかを “見る” ために使うことができます.

プロジェクトの外で使用される場合は, スパイダーごとの特定の動作は適用されず, デフォルトのScrapyダウンローダ設定を使用します.

使用例:

$ scrapy fetch --nolog http://www.example.com/some/page.html
[ ... html content here ... ]

$ scrapy fetch --nolog --headers http://www.example.com/
{'Accept-Ranges': ['bytes'],
 'Age': ['1263   '],
 'Connection': ['close     '],
 'Content-Length': ['596'],
 'Content-Type': ['text/html; charset=UTF-8'],
 'Date': ['Wed, 18 Aug 2010 23:59:46 GMT'],
 'Etag': ['"573c1-254-48c9c87349680"'],
 'Last-Modified': ['Fri, 30 Jul 2010 15:30:18 GMT'],
 'Server': ['Apache/2.2.3 (CentOS)']}
view
  • シンタックス: scrapy view <url>
  • プロジェクトに必要か: no

Scrapyスパイダーがそれを “見る” ようにブラウザでURLを開きます. スパイダーは通常のユーザーとは違うページを表示することがあるので, スパイダーが何を見ているかを確認し, 期待通りのものかどうかを確認することができます.

使用例:

$ scrapy view http://www.example.com/some/page.html
[ ... browser starts ... ]
shell
  • シンタックス: scrapy shell [url]
  • プロジェクトに必要か: no

指定されたURL(指定されている場合)またはURLが指定されていない場合は空のScrapyシェルを開始します. また, UNIX形式のローカルファイルパスをサポートしています. ./ または ../ を接頭辞とした相対パス, もしくは絶対パスです. 詳細については,  Scrapy シェル を参照してください.

使用例:

$ scrapy shell http://www.example.com/some/page.html
[ ... scrapy shell starts ... ]
parse
  • シンタックス: scrapy parse <url> [options]
  • プロジェクトに必要か: yes

指定されたURLを取得し、それをスパイダーで処理・解析します.  --callback オプションで渡されたメソッドを使用します. 指定されていない場合は parse メソッドを使用します.

サポートされているオプション:

  • --spider=SPIDER: スパイダーの自動検出をバイパスし, 特定のスパイダーを強制的に使用する
  • --a NAME=VALUE: スパイダー引数を設定する(繰り返してもよい)
  • --callback または -c: レスポンスを解析するためのコールバックとして使用するspiderメソッド
  • --pipelines: パイプラインを通じてアイテムを処理する
  • --rules または -r:  CrawlSpider のルールを使用して, レスポンスの解析に使用するコールバック (i.e. spider メソッド) を検出する
  • --noitems: スクレイピングしたアイテムを表示しない
  • --nolinks: 抽出されたリンクを表示しない
  • --nocolour: 出力の色分けを行わない
  • --depth または -d: 要求を再帰的に追跡する深さレベル(デフォルト:1)
  • --verbose または -v: 各深度レベルの情報を表示する

使用例:

$ scrapy parse http://www.example.com/ -c parse_item
[ ... scrapy log lines crawling example.com spider ... ]

>>> STATUS DEPTH LEVEL 1 <<<
# Scraped Items  ------------------------------------------------------------
[{'name': u'Example item',
 'category': u'Furniture',
 'length': u'12 cm'}]

# Requests  -----------------------------------------------------------------
[]
settings
  • シンタックス: scrapy settings [options]
  • プロジェクトに必要か: no

Scrapy設定の値を取得します.

プロジェクト内で使用されている場合はプロジェクト設定値が表示され, そうでない場合はその設定のデフォルトのScrapy値が表示されます.

使用例:

$ scrapy settings --get BOT_NAME
scrapybot
$ scrapy settings --get DOWNLOAD_DELAY
0
runspider
  • シンタックス: scrapy runspider <spider_file.py>
  • プロジェクトに必要か: no

プロジェクトを作成せずに, Pythonファイルに含まれているスパイダーを実行します.

使用例:

$ scrapy runspider myspider.py
[ ... spider starts crawling ... ]
version
  • シンタックス: scrapy version [-v]
  • プロジェクトに必要か: no

Scrapy のバージョンを表示します.  -v と一緒に使用すると, バグレポートに便利な Python, Twisted, そしてプラットフォームの情報も表示されます.

bench

バージョン 0.17 で追加.

  • シンタックス: scrapy bench
  • プロジェクトに必要か: no

かんたんなベンチマークテストを実行します. ベンチマーク を参照してください.

カスタムプロジェクトコマンド

COMMANDS_MODULE 設定を使用してカスタムプロジェクトコマンドを追加することができます. コマンドの実装方法の例については, scrapy/コマンド の Scrapy コマンドを参照してください.

COMMANDS_MODULE

初期値: '' (空文字列)

カスタムのScrapyコマンドを検索するためのモジュール. これは Scrapy プロジェクトのカスタムコマンドを追加するために使用されます.

例:

COMMANDS_MODULE = 'mybot.commands'
setup.pyエントリポイントを介してコマンドを登録する

注釈

これは実験的な機能なので注意してください.

scrapy.commands ファイルのエントリポイントに,  setup.py セクションを追加することで, 外部ライブラリから Scrapy コマンドを追加することもできます.

次の例では,  my_command コマンドを追加しています:

from setuptools import setup, find_packages

setup(name='scrapy-mymodule',
  entry_points={
    'scrapy.commands': [
      'my_command=my_scrapy_module.commands:MyCommand',
    ],
  },
 )

スパイダー

パイダーとは, クロールの実行方法(リンクをたどる方法)やページから構造化データを抽出する方法(アイテムのスクレイピングなど)を含む, 特定のサイト(またはサイトのグループ)のスクレイピング方法を定義するクラスです. 言い換えれば, スパイダー は, 特定のサイト(場合によってはサイトのグループ)のページをクロールして 解析するためのカスタム動作を定義する場所です.

スパイダーの場合, スクレイピングのライフサイクルは以下のようになります:

  1. 最初のURLをクロールする最初のリクエストを生成し, それらのリクエストからダウンロードされたレスポンスで呼び出されるコールバック関数を指定します.

    実行する最初のリクエストは, start_requests() メソッドを呼び出すことによって取得されます. このメソッドは, デフォルトで start_urls で指定されたURLと Request のコールバック関数として呼ばれる parse メソッドで, Request を生成します.

  2. コールバック関数では, レスポンス(Webページ)を解析し, 抽出されたデータ, Item オブジェクト, Request オブジェクト, またはこれらのオブジェクトの反復可能なものを返します これらのリクエストには, コールバックが含まれ, Scrapyによってダウンロードされ, その後指定されたコールバックによってリクエストが処理されます.

  3. コールバック関数では, セレクタ を使用してページの内容を解析します(ただし, BeautifulSoup, lxmlなどの任意のメカニズムを使用することもできます).

  4. 最後に, スパイダーから返されたアイテムは, 通常, データベース(一部の アイテムパイプライン 内)に永続化されるか, または フィードのエクスポート を使用してファイルに書き込まれます.

このサイクルはどんな種類のスパイダーにも(多かれ少なかれ)適用されますが, さまざまな種類のデフォルトのスパイダーが Scrapy にバンドルされています. これらのタイプについてはここで説明します.

scrapy.Spider

class scrapy.spiders.Spider

これは最もシンプルなスパイダーで, 他のすべてのスパイダーが継承しなければならないものです(Scrapyにバンドルされたスパイダー, あなた自身で作成したスパイダーを含む). 特別な機能は提供しません. start_urls 属性からリクエストを送信し, スパイダーの parse メソッドを, レスポンス結果ごとに呼び出す start_requests() メソッドの実装を提供するだけです.

name

スパイダーの名前を定義する文字列. 名前は, スパイダーが Scrapy によってどのように配置(インスタンス化)されているか判別するために, ユニークでなければなりません. ただし, 同じスパイダーのインスタンスは一つだけ作成可能で, 複数インスタンス化することはできません. これは最も重要なスパイダー属性であり, 必須です.

スパイダーが単一のドメインをスクラップする場合, 一般的には,  TLD の有無にかかわらず, ドメイン名と同じの名前を付けます. したがって, たとえば, mywebsite.com をクロールするスパイダーには,  mywebsite という名前をつけます.

注釈

Python 2 では, ASCIIのみでなければなりません.

allowed_domains

このスパイダーがクロールできるドメインを含む文字列のオプションのリスト. OffsiteMiddleware が有効になっている場合, このリスト(またはそのサブドメイン)で指定された ドメイン名に属していないURLに対するリクエストは追跡されません.

start_urls

特定のURLが指定されていない場合, スパイダーがクロールを開始するURLのリスト. したがって, ダウンロードされる最初のページはここにリストされたページになります. 後続のURLは, 開始URLに含まれるデータから順番に生成されます.

custom_settings

このスパイダーを実行するときにオーバーライドされるプロジェクトの設定の辞書. インスタンス化前に設定が更新されるため, クラス属性として定義する必要があります.

使用可能なビルトイン設定のリストについては, ビルトイン設定リファレンス を参照してください.

crawler

この属性は, クラスを初期化した後の from_crawler() クラスメソッドによって設定され, このスパイダーインスタンスがバインドされている Crawler オブジェクトへのリンクになります.

クローラは, 単一エントリアクセス(エクステンション, ミドルウェア, シグナルマネージャなど)のために, プロジェクト内の多くのコンポーネントをカプセル化します. 詳細については クローラー API を参照してください.

settings

このスパイダーを実行するための設定. これは Settings インスタンスです. このトピックの詳細な紹介については, 設定 を参照してください.

logger

スパイダーの name で作成された Python ロガー. スパイダーからのロギング で説明しているように, これを使ってログメッセージを送信することができます.

from_crawler(crawler, *args, **kwargs)

これは Scrapy があなたのスパイダーを作成するために使用するクラスメソッドです.

デフォルトの実装は __init__() メソッドへのプロキシとして機能し, 与えられた引数 args と名前付き引数 kwargs を呼び出すので, これを直接オーバーライドする必要はないでしょう.

それにもかかわらず, このメソッドは crawlersettings 属性を新しいインスタンスに設定し, 後でスパイダのコード内でアクセスできるようにします.

パラメータ:
  • crawler (Crawler インスタンス) – スパイダーにバインドされるクローラー
  • args (list) – __init__() メソッドに渡される引数
  • kwargs (dict) – __init__() メソッドに渡されるキーワード引数
start_requests()

このメソッドは, スパイダーの最初のクロールリクエストで繰り返し可能な値を返す必要があります.

これは, 特定のURLが指定されずにスパイダーを開いてスクレイピングするときに, Scrapy によって呼び出されるメソッドです. 特定のURLが指定されている場合, 代わりに make_requests_from_url() がリクエストを作成するために使用されます. このメソッドはScrapyから1回だけ呼び出されるため, 安全にジェネレータとして実装することができます.

デフォルトの実装では, make_requests_from_url() を使用して, start_urls 内の各URLのリクエストを生成します.

ドメインのスクレイピングを開始するために使用されるリクエストを変更したい場合は, これをオーバーライドすることができます. たとえば, POSTリクエストを使用してログインする必要がある場合は:

class MySpider(scrapy.Spider):
    name = 'myspider'

    def start_requests(self):
        return [scrapy.FormRequest("http://www.example.com/login",
                                   formdata={'user': 'john', 'pass': 'secret'},
                                   callback=self.logged_in)]

    def logged_in(self, response):
        # here you would extract links to follow and return Requests for
        # each of them, with another callback
        pass
make_requests_from_url(url)

URLを受け取って, Request オブジェクト (または Request オブジェクトのリスト) を返すメソッド. このメソッドは, start_requests() メソッドで初期リクエストを作成するために使用され, 通常は URL をリクエストに変換するために使用されます.

オーバーライドされない限り, このメソッドは, callback関数として parse() メソッドを使用し, パラメータを有効にしてリクエストを返します (詳細は Request クラスを参照してください).

parse(response)

これは, リクエストがコールバックを指定していないときに, ダウンロードされたレスポンスを処理するために Scrapy に使用されるデフォルトのコールバックです.

parse ソッドは, レスポンスを処理し, スクレイピングされたデータおよび/または, より多くのURLを返すことを担当します. その他のリクエストコールバックは Spider クラスと同じ要件を持ちます.

このメソッドおよび他のRequestコールバックは, イテレータブルな Request および/または, dict または, Item オブジェクトを返さなければなりません.

パラメータ:response (Response) – パースするレスポンス
log(message[, level, component])

スパイダーの logger を介してログメッセージを送信し, 下位互換性を保つために保管されたラッパー. 詳細については スパイダーからのロギング を参照してください.

closed(reason)

スパイダーが閉じたときに呼び出されます. このメソッドは, spider_closed シグナルを送信するための signals.connect() メソッドのショートカットを提供します.

例を見てみましょう:

import scrapy


class MySpider(scrapy.Spider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = [
        'http://www.example.com/1.html',
        'http://www.example.com/2.html',
        'http://www.example.com/3.html',
    ]

    def parse(self, response):
        self.logger.info('A response from %s just arrived!', response.url)

単一のコールバックから複数のリクエストとアイテムを返します:

import scrapy

class MySpider(scrapy.Spider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = [
        'http://www.example.com/1.html',
        'http://www.example.com/2.html',
        'http://www.example.com/3.html',
    ]

    def parse(self, response):
        for h3 in response.xpath('//h3').extract():
            yield {"title": h3}

        for url in response.xpath('//a/@href').extract():
            yield scrapy.Request(url, callback=self.parse)

start_urls の代わりに start_requests() を直接使うことができます. アイテム を使用することで多くの構造体にデータを与えることができます:

import scrapy
from myproject.items import MyItem

class MySpider(scrapy.Spider):
    name = 'example.com'
    allowed_domains = ['example.com']

    def start_requests(self):
        yield scrapy.Request('http://www.example.com/1.html', self.parse)
        yield scrapy.Request('http://www.example.com/2.html', self.parse)
        yield scrapy.Request('http://www.example.com/3.html', self.parse)

    def parse(self, response):
        for h3 in response.xpath('//h3').extract():
            yield MyItem(title=h3)

        for url in response.xpath('//a/@href').extract():
            yield scrapy.Request(url, callback=self.parse)

スパイダーの引数

スパイダーは行動を変更する引数を受け取ることができます. スパイダー引数の一般的な用途の1つは, 開始URLを定義するか, サイトの特定のセクションにクロールを制限することですが, スパイダーの機能を構成するために使用できます.

スパイダーの引数は, -a オプションを使用して crawl コマンドに渡されます. 例えば:

scrapy crawl myspider -a category=electronics

スパイダーは __init__ メソッドで引数にアクセスできます:

import scrapy

class MySpider(scrapy.Spider):
    name = 'myspider'

    def __init__(self, category=None, *args, **kwargs):
        super(MySpider, self).__init__(*args, **kwargs)
        self.start_urls = ['http://www.example.com/categories/%s' % category]
        # ...

スパイダーの引数は, Scrapydの schedule.json API を介して渡すこともできます. Scrapyd documentation を参照してください.

一般的なスパイダー

Scrapy には, スパイダーをサブクラス化するために使用できる, いくつかの有用なスパイダーがあります. その目的は, 特定のルールに基づいてサイトのすべてのリンクをたどったり, Sitemaps からクロールしたり, XML / CSV フィードを解析するなど, いくつかの一般的なスクラップケースに対して便利な機能を提供することです

以下のスパイダーで使用されているサンプルについては, myproject.items モジュールで宣言された TestItem を持つプロジェクトがあると仮定します:

import scrapy

class TestItem(scrapy.Item):
    id = scrapy.Field()
    name = scrapy.Field()
    description = scrapy.Field()
CrawlSpider
class scrapy.spiders.CrawlSpider

これは定期的なウェブサイトをクロールするために最も一般的に使用されるスパイダーです. 一連のルールを定義してリンクをたどるための便利なメカニズムを提供します. 特定のウェブサイトやプロジェクトには最適ではないかもしれませんが, いくつかのケースでは十分に一般的なので, より多くのカスタム機能のために必要に応じて上書きしたり, 独自のスパイダーを実装することができます.

Spiderから継承した属性(指定する必要がある)を除いて, このクラスは新しい属性をサポートします:

rules

1つ(または複数)の Rule オブジェクトのリストです. 各 Rule は, サイトをクロールするための特定の動作を定義します. Rules オブジェクトについては以下で説明します. 複数のルールが同じリンクに一致する場合, この属性で定義されている順序に従って, 最初のリンクが使用されます.

このスパイダーは, オーバーライド可能なメソッドも公開しています:

parse_start_url(response)

このメソッドは, start_urlsレスポンスに対して呼び出されます. これは初期応答を解析することを可能にし, Item オブジェクト, Request オブジェクト, またはそれらのいずれかを含む iterable を返さなければなりません.

Rule
class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)

link_extractor は, クロールされた各ページからリンクを抽出する方法を定義する Link Extractor オブジェクトです.

callback は抽出されたリンクごとに呼び出される, 呼び出し可能または文字列です(この場合, その名前を持つスパイダーオブジェクトのメソッドが使用されます). このコールバックは, 最初の引数としてレスポンスを受け取り, Item オブジェクトおよび/または Request オブジェクト(またはそれらのサブクラス)を含むリストを返す必要があります.

警告

クロールスパイダールールを作成するときは, CrawlSpider は parse メソッド自体を使用してロジックを実装するため, parse メソッドをコールバックとして使用しないでください. parse メソッドをオーバーライドすると, CrawlSpider が機能しなくなります.

cb_kwargs は, コールバック関数に渡すキーワード引数を含む dict です.

follow は, このルールで抽出された各レスポンスからリンクをたどるかどうかを指定する bool 値です. callback が None の場合, follow のデフォルトは True になります. それ以外の場合は False にデフォルト設定されます

process_links は呼び出し可能です. 指定された link_extractor を使用して各レスポンスから抽出されたリンクのリストごとに呼び出される文字列 (その名前のスパイダーオブジェクトのメソッドが使用されます)です. これは, 主にフィルタリングの目的で使用されます.

process_request は呼び出し可能です. このルールで抽出されたリクエストごとに呼び出され, リクエストを返す必要があります(リクエストをフィルタにかけるには, その名前を持つスパイダオブジェクトのメソッドが呼び出されます).

CrawlSpider の設定例

ルールを使用したCrawlSpiderの例を見てみましょう:

import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor

class MySpider(CrawlSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com']

    rules = (
        # 'category.php' に一致するリンクを抽出する ('subsection.php' とは一致しません)
        # そして, それらのリンクをたどります (なぜならコールバックはデフォルトで follow=True ではないからです).
        Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),

        # 'allow' と一致するリンクを抽出し, スパイダーの parse_item() メソッドで解析します
        Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
    )

    def parse_item(self, response):
        self.logger.info('Hi, this is an item page! %s', response.url)
        item = scrapy.Item()
        item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
        item['name'] = response.xpath('//td[@id="item_name"]/text()').extract()
        item['description'] = response.xpath('//td[@id="item_description"]/text()').extract()
        return item

このスパイダーは, example.comのホームページをクロールし, カテゴリリンクとアイテムリンクを収集し, 後者を parse_item メソッドで解析します. 各アイテムのレスポンスでは, XPathを使用してHTMLからいくつかのデータが抽出され, Item にそのデータをいれています.

XMLFeedSpider
class scrapy.spiders.XMLFeedSpider

XMLFeedSpider は, XML フィードを特定のノード名で繰り返し解析するために設計されています. イテレーター は  iternodes, xml, 及び html から選択することができます. xmlhtml のイテレーターは, 解析するために一度に DOM 全体を生成するので, パフォーマンス上の理由から iternode イテレーターを使用することをお勧めします. しかし, イテレータとして html を使用すると, だめなマークアップで書かれた XML を解析するときに便利です.

イテレータとタグ名を設定するには, 次のクラス属性を定義する必要があります:

iterator

使用するイテレータを定義する文字列:

  • 'iternodes' - 正規表現に基づく高速なイテレータ
  • 'html' - Selector を使用するイテレータ.

  これは DOM 解析を使用しており, 大きなフィードの問題となる可能性のあるすべての DOM をメモリにロードする必要があることに注意してください

  • 'xml' - Selector を使用するイテレータ.

  これは DOM 解析を使用しており, 大きなフィードの問題となる可能性のあるすべての DOM をメモリにロードする必要があることに注意してください

デフォルト: 'iternodes'.

itertag

反復処理するノード(または要素)の名前の文字列.

例:

itertag = 'product'
namespaces

このスパイダーで処理される, 文書で利用可能な名前空間を定義する (prefix, uri) タプルのリスト. prefixuri は, register_namespace() メソッドを使って名前空間を自動的に登録するために使われます.

itertag 属性に名前空間を持つノードを指定できます.

例:

class YourSpider(XMLFeedSpider):

    namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
    itertag = 'n:url'
    # ...

これらの属性とは別に, このスパイダーは次の無効化可能なメソッドも持っています:

adapt_response(response)

スパイダーミドルウェアから送られたレスポンスを, スパイダーが解析を開始する前に受け取るメソッド. これは, 解析する前にレスポンス本体の内容を変更するために使用できます. このメソッドはレスポンスを受け取り, レスポンスを返します(同じものか別のものかが返されます).

parse_node(response, selector)

このメソッドは, 指定されたタグ名 (itertag) と一致するノードに対して呼び出されます. 各ノードのレスポンスと Selector を受け取ります. このメソッドをオーバーライドすることは必須です. そうしなければ, スパイダーが動作しません. このメソッドは, Item オブジェクト, Request オブジェクト, またはいずれかを含む iterable を返す必要があります.

process_results(response, results)

このメソッドは, スパイダーから返された各結果(アイテムまたはリクエスト)に対して呼び出され, 結果をフレームワークコアに返す前に必要な最後の処理(たとえば, アイテムIDの設定)を実行するためのものです. これは結果のリストとそれらの結果を生み出したレスポンスを受け取ります. 結果のリスト(アイテムまたはレスポンス)を返す必要があります.

XMLFeedSpider の例

これらのスパイダーはかなり使いやすいです. 一例を見てみましょう:

from scrapy.spiders import XMLFeedSpider
from myproject.items import TestItem

class MySpider(XMLFeedSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/feed.xml']
    iterator = 'iternodes'  # This is actually unnecessary, since it's the default value
    itertag = 'item'

    def parse_node(self, response, node):
        self.logger.info('Hi, this is a <%s> node!: %s', self.itertag, ''.join(node.extract()))

        item = TestItem()
        item['id'] = node.xpath('@id').extract()
        item['name'] = node.xpath('name').extract()
        item['description'] = node.xpath('description').extract()
        return item

上記は基本的な, 指定された start_urls からフィードをダウンロードし, それぞれの item タグを繰り返して印刷し, ランダムなデータを Item に格納するスパイダーです.

CSVFeedSpider
class scrapy.spiders.CSVFeedSpider

このスパイダーはXMLFeedSpiderと非常に似ていますが, ノードの代わりに行を反復する点が異なります. 各反復で呼び出されるメソッドは, parse_row() です.

delimiter

CSVファイルの各フィールドの区切り文字を含む文字列. デフォルトは ',' (コンマ).

quotechar

CSVファイル内の各フィールドのエンクロージャ文字を含む文字列. デフォルトは '"' (引用符)です.

headers

ファイルからフィールドを抽出するために使用されるCSVファイルのフィードに含まれる行のリスト.

parse_row(response, row)

CSVファイルの各ヘッダーのキーを使用(または検出)して, レスポンスと dict (各行を表す)を受け取ります. このスパイダーは, 事前処理と後処理の目的で adapt_response メソッドと process_results メソッドをオーバーライドする機会も与えます.

CSVFeedSpider の例

CSVFeedSpider を使用した, 前の例に似た例を見てみましょう:

from scrapy.spiders import CSVFeedSpider
from myproject.items import TestItem

class MySpider(CSVFeedSpider):
    name = 'example.com'
    allowed_domains = ['example.com']
    start_urls = ['http://www.example.com/feed.csv']
    delimiter = ';'
    quotechar = "'"
    headers = ['id', 'name', 'description']

    def parse_row(self, response, row):
        self.logger.info('Hi, this is a row!: %r', row)

        item = TestItem()
        item['id'] = row['id']
        item['name'] = row['name']
        item['description'] = row['description']
        return item
SitemapSpider
class scrapy.spiders.SitemapSpider

SitemapSpider では, Sitemaps を使ってURLを発見することでサイトをクロールできます.

これはネストされたサイトマップをサポートし,  robots.txt からサイトマップのURLを見つけることもできます.

sitemap_urls

クロールしたいサイトマップを指すURLのリスト.

サイトマップのURLを抽出するために robots.txt を指定することもできます.

sitemap_rules

タプル (regex, callback) のリスト:

  • regex は, サイトマップから抽出されたURLに一致する正規表現です. regex は str かコンパイルされた正規表現オブジェクトのどちらかです.
  • callback は, 正規表現に一致するURLを処理するために使用するコールバックです. callback は文字列(スパイダーメソッドの名前を示す)または呼び出し可能なものにすることができます.

例えば:

sitemap_rules = [('/product/', 'parse_product')]

ルールは順番に適用され, 一致する最初のルールのみが使用されます.

この属性を省略すると, サイトマップ内のすべてのURLが parse コールバックで処理されます.

sitemap_follow

あなたが従うべきサイトマップの正規表現のリスト. これは, 他のサイトマップファイルを指す Sitemap index files を使用するサイトのみに適用されます.

デフォルトでは, すべてのサイトマップに従います.

url の代替リンクに従うかどうかを指定します. これらは, 同じ url ブロック内で渡された別の言語の同じWebサイトのリンクです.

例えば:

<url>
    <loc>http://example.com/</loc>
    <xhtml:link rel="alternate" hreflang="de" href="http://example.com/de"/>
</url>

上記の例では, sitemap_alternate_links が設定されていると, 両方のURLが取得されます. sitemap_alternate_links を無効にすると, http://example.com/ のみが取得されます.

デフォルトでは sitemap_alternate_links は無効化されています.

SitemapSpider の例

かんたんな例: サイトマップを利用して見つけたURLを, すべて parse コールバックによって処理する:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/sitemap.xml']

    def parse(self, response):
        pass # ... scrape item here ...

特定のコールバックと, いくつかの別のコールバックを持つ他のURLを処理する:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/sitemap.xml']
    sitemap_rules = [
        ('/product/', 'parse_product'),
        ('/category/', 'parse_category'),
    ]

    def parse_product(self, response):
        pass # ... scrape product ...

    def parse_category(self, response):
        pass # ... scrape category ...

robots.txt ファイルによって定義されたサイトマップと, 一部に /sitemap_shop を含むURLのみを処理する:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/robots.txt']
    sitemap_rules = [
        ('/shop/', 'parse_shop'),
    ]
    sitemap_follow = ['/sitemap_shops']

    def parse_shop(self, response):
        pass # ... scrape shop here ...

SitemapSpiderと他のURLのソースを結合する:

from scrapy.spiders import SitemapSpider

class MySpider(SitemapSpider):
    sitemap_urls = ['http://www.example.com/robots.txt']
    sitemap_rules = [
        ('/shop/', 'parse_shop'),
    ]

    other_urls = ['http://www.example.com/about']

    def start_requests(self):
        requests = list(super(MySpider, self).start_requests())
        requests += [scrapy.Request(x, self.parse_other) for x in self.other_urls]
        return requests

    def parse_shop(self, response):
        pass # ... scrape shop here ...

    def parse_other(self, response):
        pass # ... scrape other here ...

セレクタ

When you’re scraping web pages, the most common task you need to perform is to extract data from the HTML source. There are several libraries available to achieve this:

  • BeautifulSoup is a very popular web scraping library among Python programmers which constructs a Python object based on the structure of the HTML code and also deals with bad markup reasonably well, but it has one drawback: it’s slow.
  • lxml is an XML parsing library (which also parses HTML) with a pythonic API based on ElementTree. (lxml is not part of the Python standard library.)

Scrapy comes with its own mechanism for extracting data. They’re called selectors because they “select” certain parts of the HTML document specified either by XPath or CSS expressions.

XPath is a language for selecting nodes in XML documents, which can also be used with HTML. CSS is a language for applying styles to HTML documents. It defines selectors to associate those styles with specific HTML elements.

Scrapy selectors are built over the lxml library, which means they’re very similar in speed and parsing accuracy.

This page explains how selectors work and describes their API which is very small and simple, unlike the lxml API which is much bigger because the lxml library can be used for many other tasks, besides selecting markup documents.

For a complete reference of the selectors API see Selector reference

セレクタの使用

セレクタの作成

Scrapy selectors are instances of Selector class constructed by passing text or TextResponse object. It automatically chooses the best parsing rules (XML vs HTML) based on input type:

>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse

Constructing from text:

>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']

Constructing from response:

>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').extract()
[u'good']

For convenience, response objects expose a selector on .selector attribute, it’s totally OK to use this shortcut when possible:

>>> response.selector.xpath('//span/text()').extract()
[u'good']
セレクタの使用

To explain how to use the selectors we’ll use the Scrapy shell (which provides interactive testing) and an example page located in the Scrapy documentation server:

Here’s its HTML code:

<html>
 <head>
  <base href='http://example.com/' />
  <title>Example website</title>
 </head>
 <body>
  <div id='images'>
   <a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
  </div>
 </body>
</html>

First, let’s open the shell:

scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html

Then, after the shell loads, you’ll have the response available as response shell variable, and its attached selector in response.selector attribute.

Since we’re dealing with HTML, the selector will automatically use an HTML parser.

So, by looking at the HTML code of that page, let’s construct an XPath for selecting the text inside the title tag:

>>> response.selector.xpath('//title/text()')
[<Selector (text) xpath=//title/text()>]

Querying responses using XPath and CSS is so common that responses include two convenience shortcuts: response.xpath() and response.css():

>>> response.xpath('//title/text()')
[<Selector (text) xpath=//title/text()>]
>>> response.css('title::text')
[<Selector (text) xpath=//title/text()>]

As you can see, .xpath() and .css() methods return a SelectorList instance, which is a list of new selectors. This API can be used for quickly selecting nested data:

>>> response.css('img').xpath('@src').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']

To actually extract the textual data, you must call the selector .extract() method, as follows:

>>> response.xpath('//title/text()').extract()
[u'Example website']

If you want to extract only first matched element, you can call the selector .extract_first()

>>> response.xpath('//div[@id="images"]/a/text()').extract_first()
u'Name: My image 1 '

It returns None if no element was found:

>>> response.xpath('//div[@id="not-exists"]/text()').extract_first() is None
True

A default return value can be provided as an argument, to be used instead of None:

>>> response.xpath('//div[@id="not-exists"]/text()').extract_first(default='not-found')
'not-found'

Notice that CSS selectors can select text or attribute nodes using CSS3 pseudo-elements:

>>> response.css('title::text').extract()
[u'Example website']

Now we’re going to get the base URL and some image links:

>>> response.xpath('//base/@href').extract()
[u'http://example.com/']

>>> response.css('base::attr(href)').extract()
[u'http://example.com/']

>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
[u'image1.html',
 u'image2.html',
 u'image3.html',
 u'image4.html',
 u'image5.html']

>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',
 u'image2.html',
 u'image3.html',
 u'image4.html',
 u'image5.html']

>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']

>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']
ネストセレクタ

The selection methods (.xpath() or .css()) return a list of selectors of the same type, so you can call the selection methods for those selectors too. Here’s an example:

>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.extract()
[u'<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>',
 u'<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>',
 u'<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>',
 u'<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>',
 u'<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']

>>> for index, link in enumerate(links):
...     args = (index, link.xpath('@href').extract(), link.xpath('img/@src').extract())
...     print 'Link number %d points to url %s and image %s' % args

Link number 0 points to url [u'image1.html'] and image [u'image1_thumb.jpg']
Link number 1 points to url [u'image2.html'] and image [u'image2_thumb.jpg']
Link number 2 points to url [u'image3.html'] and image [u'image3_thumb.jpg']
Link number 3 points to url [u'image4.html'] and image [u'image4_thumb.jpg']
Link number 4 points to url [u'image5.html'] and image [u'image5_thumb.jpg']
正規表現でのセレクタの使用

Selector also has a .re() method for extracting data using regular expressions. However, unlike using .xpath() or .css() methods, .re() returns a list of unicode strings. So you can’t construct nested .re() calls.

Here’s an example used to extract image names from the HTML code above:

>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
[u'My image 1',
 u'My image 2',
 u'My image 3',
 u'My image 4',
 u'My image 5']

There’s an additional helper reciprocating .extract_first() for .re(), named .re_first(). Use it to extract just the first matching string:

>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
u'My image 1'
相対XPathの操作

Keep in mind that if you are nesting selectors and use an XPath that starts with /, that XPath will be absolute to the document and not relative to the Selector you’re calling it from.

For example, suppose you want to extract all <p> elements inside <div> elements. First, you would get all <div> elements:

>>> divs = response.xpath('//div')

At first, you may be tempted to use the following approach, which is wrong, as it actually extracts all <p> elements from the document, not only those inside <div> elements:

>>> for p in divs.xpath('//p'):  # this is wrong - gets all <p> from the whole document
...     print p.extract()

This is the proper way to do it (note the dot prefixing the .//p XPath):

>>> for p in divs.xpath('.//p'):  # extracts all <p> inside
...     print p.extract()

Another common case would be to extract all direct <p> children:

>>> for p in divs.xpath('p'):
...     print p.extract()

For more details about relative XPaths see the Location Paths section in the XPath specification.

EXSLT拡張機能の使用

Being built atop lxml, Scrapy selectors also support some EXSLT extensions and come with these pre-registered namespaces to use in XPath expressions:

prefix namespace usage
re http://exslt.org/regular-expressions regular expressions
set http://exslt.org/sets set manipulation
正規表現

The test() function, for example, can prove quite useful when XPath’s starts-with() or contains() are not sufficient.

Example selecting links in list item with a “class” attribute ending with a digit:

>>> from scrapy import Selector
>>> doc = """
... <div>
...     <ul>
...         <li class="item-0"><a href="link1.html">first item</a></li>
...         <li class="item-1"><a href="link2.html">second item</a></li>
...         <li class="item-inactive"><a href="link3.html">third item</a></li>
...         <li class="item-1"><a href="link4.html">fourth item</a></li>
...         <li class="item-0"><a href="link5.html">fifth item</a></li>
...     </ul>
... </div>
... """
>>> sel = Selector(text=doc, type="html")
>>> sel.xpath('//li//@href').extract()
[u'link1.html', u'link2.html', u'link3.html', u'link4.html', u'link5.html']
>>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').extract()
[u'link1.html', u'link2.html', u'link4.html', u'link5.html']
>>>

警告

C library libxslt doesn’t natively support EXSLT regular expressions so lxml‘s implementation uses hooks to Python’s re module. Thus, using regexp functions in your XPath expressions may add a small performance penalty.

操作を設定する

These can be handy for excluding parts of a document tree before extracting text elements for example.

Example extracting microdata (sample content taken from http://schema.org/Product) with groups of itemscopes and corresponding itemprops:

>>> doc = """
... <div itemscope itemtype="http://schema.org/Product">
...   <span itemprop="name">Kenmore White 17" Microwave</span>
...   <img src="kenmore-microwave-17in.jpg" alt='Kenmore 17" Microwave' />
...   <div itemprop="aggregateRating"
...     itemscope itemtype="http://schema.org/AggregateRating">
...    Rated <span itemprop="ratingValue">3.5</span>/5
...    based on <span itemprop="reviewCount">11</span> customer reviews
...   </div>
...
...   <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
...     <span itemprop="price">$55.00</span>
...     <link itemprop="availability" href="http://schema.org/InStock" />In stock
...   </div>
...
...   Product description:
...   <span itemprop="description">0.7 cubic feet countertop microwave.
...   Has six preset cooking categories and convenience features like
...   Add-A-Minute and Child Lock.</span>
...
...   Customer reviews:
...
...   <div itemprop="review" itemscope itemtype="http://schema.org/Review">
...     <span itemprop="name">Not a happy camper</span> -
...     by <span itemprop="author">Ellie</span>,
...     <meta itemprop="datePublished" content="2011-04-01">April 1, 2011
...     <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
...       <meta itemprop="worstRating" content = "1">
...       <span itemprop="ratingValue">1</span>/
...       <span itemprop="bestRating">5</span>stars
...     </div>
...     <span itemprop="description">The lamp burned out and now I have to replace
...     it. </span>
...   </div>
...
...   <div itemprop="review" itemscope itemtype="http://schema.org/Review">
...     <span itemprop="name">Value purchase</span> -
...     by <span itemprop="author">Lucas</span>,
...     <meta itemprop="datePublished" content="2011-03-25">March 25, 2011
...     <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
...       <meta itemprop="worstRating" content = "1"/>
...       <span itemprop="ratingValue">4</span>/
...       <span itemprop="bestRating">5</span>stars
...     </div>
...     <span itemprop="description">Great microwave for the price. It is small and
...     fits in my apartment.</span>
...   </div>
...   ...
... </div>
... """
>>> sel = Selector(text=doc, type="html")
>>> for scope in sel.xpath('//div[@itemscope]'):
...     print "current scope:", scope.xpath('@itemtype').extract()
...     props = scope.xpath('''
...                 set:difference(./descendant::*/@itemprop,
...                                .//*[@itemscope]/*/@itemprop)''')
...     print "    properties:", props.extract()
...     print

current scope: [u'http://schema.org/Product']
    properties: [u'name', u'aggregateRating', u'offers', u'description', u'review', u'review']

current scope: [u'http://schema.org/AggregateRating']
    properties: [u'ratingValue', u'reviewCount']

current scope: [u'http://schema.org/Offer']
    properties: [u'price', u'availability']

current scope: [u'http://schema.org/Review']
    properties: [u'name', u'author', u'datePublished', u'reviewRating', u'description']

current scope: [u'http://schema.org/Rating']
    properties: [u'worstRating', u'ratingValue', u'bestRating']

current scope: [u'http://schema.org/Review']
    properties: [u'name', u'author', u'datePublished', u'reviewRating', u'description']

current scope: [u'http://schema.org/Rating']
    properties: [u'worstRating', u'ratingValue', u'bestRating']

>>>

Here we first iterate over itemscope elements, and for each one, we look for all itemprops elements and exclude those that are themselves inside another itemscope.

XPathのヒント

Here are some tips that you may find useful when using XPath with Scrapy selectors, based on this post from ScrapingHub’s blog. If you are not much familiar with XPath yet, you may want to take a look first at this XPath tutorial.

条件内のテキストノードの使用

When you need to use the text content as argument to an XPath string function, avoid using .//text() and use just . instead.

This is because the expression .//text() yields a collection of text elements – a node-set. And when a node-set is converted to a string, which happens when it is passed as argument to a string function like contains() or starts-with(), it results in the text for the first element only.

Example:

>>> from scrapy import Selector
>>> sel = Selector(text='<a href="#">Click here to go to the <strong>Next Page</strong></a>')

Converting a node-set to string:

>>> sel.xpath('//a//text()').extract() # take a peek at the node-set
[u'Click here to go to the ', u'Next Page']
>>> sel.xpath("string(//a[1]//text())").extract() # convert it to string
[u'Click here to go to the ']

A node converted to a string, however, puts together the text of itself plus of all its descendants:

>>> sel.xpath("//a[1]").extract() # select the first node
[u'<a href="#">Click here to go to the <strong>Next Page</strong></a>']
>>> sel.xpath("string(//a[1])").extract() # convert it to string
[u'Click here to go to the Next Page']

So, using the .//text() node-set won’t select anything in this case:

>>> sel.xpath("//a[contains(.//text(), 'Next Page')]").extract()
[]

But using the . to mean the node, works:

>>> sel.xpath("//a[contains(., 'Next Page')]").extract()
[u'<a href="#">Click here to go to the <strong>Next Page</strong></a>']
//node[1] と (//node)[1] の違いに注意してください

//node[1] selects all the nodes occurring first under their respective parents.

(//node)[1] selects all the nodes in the document, and then gets only the first of them.

Example:

>>> from scrapy import Selector
>>> sel = Selector(text="""
....:     <ul class="list">
....:         <li>1</li>
....:         <li>2</li>
....:         <li>3</li>
....:     </ul>
....:     <ul class="list">
....:         <li>4</li>
....:         <li>5</li>
....:         <li>6</li>
....:     </ul>""")
>>> xp = lambda x: sel.xpath(x).extract()

This gets all first <li> elements under whatever it is its parent:

>>> xp("//li[1]")
[u'<li>1</li>', u'<li>4</li>']

And this gets the first <li> element in the whole document:

>>> xp("(//li)[1]")
[u'<li>1</li>']

This gets all first <li> elements under an <ul> parent:

>>> xp("//ul/li[1]")
[u'<li>1</li>', u'<li>4</li>']

And this gets the first <li> element under an <ul> parent in the whole document:

>>> xp("(//ul/li)[1]")
[u'<li>1</li>']
クラス別にクエリを実行する場合は, CSSを使用することを検討してください

Because an element can contain multiple CSS classes, the XPath way to select elements by class is the rather verbose:

*[contains(concat(' ', normalize-space(@class), ' '), ' someclass ')]

If you use @class='someclass' you may end up missing elements that have other classes, and if you just use contains(@class, 'someclass') to make up for that you may end up with more elements that you want, if they have a different class name that shares the string someclass.

As it turns out, Scrapy selectors allow you to chain selectors, so most of the time you can just select by class using CSS and then switch to XPath when needed:

>>> from scrapy import Selector
>>> sel = Selector(text='<div class="hero shout"><time datetime="2014-07-23 19:00">Special date</time></div>')
>>> sel.css('.shout').xpath('./time/@datetime').extract()
[u'2014-07-23 19:00']

This is cleaner than using the verbose XPath trick shown above. Just remember to use the . in the XPath expressions that will follow.

ビルトインセレクタリファレンス

class scrapy.selector.Selector(response=None, text=None, type=None)

An instance of Selector is a wrapper over response to select certain parts of its content.

response is an HtmlResponse or an XmlResponse object that will be used for selecting and extracting data.

text is a unicode string or utf-8 encoded text for cases when a response isn’t available. Using text and response together is undefined behavior.

type defines the selector type, it can be "html", "xml" or None (default).

If type is None, the selector automatically chooses the best type based on response type (see below), or defaults to "html" in case it is used together with text.

If type is None and a response is passed, the selector type is inferred from the response type as follows:

Otherwise, if type is set, the selector type will be forced and no detection will occur.

xpath(query)

Find nodes matching the xpath query and return the result as a SelectorList instance with all elements flattened. List elements implement Selector interface too.

query is a string containing the XPATH query to apply.

注釈

For convenience, this method can be called as response.xpath()

css(query)

Apply the given CSS selector and return a SelectorList instance.

query is a string containing the CSS selector to apply.

In the background, CSS queries are translated into XPath queries using cssselect library and run .xpath() method.

注釈

For convenience this method can be called as response.css()

extract()

Serialize and return the matched nodes as a list of unicode strings. Percent encoded content is unquoted.

re(regex)

Apply the given regex and return a list of unicode strings with the matches.

regex can be either a compiled regular expression or a string which will be compiled to a regular expression using re.compile(regex)

register_namespace(prefix, uri)

Register the given namespace to be used in this Selector. Without registering namespaces you can’t select or extract data from non-standard namespaces. See examples below.

remove_namespaces()

Remove all namespaces, allowing to traverse the document using namespace-less xpaths. See example below.

__nonzero__()

Returns True if there is any real content selected or False otherwise. In other words, the boolean value of a Selector is given by the contents it selects.

SelectorList オブジェクト
class scrapy.selector.SelectorList

The SelectorList class is a subclass of the builtin list class, which provides a few additional methods.

xpath(query)

Call the .xpath() method for each element in this list and return their results flattened as another SelectorList.

query is the same argument as the one in Selector.xpath()

css(query)

Call the .css() method for each element in this list and return their results flattened as another SelectorList.

query is the same argument as the one in Selector.css()

extract()

Call the .extract() method for each element in this list and return their results flattened, as a list of unicode strings.

re()

Call the .re() method for each element in this list and return their results flattened, as a list of unicode strings.

__nonzero__()

returns True if the list is not empty, False otherwise.

HTMLレスポンスのセレクタの例

Here’s a couple of Selector examples to illustrate several concepts. In all cases, we assume there is already a Selector instantiated with a HtmlResponse object like this:

sel = Selector(html_response)
  1. Select all <h1> elements from an HTML response body, returning a list of Selector objects (ie. a SelectorList object):

    sel.xpath("//h1")
    
  2. Extract the text of all <h1> elements from an HTML response body, returning a list of unicode strings:

    sel.xpath("//h1").extract()         # this includes the h1 tag
    sel.xpath("//h1/text()").extract()  # this excludes the h1 tag
    
  3. Iterate over all <p> tags and print their class attribute:

    for node in sel.xpath("//p"):
        print node.xpath("@class").extract()
    
XMLレスポンスのセレクタの例

Here’s a couple of examples to illustrate several concepts. In both cases we assume there is already a Selector instantiated with an XmlResponse object like this:

sel = Selector(xml_response)
  1. Select all <product> elements from an XML response body, returning a list of Selector objects (ie. a SelectorList object):

    sel.xpath("//product")
    
  2. Extract all prices from a Google Base XML feed which requires registering a namespace:

    sel.register_namespace("g", "http://base.google.com/ns/1.0")
    sel.xpath("//g:price").extract()
    
ネームスペースの削除

When dealing with scraping projects, it is often quite convenient to get rid of namespaces altogether and just work with element names, to write more simple/convenient XPaths. You can use the Selector.remove_namespaces() method for that.

Let’s show an example that illustrates this with GitHub blog atom feed.

First, we open the shell with the url we want to scrape:

$ scrapy shell https://github.com/blog.atom

Once in the shell we can try selecting all <link> objects and see that it doesn’t work (because the Atom XML namespace is obfuscating those nodes):

>>> response.xpath("//link")
[]

But once we call the Selector.remove_namespaces() method, all nodes can be accessed directly by their names:

>>> response.selector.remove_namespaces()
>>> response.xpath("//link")
[<Selector xpath='//link' data=u'<link xmlns="http://www.w3.org/2005/Atom'>,
 <Selector xpath='//link' data=u'<link xmlns="http://www.w3.org/2005/Atom'>,
 ...

If you wonder why the namespace removal procedure isn’t always called by default instead of having to call it manually, this is because of two reasons, which, in order of relevance, are:

  1. Removing namespaces requires to iterate and modify all nodes in the document, which is a reasonably expensive operation to perform for all documents crawled by Scrapy
  2. There could be some cases where using namespaces is actually required, in case some element names clash between namespaces. These cases are very rare though.

アイテム

スクレイピングの主な目的は, 構造化されていないソース(通常はWebページ)から構造化データを抽出することです. Scrapy スパイダーは Python の dicts として抽出されたデータを返すことができます. Python の dicts は便利で親しみがありますが, 構造が欠けています. 特に, 多くのスパイダーを持つ大規模なプロジェクトでは, フィールド名の入力ミスを起こしたり, 矛盾したデータを返すことがよく起こります.

一般的な出力データフォーマットを定義するために, Scrapyは Item クラスを提供しています. Item オブジェクトは, スクラップされたデータを収集するために使用される単純なコンテナです. 利用可能なフィールドを宣言するのに便利な構文を備えた dictionary-like なAPIを提供します.

Various Scrapy components use extra information provided by Items: exporters look at declared fields to figure out columns to export, serialization can be customized using Item fields metadata, trackref tracks Item instances to help finding memory leaks (see trackref でメモリリークのデバッグをする), etc.

宣言項目

Items are declared using a simple class definition syntax and Field objects. Here is an example:

import scrapy

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)

注釈

Those familiar with Django will notice that Scrapy Items are declared similar to Django Models, except that Scrapy Items are much simpler as there is no concept of different field types.

アイテムフィールド

Field objects are used to specify metadata for each field. For example, the serializer function for the last_updated field illustrated in the example above.

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 component, 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.

アイテムの操作

Here are some examples of common tasks performed with items, using the Product item declared above. You will notice the API is very similar to the dict API.

アイテムの作成
>>> product = Product(name='Desktop PC', price=1000)
>>> print product
Product(name='Desktop PC', price=1000)
フィールド値の取得
>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC

>>> product['price']
1000

>>> product['last_updated']
Traceback (most recent call last):
    ...
KeyError: 'last_updated'

>>> product.get('last_updated', 'not set')
not set

>>> product['lala'] # getting unknown field
Traceback (most recent call last):
    ...
KeyError: 'lala'

>>> product.get('lala', 'unknown field')
'unknown field'

>>> 'name' in product  # is name field populated?
True

>>> 'last_updated' in product  # is last_updated populated?
False

>>> 'last_updated' in product.fields  # is last_updated a declared field?
True

>>> 'lala' in product.fields  # is lala a declared field?
False
フィールド値の設定
>>> product['last_updated'] = 'today'
>>> product['last_updated']
today

>>> product['lala'] = 'test' # setting unknown field
Traceback (most recent call last):
    ...
KeyError: 'Product does not support field: lala'
入力されたすべての値にアクセスする

入力された全てにアゥセスする, 標準的な dict API:

>>> product.keys()
['price', 'name']

>>> product.items()
[('price', 1000), ('name', 'Desktop PC')]
その他の一般的なタスク

items をコピーする:

>>> product2 = Product(product)
>>> print product2
Product(name='Desktop PC', price=1000)

>>> product3 = product2.copy()
>>> print product3
Product(name='Desktop PC', price=1000)

items から dicts を作成する:

>>> dict(product) # create a dict from all populated values
{'price': 1000, 'name': 'Desktop PC'}

dicts から items を作成する:

>>> Product({'name': 'Laptop PC', 'price': 1500})
Product(price=1500, name='Laptop PC')

>>> Product({'name': 'Laptop PC', 'lala': 1500}) # warning: unknown field in dict
Traceback (most recent call last):
    ...
KeyError: 'Product does not support field: lala'

アイテムの拡張

You can extend Items (to add more fields or to change some metadata for some fields) by declaring a subclass of your original Item.

For example:

class DiscountedProduct(Product):
    discount_percent = scrapy.Field(serializer=str)
    discount_expiration_date = scrapy.Field()

You can also extend field metadata by using the previous field metadata and appending more values, or changing existing values, like this:

class SpecificProduct(Product):
    name = scrapy.Field(Product.fields['name'], serializer=my_serializer)

That adds (or replaces) the serializer metadata key for the name field, keeping all the previously existing metadata values.

Itemオブジェクト

class scrapy.item.Item([arg])

Return a new Item optionally initialized from the given argument.

Items replicate the standard dict API, including its constructor. The only additional attribute provided by Items is:

fields

A dictionary containing all declared fields for this Item, not only those populated. The keys are the field names and the values are the Field objects used in the Item declaration.

Fieldオブジェクト

class scrapy.item.Field([arg])

The Field class is just an alias to the built-in dict class and doesn’t provide any extra functionality or attributes. In other words, Field objects are plain-old Python dicts. A separate class is used to support the item declaration syntax based on class attributes.

アイテムローダー

アイテムローダーは, 集めてきた アイテム を投入するための便利なメカニズムを提供します. Items は独自の辞書型APIを使用して作成できますが, アイテムローダーは, スクレイピング処理からデータを取り込むためのより便利なAPIを提供しています.

言い換えれば, アイテム スクレイピングされたデータの コンテナ を提供し, アイテムローダーはそのコンテナを投入するためのメカニズムを提供します.

アイテムローダーは, スパイダー, またはソースフォーマット(HTML, XMLなど)のいずれかで, 維持する悪夢にうなされずに, さまざまなフィールド解析ルールを拡張および上書きするための 柔軟で効率的で簡単なメカニズムを提供するように設計されています.

アイテムローダーを使用したアイテムの設定

To use an Item Loader, you must first instantiate it. You can either instantiate it with a dict-like object (e.g. Item or dict) or without one, in which case an Item is automatically instantiated in the Item Loader constructor using the Item class specified in the ItemLoader.default_item_class attribute.

Then, you start collecting values into the Item Loader, typically using Selectors. You can add more than one value to the same item field; the Item Loader will know how to “join” those values later using a proper processing function.

Here is a typical Item Loader usage in a Spider, using the Product item declared in the Items chapter:

from scrapy.loader import ItemLoader
from myproject.items import Product

def parse(self, response):
    l = ItemLoader(item=Product(), response=response)
    l.add_xpath('name', '//div[@class="product_name"]')
    l.add_xpath('name', '//div[@class="product_title"]')
    l.add_xpath('price', '//p[@id="price"]')
    l.add_css('stock', 'p#stock]')
    l.add_value('last_updated', 'today') # you can also use literal values
    return l.load_item()

By quickly looking at that code, we can see the name field is being extracted from two different XPath locations in the page:

  1. //div[@class="product_name"]
  2. //div[@class="product_title"]

In other words, data is being collected by extracting it from two XPath locations, using the add_xpath() method. This is the data that will be assigned to the name field later.

Afterwards, similar calls are used for price and stock fields (the latter using a CSS selector with the add_css() method), and finally the last_update field is populated directly with a literal value (today) using a different method: add_value().

Finally, when all data is collected, the ItemLoader.load_item() method is called which actually returns the item populated with the data previously extracted and collected with the add_xpath(), add_css(), and add_value() calls.

入力および出力プロセッサ

An Item Loader contains one input processor and one output processor for each (item) field. The input processor processes the extracted data as soon as it’s received (through the add_xpath(), add_css() or add_value() methods) and the result of the input processor is collected and kept inside the ItemLoader. After collecting all data, the ItemLoader.load_item() method is called to populate and get the populated Item object. That’s when the output processor is called with the data previously collected (and processed using the input processor). The result of the output processor is the final value that gets assigned to the item.

Let’s see an example to illustrate how the input and output processors are called for a particular field (the same applies for any other field):

l = ItemLoader(Product(), some_selector)
l.add_xpath('name', xpath1) # (1)
l.add_xpath('name', xpath2) # (2)
l.add_css('name', css) # (3)
l.add_value('name', 'test') # (4)
return l.load_item() # (5)

So what happens is:

  1. Data from xpath1 is extracted, and passed through the input processor of the name field. The result of the input processor is collected and kept in the Item Loader (but not yet assigned to the item).
  2. Data from xpath2 is extracted, and passed through the same input processor used in (1). The result of the input processor is appended to the data collected in (1) (if any).
  3. This case is similar to the previous ones, except that the data is extracted from the css CSS selector, and passed through the same input processor used in (1) and (2). The result of the input processor is appended to the data collected in (1) and (2) (if any).
  4. This case is also similar to the previous ones, except that the value to be collected is assigned directly, instead of being extracted from a XPath expression or a CSS selector. However, the value is still passed through the input processors. In this case, since the value is not iterable it is converted to an iterable of a single element before passing it to the input processor, because input processor always receive iterables.
  5. The data collected in steps (1), (2), (3) and (4) is passed through the output processor of the name field. The result of the output processor is the value assigned to the name field in the item.

It’s worth noticing that processors are just callable objects, which are called with the data to be parsed, and return a parsed value. So you can use any function as input or output processor. The only requirement is that they must accept one (and only one) positional argument, which will be an iterator.

注釈

Both input and output processors must receive an iterator as their first argument. The output of those functions can be anything. The result of input processors will be appended to an internal list (in the Loader) containing the collected values (for that field). The result of the output processors is the value that will be finally assigned to the item.

The other thing you need to keep in mind is that the values returned by input processors are collected internally (in lists) and then passed to output processors to populate the fields.

Last, but not least, Scrapy comes with some commonly used processors built-in for convenience.

アイテムローダーの宣言

Item Loaders are declared like Items, by using a class definition syntax. Here is an example:

from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join

class ProductLoader(ItemLoader):

    default_output_processor = TakeFirst()

    name_in = MapCompose(unicode.title)
    name_out = Join()

    price_in = MapCompose(unicode.strip)

    # ...

As you can see, input processors are declared using the _in suffix while output processors are declared using the _out suffix. And you can also declare a default input/output processors using the ItemLoader.default_input_processor and ItemLoader.default_output_processor attributes.

入力および出力プロセッサの宣言

As seen in the previous section, input and output processors can be declared in the Item Loader definition, and it’s very common to declare input processors this way. However, there is one more place where you can specify the input and output processors to use: in the Item Field metadata. Here is an example:

import scrapy
from scrapy.loader.processors import Join, MapCompose, TakeFirst
from w3lib.html import remove_tags

def filter_price(value):
    if value.isdigit():
        return value

class Product(scrapy.Item):
    name = scrapy.Field(
        input_processor=MapCompose(remove_tags),
        output_processor=Join(),
    )
    price = scrapy.Field(
        input_processor=MapCompose(remove_tags, filter_price),
        output_processor=TakeFirst(),
    )
>>> from scrapy.loader import ItemLoader
>>> il = ItemLoader(item=Product())
>>> il.add_value('name', [u'Welcome to my', u'<strong>website</strong>'])
>>> il.add_value('price', [u'&euro;', u'<span>1000</span>'])
>>> il.load_item()
{'name': u'Welcome to my website', 'price': u'1000'}

The precedence order, for both input and output processors, is as follows:

  1. Item Loader field-specific attributes: field_in and field_out (most precedence)
  2. Field metadata (input_processor and output_processor key)
  3. Item Loader defaults: ItemLoader.default_input_processor() and ItemLoader.default_output_processor() (least precedence)

アイテムローダの再利用と拡張 を参照してください.

アイテムローダーコンテキスト

The Item Loader Context is a dict of arbitrary key/values which is shared among all input and output processors in the Item Loader. It can be passed when declaring, instantiating or using Item Loader. They are used to modify the behaviour of the input/output processors.

For example, suppose you have a function parse_length which receives a text value and extracts a length from it:

def parse_length(text, loader_context):
    unit = loader_context.get('unit', 'm')
    # ... length parsing code goes here ...
    return parsed_length

By accepting a loader_context argument the function is explicitly telling the Item Loader that it’s able to receive an Item Loader context, so the Item Loader passes the currently active context when calling it, and the processor function (parse_length in this case) can thus use them.

There are several ways to modify Item Loader context values:

  1. By modifying the currently active Item Loader context (context attribute):

    loader = ItemLoader(product)
    loader.context['unit'] = 'cm'
    
  2. On Item Loader instantiation (the keyword arguments of Item Loader constructor are stored in the Item Loader context):

    loader = ItemLoader(product, unit='cm')
    
  3. On Item Loader declaration, for those input/output processors that support instantiating them with an Item Loader context. MapCompose is one of them:

    class ProductLoader(ItemLoader):
        length_out = MapCompose(parse_length, unit='cm')
    

ItemLoader オブジェクト

class scrapy.loader.ItemLoader([item, selector, response, ]**kwargs)

Return a new Item Loader for populating the given Item. If no item is given, one is instantiated automatically using the class in default_item_class.

When instantiated with a selector or a response parameters the ItemLoader class provides convenient mechanisms for extracting data from web pages using selectors.

パラメータ:

The item, selector, response and the remaining keyword arguments are assigned to the Loader context (accessible through the context attribute).

ItemLoader instances have the following methods:

get_value(value, *processors, **kwargs)

Process the given value by the given processors and keyword arguments.

Available keyword arguments:

パラメータ:re (str or compiled regex) – a regular expression to use for extracting data from the given value using extract_regex() method, applied before processors

例:

>>> from scrapy.loader.processors import TakeFirst
>>> loader.get_value(u'name: foo', TakeFirst(), unicode.upper, re='name: (.+)')
'FOO`
add_value(field_name, value, *processors, **kwargs)

Process and then add the given value for the given field.

The value is first passed through get_value() by giving the processors and kwargs, and then passed through the field input processor and its result appended to the data collected for that field. If the field already contains collected data, the new data is added.

The given field_name can be None, in which case values for multiple fields may be added. And the processed value should be a dict with field_name mapped to values.

Examples:

loader.add_value('name', u'Color TV')
loader.add_value('colours', [u'white', u'blue'])
loader.add_value('length', u'100')
loader.add_value('name', u'name: foo', TakeFirst(), re='name: (.+)')
loader.add_value(None, {'name': u'foo', 'sex': u'male'})
replace_value(field_name, value, *processors, **kwargs)

Similar to add_value() but replaces the collected data with the new value instead of adding it.

get_xpath(xpath, *processors, **kwargs)

Similar to ItemLoader.get_value() but receives an XPath instead of a value, which is used to extract a list of unicode strings from the selector associated with this ItemLoader.

パラメータ:
  • xpath (str) – the XPath to extract data from
  • re (str or compiled regex) – a regular expression to use for extracting data from the selected XPath region

例:

# HTML snippet: <p class="product-name">Color TV</p>
loader.get_xpath('//p[@class="product-name"]')
# HTML snippet: <p id="price">the price is $1200</p>
loader.get_xpath('//p[@id="price"]', TakeFirst(), re='the price is (.*)')
add_xpath(field_name, xpath, *processors, **kwargs)

Similar to ItemLoader.add_value() but receives an XPath instead of a value, which is used to extract a list of unicode strings from the selector associated with this ItemLoader.

See get_xpath() for kwargs.

パラメータ:xpath (str) – the XPath to extract data from

例:

# HTML snippet: <p class="product-name">Color TV</p>
loader.add_xpath('name', '//p[@class="product-name"]')
# HTML snippet: <p id="price">the price is $1200</p>
loader.add_xpath('price', '//p[@id="price"]', re='the price is (.*)')
replace_xpath(field_name, xpath, *processors, **kwargs)

Similar to add_xpath() but replaces collected data instead of adding it.

get_css(css, *processors, **kwargs)

Similar to ItemLoader.get_value() but receives a CSS selector instead of a value, which is used to extract a list of unicode strings from the selector associated with this ItemLoader.

パラメータ:
  • css (str) – the CSS selector to extract data from
  • re (str or compiled regex) – a regular expression to use for extracting data from the selected CSS region

例:

# HTML snippet: <p class="product-name">Color TV</p>
loader.get_css('p.product-name')
# HTML snippet: <p id="price">the price is $1200</p>
loader.get_css('p#price', TakeFirst(), re='the price is (.*)')
add_css(field_name, css, *processors, **kwargs)

Similar to ItemLoader.add_value() but receives a CSS selector instead of a value, which is used to extract a list of unicode strings from the selector associated with this ItemLoader.

See get_css() for kwargs.

パラメータ:css (str) – the CSS selector to extract data from

例:

# HTML snippet: <p class="product-name">Color TV</p>
loader.add_css('name', 'p.product-name')
# HTML snippet: <p id="price">the price is $1200</p>
loader.add_css('price', 'p#price', re='the price is (.*)')
replace_css(field_name, css, *processors, **kwargs)

Similar to add_css() but replaces collected data instead of adding it.

load_item()

Populate the item with the data collected so far, and return it. The data collected is first passed through the output processors to get the final value to assign to each item field.

nested_xpath(xpath)

Create a nested loader with an xpath selector. The supplied selector is applied relative to selector associated with this ItemLoader. The nested loader shares the Item with the parent ItemLoader so calls to add_xpath(), add_value(), replace_value(), etc. will behave as expected.

nested_css(css)

Create a nested loader with a css selector. The supplied selector is applied relative to selector associated with this ItemLoader. The nested loader shares the Item with the parent ItemLoader so calls to add_xpath(), add_value(), replace_value(), etc. will behave as expected.

get_collected_values(field_name)

Return the collected values for the given field.

get_output_value(field_name)

Return the collected values parsed using the output processor, for the given field. This method doesn’t populate or modify the item at all.

get_input_processor(field_name)

指定されたフィールドの入力プロセッサーを返します.

get_output_processor(field_name)

Return the output processor for the given field.

ItemLoader instances have the following attributes:

item

The Item object being parsed by this Item Loader.

context

The currently active Context of this Item Loader.

default_item_class

An Item class (or factory), used to instantiate items when not given in the constructor.

default_input_processor

指定されていないフィールドに使用するデフォルトの入力プロセッサー.

default_output_processor

特に指定のないフィールドに使用するデフォルト出力プロセッサ.

default_selector_class

The class used to construct the selector of this ItemLoader, if only a response is given in the constructor. If a selector is given in the constructor this attribute is ignored. This attribute is sometimes overridden in subclasses.

selector

The Selector object to extract data from. It’s either the selector given in the constructor or one created from the response given in the constructor using the default_selector_class. This attribute is meant to be read-only.

ネストされたローダー

When parsing related values from a subsection of a document, it can be useful to create nested loaders. Imagine you’re extracting details from a footer of a page that looks something like:

Example:

<footer>
    <a class="social" href="http://facebook.com/whatever">Like Us</a>
    <a class="social" href="http://twitter.com/whatever">Follow Us</a>
    <a class="email" href="mailto:whatever@example.com">Email Us</a>
</footer>

Without nested loaders, you need to specify the full xpath (or css) for each value that you wish to extract.

Example:

loader = ItemLoader(item=Item())
# load stuff not in the footer
loader.add_xpath('social', '//footer/a[@class = "social"]/@href')
loader.add_xpath('email', '//footer/a[@class = "email"]/@href')
loader.load_item()

Instead, you can create a nested loader with the footer selector and add values relative to the footer. The functionality is the same but you avoid repeating the footer selector.

Example:

loader = ItemLoader(item=Item())
# load stuff not in the footer
footer_loader = loader.nested_xpath('//footer')
footer_loader.add_xpath('social', 'a[@class = "social"]/@href')
footer_loader.add_xpath('email', 'a[@class = "email"]/@href')
# footer_loader.load_item() を実行する必要はない
loader.load_item()

You can nest loaders arbitrarily and they work with either xpath or css selectors. As a general guideline, use nested loaders when they make your code simpler but do not go overboard with nesting or your parser can become difficult to read.

アイテムローダの再利用と拡張

As your project grows bigger and acquires more and more spiders, maintenance becomes a fundamental problem, especially when you have to deal with many different parsing rules for each spider, having a lot of exceptions, but also wanting to reuse the common processors.

Item Loaders are designed to ease the maintenance burden of parsing rules, without losing flexibility and, at the same time, providing a convenient mechanism for extending and overriding them. For this reason Item Loaders support traditional Python class inheritance for dealing with differences of specific spiders (or groups of spiders).

Suppose, for example, that some particular site encloses their product names in three dashes (e.g. ---Plasma TV---) and you don’t want to end up scraping those dashes in the final product names.

Here’s how you can remove those dashes by reusing and extending the default Product Item Loader (ProductLoader):

from scrapy.loader.processors import MapCompose
from myproject.ItemLoaders import ProductLoader

def strip_dashes(x):
    return x.strip('-')

class SiteSpecificLoader(ProductLoader):
    name_in = MapCompose(strip_dashes, ProductLoader.name_in)

Another case where extending Item Loaders can be very helpful is when you have multiple source formats, for example XML and HTML. In the XML version you may want to remove CDATA occurrences. Here’s an example of how to do it:

from scrapy.loader.processors import MapCompose
from myproject.ItemLoaders import ProductLoader
from myproject.utils.xml import remove_cdata

class XmlProductLoader(ProductLoader):
    name_in = MapCompose(remove_cdata, ProductLoader.name_in)

そしてそれは典型的な入力プロセッサを拡張する方法です.

As for output processors, it is more common to declare them in the field metadata, as they usually depend only on the field and not on each specific site parsing rule (as input processors do). See also: 入力および出力プロセッサの宣言.

There are many other possible ways to extend, inherit and override your Item Loaders, and different Item Loaders hierarchies may fit better for different projects. Scrapy only provides the mechanism; it doesn’t impose any specific organization of your Loaders collection - that’s up to you and your project’s needs.

使用可能な内蔵プロセッサ

Even though you can use any callable function as input and output processors, Scrapy provides some commonly used processors, which are described below. Some of them, like the MapCompose (which is typically used as input processor) compose the output of several functions executed in order, to produce the final parsed value.

Here is a list of all built-in processors:

class scrapy.loader.processors.Identity

The simplest processor, which doesn’t do anything. It returns the original values unchanged. It doesn’t receive any constructor arguments, nor does it accept Loader contexts.

Example:

>>> from scrapy.loader.processors import Identity
>>> proc = Identity()
>>> proc(['one', 'two', 'three'])
['one', 'two', 'three']
class scrapy.loader.processors.TakeFirst

Returns the first non-null/non-empty value from the values received, so it’s typically used as an output processor to single-valued fields. It doesn’t receive any constructor arguments, nor does it accept Loader contexts.

Example:

>>> from scrapy.loader.processors import TakeFirst
>>> proc = TakeFirst()
>>> proc(['', 'one', 'two', 'three'])
'one'
class scrapy.loader.processors.Join(separator=u' ')

Returns the values joined with the separator given in the constructor, which defaults to u' '. It doesn’t accept Loader contexts.

When using the default separator, this processor is equivalent to the function: u' '.join

Examples:

>>> from scrapy.loader.processors import Join
>>> proc = Join()
>>> proc(['one', 'two', 'three'])
u'one two three'
>>> proc = Join('<br>')
>>> proc(['one', 'two', 'three'])
u'one<br>two<br>three'
class scrapy.loader.processors.Compose(*functions, **default_loader_context)

A processor which is constructed from the composition of the given functions. This means that each input value of this processor is passed to the first function, and the result of that function is passed to the second function, and so on, until the last function returns the output value of this processor.

By default, stop process on None value. This behaviour can be changed by passing keyword argument stop_on_none=False.

Example:

>>> from scrapy.loader.processors import Compose
>>> proc = Compose(lambda v: v[0], str.upper)
>>> proc(['hello', 'world'])
'HELLO'

Each function can optionally receive a loader_context parameter. For those which do, this processor will pass the currently active Loader context through that parameter.

The keyword arguments passed in the constructor are used as the default Loader context values passed to each function call. However, the final Loader context values passed to functions are overridden with the currently active Loader context accessible through the ItemLoader.context() attribute.

class scrapy.loader.processors.MapCompose(*functions, **default_loader_context)

A processor which is constructed from the composition of the given functions, similar to the Compose processor. The difference with this processor is the way internal results are passed among functions, which is as follows:

The input value of this processor is iterated and the first function is applied to each element. The results of these function calls (one for each element) are concatenated to construct a new iterable, which is then used to apply the second function, and so on, until the last function is applied to each value of the list of values collected so far. The output values of the last function are concatenated together to produce the output of this processor.

Each particular function can return a value or a list of values, which is flattened with the list of values returned by the same function applied to the other input values. The functions can also return None in which case the output of that function is ignored for further processing over the chain.

This processor provides a convenient way to compose functions that only work with single values (instead of iterables). For this reason the MapCompose processor is typically used as input processor, since data is often extracted using the extract() method of selectors, which returns a list of unicode strings.

The example below should clarify how it works:

>>> def filter_world(x):
...     return None if x == 'world' else x
...
>>> from scrapy.loader.processors import MapCompose
>>> proc = MapCompose(filter_world, unicode.upper)
>>> proc([u'hello', u'world', u'this', u'is', u'scrapy'])
[u'HELLO, u'THIS', u'IS', u'SCRAPY']

As with the Compose processor, functions can receive Loader contexts, and constructor keyword arguments are used as default context values. See Compose processor for more info.

class scrapy.loader.processors.SelectJmes(json_path)

Queries the value using the json path provided to the constructor and returns the output. Requires jmespath (https://github.com/jmespath/jmespath.py) to run. This processor takes only one input at a time.

例:

>>> from scrapy.loader.processors import SelectJmes, Compose, MapCompose
>>> proc = SelectJmes("foo") #for direct use on lists and dictionaries
>>> proc({'foo': 'bar'})
'bar'
>>> proc({'foo': {'bar': 'baz'}})
{'bar': 'baz'}

Json の中身を見る:

>>> import json
>>> proc_single_json_str = Compose(json.loads, SelectJmes("foo"))
>>> proc_single_json_str('{"foo": "bar"}')
u'bar'
>>> proc_json_list = Compose(json.loads, MapCompose(SelectJmes('foo')))
>>> proc_json_list('[{"foo":"bar"}, {"baz":"tar"}]')
[u'bar']

Scrapy シェル

The Scrapy shell is an interactive shell where you can try and debug your scraping code very quickly, without having to run the spider. It’s meant to be used for testing data extraction code, but you can actually use it for testing any kind of code as it is also a regular Python shell.

The shell is used for testing XPath or CSS expressions and see how they work and what data they extract from the web pages you’re trying to scrape. It allows you to interactively test your expressions while you’re writing your spider, without having to run the spider to test every change.

Once you get familiarized with the Scrapy shell, you’ll see that it’s an invaluable tool for developing and debugging your spiders.

シェルの設定

If you have IPython installed, the Scrapy shell will use it (instead of the standard Python console). The IPython console is much more powerful and provides smart auto-completion and colorized output, among other things.

We highly recommend you install IPython, specially if you’re working on Unix systems (where IPython excels). See the IPython installation guide for more info.

Scrapy also has support for bpython, and will try to use it where IPython is unavailable.

Through scrapy’s settings you can configure it to use any one of ipython, bpython or the standard python shell, regardless of which are installed. This is done by setting the SCRAPY_PYTHON_SHELL environment variable; or by defining it in your scrapy.cfg:

[settings]
shell = bpython

シェルの実行

To launch the Scrapy shell you can use the shell command like this:

scrapy shell <url>

Where the <url> is the URL you want to scrape.

shell also works for local files. This can be handy if you want to play around with a local copy of a web page. shell understands the following syntaxes for local files:

# UNIX-style
scrapy shell ./path/to/file.html
scrapy shell ../other/path/to/file.html
scrapy shell /absolute/path/to/file.html

# File URI
scrapy shell file:///absolute/path/to/file.html

注釈

When using relative file paths, be explicit and prepend them with ./ (or ../ when relevant). scrapy shell index.html will not work as one might expect (and this is by design, not a bug).

Because shell favors HTTP URLs over File URIs, and index.html being syntactically similar to example.com, shell will treat index.html as a domain name and trigger a DNS lookup error:

$ scrapy shell index.html
[ ... scrapy shell starts ... ]
[ ... traceback ... ]
twisted.internet.error.DNSLookupError: DNS lookup failed:
address 'index.html' not found: [Errno -5] No address associated with hostname.

shell will not test beforehand if a file called index.html exists in the current directory. Again, be explicit.

シェルの使用

The Scrapy shell is just a regular Python console (or IPython console if you have it available) which provides some additional shortcut functions for convenience.

使用可能なショートカット
  • shelp() - print a help with the list of available objects and shortcuts
  • fetch(request_or_url) - fetch a new response from the given request or URL and update all related objects accordingly.
  • view(response) - open the given response in your local web browser, for inspection. This will add a <base> tag to the response body in order for external links (such as images and style sheets) to display properly. Note, however, that this will create a temporary file in your computer, which won’t be removed automatically.
使用可能な Scrapy オブジェクト

The Scrapy shell automatically creates some convenient objects from the downloaded page, like the Response object and the Selector objects (for both HTML and XML content).

Those objects are:

  • crawler - the current Crawler object.
  • spider - the Spider which is known to handle the URL, or a Spider object if there is no spider found for the current URL
  • request - a Request object of the last fetched page. You can modify this request using replace() or fetch a new request (without leaving the shell) using the fetch shortcut.
  • response - a Response object containing the last fetched page
  • settings - the current Scrapy settings

シェルセッションの例

Here’s an example of a typical shell session where we start by scraping the http://scrapy.org page, and then proceed to scrape the https://reddit.com page. Finally, we modify the (Reddit) request method to POST and re-fetch it getting an error. We end the session by typing Ctrl-D (in Unix systems) or Ctrl-Z in Windows.

Keep in mind that the data extracted here may not be the same when you try it, as those pages are not static and could have changed by the time you test this. The only purpose of this example is to get you familiarized with how the Scrapy shell works.

First, we launch the shell:

scrapy shell 'http://scrapy.org' --nolog

Then, the shell fetches the URL (using the Scrapy downloader) and prints the list of available objects and useful shortcuts (you’ll notice that these lines all start with the [s] prefix):

[s] Available Scrapy objects:
[s]   crawler    <scrapy.crawler.Crawler object at 0x1e16b50>
[s]   item       {}
[s]   request    <GET http://scrapy.org>
[s]   response   <200 http://scrapy.org>
[s]   settings   <scrapy.settings.Settings object at 0x2bfd650>
[s]   spider     <Spider 'default' at 0x20c6f50>
[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

>>>

After that, we can start playing with the objects:

>>> response.xpath('//title/text()').extract_first()
u'Scrapy | A Fast and Powerful Scraping and Web Crawling Framework'

>>> fetch("http://reddit.com")
[s] Available Scrapy objects:
[s]   crawler    <scrapy.crawler.Crawler object at 0x7fb3ed9c9c90>
[s]   item       {}
[s]   request    <GET http://reddit.com>
[s]   response   <200 https://www.reddit.com/>
[s]   settings   <scrapy.settings.Settings object at 0x7fb3ed9c9c10>
[s]   spider     <DefaultSpider 'default' at 0x7fb3ecdd3390>
[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.xpath('//title/text()').extract()
[u'reddit: the front page of the internet']

>>> request = request.replace(method="POST")

>>> fetch(request)
[s] Available Scrapy objects:
[s]   crawler    <scrapy.crawler.Crawler object at 0x1e16b50>
...

>>>

応答を調べるためにシェルを起動する

Sometimes you want to inspect the responses that are being processed in a certain point of your spider, if only to check that response you expect is getting there.

This can be achieved by using the scrapy.shell.inspect_response function.

Here’s an example of how you would call it from your spider:

import scrapy


class MySpider(scrapy.Spider):
    name = "myspider"
    start_urls = [
        "http://example.com",
        "http://example.org",
        "http://example.net",
    ]

    def parse(self, response):
        # We want to inspect one specific response.
        if ".org" in response.url:
            from scrapy.shell import inspect_response
            inspect_response(response, self)

        # Rest of parsing code.

When you run the spider, you will get something similar to this:

2014-01-23 17:48:31-0400 [scrapy] DEBUG: Crawled (200) <GET http://example.com> (referer: None)
2014-01-23 17:48:31-0400 [scrapy] DEBUG: Crawled (200) <GET http://example.org> (referer: None)
[s] Available Scrapy objects:
[s]   crawler    <scrapy.crawler.Crawler object at 0x1e16b50>
...

>>> response.url
'http://example.org'

Then, you can check if the extraction code is working:

>>> response.xpath('//h1[@class="fn"]')
[]

Nope, it doesn’t. So you can open the response in your web browser and see if it’s the response you were expecting:

>>> view(response)
True

Finally you hit Ctrl-D (or Ctrl-Z in Windows) to exit the shell and resume the crawling:

>>> ^D
2014-01-23 17:50:03-0400 [scrapy] DEBUG: Crawled (200) <GET http://example.net> (referer: None)
...

Note that you can’t use the fetch shortcut here since the Scrapy engine is blocked by the shell. However, after you leave the shell, the spider will continue crawling where it stopped, as shown above.

アイテムパイプライン

After an item has been scraped by a spider, it is sent to the Item Pipeline which processes it through several components that are executed sequentially.

Each item pipeline component (sometimes referred as just “Item Pipeline”) is a Python class that implements a simple method. They receive an item and perform an action over it, also deciding if the item should continue through the pipeline or be dropped and no longer processed.

Typical uses of item pipelines are:

  • cleansing HTML data
  • validating scraped data (checking that the items contain certain fields)
  • checking for duplicates (and dropping them)
  • storing the scraped item in a database

独自のアイテムパイプラインの作成

Each item pipeline component is a Python class that must implement the following method:

process_item(self, item, spider)

This method is called for every item pipeline component. process_item() must either: return a dict with data, return an Item (or any descendant class) object, return a Twisted Deferred or raise DropItem exception. Dropped items are no longer processed by further pipeline components.

パラメータ:
  • item (Item object or a dict) – the item scraped
  • spider (Spider object) – the spider which scraped the item

Additionally, they may also implement the following methods:

open_spider(self, spider)

This method is called when the spider is opened.

パラメータ:spider (Spider object) – the spider which was opened
close_spider(self, spider)

This method is called when the spider is closed.

パラメータ:spider (Spider object) – the spider which was closed
from_crawler(cls, crawler)

If present, this classmethod is called to create a pipeline instance from a Crawler. It must return a new instance of the pipeline. Crawler object provides access to all Scrapy core components like settings and signals; it is a way for pipeline to access them and hook its functionality into Scrapy.

パラメータ:crawler (Crawler object) – crawler that uses this pipeline

アイテムパイプラインの例

価格の確認と価格のないアイテムのドロップ

Let’s take a look at the following hypothetical pipeline that adjusts the price attribute for those items that do not include VAT (price_excludes_vat attribute), and drops those items which don’t contain a price:

from scrapy.exceptions import DropItem

class PricePipeline(object):

    vat_factor = 1.15

    def process_item(self, item, spider):
        if item['price']:
            if item['price_excludes_vat']:
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise DropItem("Missing price in %s" % item)
アイテムをJSONファイルに出力する

The following pipeline stores all scraped items (from all spiders) into a single items.jl file, containing one item per line serialized in JSON format:

import json

class JsonWriterPipeline(object):

    def open_spider(self, spider):
        self.file = open('items.jl', 'wb')

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item

注釈

The purpose of JsonWriterPipeline is just to introduce how to write item pipelines. If you really want to store all scraped items into a JSON file you should use the Feed exports.

アイテムをMongoDBに出力する

In this example we’ll write items to MongoDB using pymongo. MongoDB address and database name are specified in Scrapy settings; MongoDB collection is named after item class.

The main point of this example is to show how to use from_crawler() method and how to clean up the resources properly.:

import pymongo

class MongoPipeline(object):

    collection_name = 'scrapy_items'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.collection_name].insert(dict(item))
        return item
アイテムのスクリーンショットを撮る

This example demonstrates how to return Deferred from process_item() method. It uses Splash to render screenshot of item url. Pipeline makes request to locally running instance of Splash. After request is downloaded and Deferred callback fires, it saves item to a file and adds filename to an item.

import scrapy
import hashlib
from urllib.parse import quote


class ScreenshotPipeline(object):
    """Pipeline that uses Splash to render screenshot of
    every Scrapy item."""

    SPLASH_URL = "http://localhost:8050/render.png?url={}"

    def process_item(self, item, spider):
        encoded_item_url = quote(item["url"])
        screenshot_url = self.SPLASH_URL.format(encoded_item_url)
        request = scrapy.Request(screenshot_url)
        dfd = spider.crawler.engine.download(request, spider)
        dfd.addBoth(self.return_item, item)
        return dfd

    def return_item(self, response, item):
        if response.status != 200:
            # Error happened, return item.
            return item

        # Save screenshot to file, filename will be hash of url.
        url = item["url"]
        url_hash = hashlib.md5(url.encode("utf8")).hexdigest()
        filename = "{}.png".format(url_hash)
        with open(filename, "wb") as f:
            f.write(response.body)

        # Store filename in item.
        item["screenshot_filename"] = filename
        return item
重複チェック

A filter that looks for duplicate items, and drops those items that were already processed. Let’s say that our items have a unique id, but our spider returns multiples items with the same id:

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return item

アイテムパイプラインコンポーネントの有効化

To activate an Item Pipeline component you must add its class to the ITEM_PIPELINES setting, like in the following example:

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

The integer values you assign to classes in this setting determine the order in which they run: items go through from lower valued to higher valued classes. It’s customary to define these numbers in the 0-1000 range.

フィードのエクスポート

バージョン 0.10 で追加.

One of the most frequently required features when implementing scrapers is being able to store the scraped data properly and, quite often, that means generating an “export file” with the scraped data (commonly called “export feed”) to be consumed by other systems.

Scrapy provides this functionality out of the box with the Feed Exports, which allows you to generate a feed with the scraped items, using multiple serialization formats and storage backends.

シリアライズフォーマット

For serializing the scraped data, the feed exports use the Item exporters. These formats are supported out of the box:

But you can also extend the supported format through the FEED_EXPORTERS setting.

JSON
JSON lines
CSV
  • FEED_FORMAT: csv
  • Exporter used: CsvItemExporter
  • To specify columns to export and their order use FEED_EXPORT_FIELDS. Other feed exporters can also use this option, but it is important for CSV because unlike many other export formats CSV uses a fixed header.
XML
Pickle
Marshal
  • FEED_FORMAT: marshal
  • Exporter used: MarshalItemExporter

ストレージ

When using the feed exports you define where to store the feed using a URI (through the FEED_URI setting). The feed exports supports multiple storage backend types which are defined by the URI scheme.

The storages backends supported out of the box are:

Some storage backends may be unavailable if the required external libraries are not available. For example, the S3 backend is only available if the botocore or boto library is installed (Scrapy supports boto only on Python 2).

ストレージURIパラメータ

The storage URI can also contain parameters that get replaced when the feed is being created. These parameters are:

  • %(time)s - gets replaced by a timestamp when the feed is being created
  • %(name)s - gets replaced by the spider name

Any other named parameter gets replaced by the spider attribute of the same name. For example, %(site_id)s would get replaced by the spider.site_id attribute the moment the feed is being created.

Here are some examples to illustrate:

  • Store in FTP using one directory per spider:
    • ftp://user:password@ftp.example.com/scraping/feeds/%(name)s/%(time)s.json
  • Store in S3 using one directory per spider:
    • s3://mybucket/scraping/feeds/%(name)s/%(time)s.json

バックエンドストレージ

ローカルファイルシステム

The feeds are stored in the local filesystem.

  • URI scheme: file
  • Example URI: file:///tmp/export.csv
  • Required external libraries: none

Note that for the local filesystem storage (only) you can omit the scheme if you specify an absolute path like /tmp/export.csv. This only works on Unix systems though.

FTP

The feeds are stored in a FTP server.

  • URI scheme: ftp
  • Example URI: ftp://user:pass@ftp.example.com/path/to/export.csv
  • Required external libraries: none
S3

The feeds are stored on Amazon S3.

  • URI scheme: s3
  • Example URIs:
    • s3://mybucket/path/to/export.csv
    • s3://aws_key:aws_secret@mybucket/path/to/export.csv
  • Required external libraries: botocore or boto

The AWS credentials can be passed as user/password in the URI, or they can be passed through the following settings:

標準出力

The feeds are written to the standard output of the Scrapy process.

  • URI scheme: stdout
  • Example URI: stdout:
  • Required external libraries: none

設定

These are the settings used for configuring the feed exports:

FEED_URI

Default: None

The URI of the export feed. See バックエンドストレージ for supported URI schemes.

This setting is required for enabling the feed exports.

FEED_FORMAT

The serialization format to be used for the feed. See シリアライズフォーマット for possible values.

FEED_EXPORT_ENCODING

Default: None

The encoding to be used for the feed.

If unset or set to None (default) it uses UTF-8 for everything except JSON output, which uses safe numeric encoding (\uXXXX sequences) for historic reasons.

Use utf-8 if you want UTF-8 for JSON too.

FEED_EXPORT_FIELDS

Default: None

A list of fields to export, optional. Example: FEED_EXPORT_FIELDS = ["foo", "bar", "baz"].

Use FEED_EXPORT_FIELDS option to define fields to export and their order.

When FEED_EXPORT_FIELDS is empty or None (default), Scrapy uses fields defined in dicts or Item subclasses a spider is yielding.

If an exporter requires a fixed set of fields (this is the case for CSV export format) and FEED_EXPORT_FIELDS is empty or None, then Scrapy tries to infer field names from the exported data - currently it uses field names from the first item.

FEED_STORE_EMPTY

Default: False

Whether to export empty feeds (ie. feeds with no items).

FEED_STORAGES

Default: {}

A dict containing additional feed storage backends supported by your project. The keys are URI schemes and the values are paths to storage classes.

FEED_STORAGES_BASE

Default:

{
    '': 'scrapy.extensions.feedexport.FileFeedStorage',
    'file': 'scrapy.extensions.feedexport.FileFeedStorage',
    'stdout': 'scrapy.extensions.feedexport.StdoutFeedStorage',
    's3': 'scrapy.extensions.feedexport.S3FeedStorage',
    'ftp': 'scrapy.extensions.feedexport.FTPFeedStorage',
}

A dict containing the built-in feed storage backends supported by Scrapy. You can disable any of these backends by assigning None to their URI scheme in FEED_STORAGES. E.g., to disable the built-in FTP storage backend (without replacement), place this in your settings.py:

FEED_STORAGES = {
    'ftp': None,
}
FEED_EXPORTERS

Default: {}

A dict containing additional exporters supported by your project. The keys are serialization formats and the values are paths to Item exporter classes.

FEED_EXPORTERS_BASE

Default:

{
    'json': 'scrapy.exporters.JsonItemExporter',
    'jsonlines': 'scrapy.exporters.JsonLinesItemExporter',
    'jl': 'scrapy.exporters.JsonLinesItemExporter',
    'csv': 'scrapy.exporters.CsvItemExporter',
    'xml': 'scrapy.exporters.XmlItemExporter',
    'marshal': 'scrapy.exporters.MarshalItemExporter',
    'pickle': 'scrapy.exporters.PickleItemExporter',
}

A dict containing the built-in feed exporters supported by Scrapy. You can disable any of these exporters by assigning None to their serialization format in FEED_EXPORTERS. E.g., to disable the built-in CSV exporter (without replacement), place this in your settings.py:

FEED_EXPORTERS = {
    'csv': None,
}

リクエストとレスポンス

Scrapy uses Request and Response objects for crawling web sites.

Typically, Request objects are generated in the spiders and pass across the system until they reach the Downloader, which executes the request and returns a Response object which travels back to the spider that issued the request.

Both Request and Response classes have subclasses which add functionality not required in the base classes. These are described below in Request subclasses and Response サブクラス.

Request オブジェクト

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

A Request object represents an HTTP request, which is usually generated in the Spider and executed by the Downloader, and thus generating a Response.

パラメータ:
  • url (string) – the URL of this request
  • callback (callable) – the function that will be called with the response of this request (once its downloaded) as its first parameter. For more information see 追加データをコールバック関数に渡す below. If a Request doesn’t specify a callback, the spider’s parse() method will be used. Note that if exceptions are raised during processing, errback is called instead.
  • method (string) – the HTTP method of this request. Defaults to 'GET'.
  • meta (dict) – the initial values for the Request.meta attribute. If given, the dict passed in this parameter will be shallow copied.
  • body (str or unicode) – the request body. If a unicode is passed, then it’s encoded to str using the encoding passed (which defaults to utf-8). If body is not given, an empty string is stored. Regardless of the type of this argument, the final value stored will be a str (never unicode or None).
  • headers (dict) – the headers of this request. The dict values can be strings (for single valued headers) or lists (for multi-valued headers). If None is passed as value, the HTTP header will not be sent at all.
  • cookies (dict or list) –

    the request cookies. These can be sent in two forms.

    1. Using a dict:
      request_with_cookies = Request(url="http://www.example.com",
                                     cookies={'currency': 'USD', 'country': 'UY'})
      
    2. Using a list of dicts:
      request_with_cookies = Request(url="http://www.example.com",
                                     cookies=[{'name': 'currency',
                                              'value': 'USD',
                                              'domain': 'example.com',
                                              'path': '/currency'}])
      

    The latter form allows for customizing the domain and path attributes of the cookie. This is only useful if the cookies are saved for later requests.

    When some site returns cookies (in a response) those are stored in the cookies for that domain and will be sent again in future requests. That’s the typical behaviour of any regular web browser. However, if, for some reason, you want to avoid merging with existing cookies you can instruct Scrapy to do so by setting the dont_merge_cookies key to True in the Request.meta.

    Example of request without merging cookies:

    request_with_cookies = Request(url="http://www.example.com",
                                   cookies={'currency': 'USD', 'country': 'UY'},
                                   meta={'dont_merge_cookies': True})
    

    For more info see CookiesMiddleware.

  • encoding (string) – the encoding of this request (defaults to 'utf-8'). This encoding will be used to percent-encode the URL and to convert the body to str (if given as unicode).
  • priority (int) – the priority of this request (defaults to 0). The priority is used by the scheduler to define the order used to process requests. Requests with a higher priority value will execute earlier. Negative values are allowed in order to indicate relatively low-priority.
  • dont_filter (boolean) – indicates that this request should not be filtered by the scheduler. This is used when you want to perform an identical request multiple times, to ignore the duplicates filter. Use it with care, or you will get into crawling loops. Default to False.
  • errback (callable) – a function that will be called if any exception was raised while processing the request. This includes pages that failed with 404 HTTP errors and such. It receives a Twisted Failure instance as first parameter. For more information, see エラー処理を使用して要求処理で例外を捕捉する below.
url

A string containing the URL of this request. Keep in mind that this attribute contains the escaped URL, so it can differ from the URL passed in the constructor.

This attribute is read-only. To change the URL of a Request use replace().

method

A string representing the HTTP method in the request. This is guaranteed to be uppercase. Example: "GET", "POST", "PUT", etc

headers

A dictionary-like object which contains the request headers.

body

A str that contains the request body.

This attribute is read-only. To change the body of a Request use replace().

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 特殊キー 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.

copy()

Return a new Request which is a copy of this Request. See also: 追加データをコールバック関数に渡す.

replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback])

Return a Request object with the same members, except for those members given new values by whichever keyword arguments are specified. The attribute Request.meta is copied by default (unless a new value is given in the meta argument). See also 追加データをコールバック関数に渡す.

追加データをコールバック関数に渡す

The callback of a request is a function that will be called when the response of that request is downloaded. The callback function will be called with the downloaded Response object as its first argument.

Example:

def parse_page1(self, response):
    return scrapy.Request("http://www.example.com/some_page.html",
                          callback=self.parse_page2)

def parse_page2(self, response):
    # this would log http://www.example.com/some_page.html
    self.logger.info("Visited %s", response.url)

In some cases you may be interested in passing arguments to those callback functions so you can receive the arguments later, in the second callback. You can use the Request.meta attribute for that.

Here’s an example of how to pass an item using this mechanism, to populate different fields from different pages:

def parse_page1(self, response):
    item = MyItem()
    item['main_url'] = response.url
    request = scrapy.Request("http://www.example.com/some_page.html",
                             callback=self.parse_page2)
    request.meta['item'] = item
    return request

def parse_page2(self, response):
    item = response.meta['item']
    item['other_url'] = response.url
    return item
エラー処理を使用して要求処理で例外を捕捉する

The errback of a request is a function that will be called when an exception is raise while processing it.

It receives a Twisted Failure instance as first parameter and can be used to track connection establishment timeouts, DNS errors etc.

Here’s an example spider logging all errors and catching some specific errors if needed:

import scrapy

from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError

class ErrbackSpider(scrapy.Spider):
    name = "errback_example"
    start_urls = [
        "http://www.httpbin.org/",              # HTTP 200 expected
        "http://www.httpbin.org/status/404",    # Not found error
        "http://www.httpbin.org/status/500",    # server issue
        "http://www.httpbin.org:12345/",        # non-responding host, timeout expected
        "http://www.httphttpbinbin.org/",       # DNS error expected
    ]

    def start_requests(self):
        for u in self.start_urls:
            yield scrapy.Request(u, callback=self.parse_httpbin,
                                    errback=self.errback_httpbin,
                                    dont_filter=True)

    def parse_httpbin(self, response):
        self.logger.info('Got successful response from {}'.format(response.url))
        # do something useful here...

    def errback_httpbin(self, failure):
        # log all failures
        self.logger.error(repr(failure))

        # in case you want to do something special for some errors,
        # you may need the failure's type:

        if failure.check(HttpError):
            # these exceptions come from HttpError spider middleware
            # you can get the non-200 response
            response = failure.value.response
            self.logger.error('HttpError on %s', response.url)

        elif failure.check(DNSLookupError):
            # this is the original request
            request = failure.request
            self.logger.error('DNSLookupError on %s', request.url)

        elif failure.check(TimeoutError, TCPTimedOutError):
            request = failure.request
            self.logger.error('TimeoutError on %s', request.url)

Request.meta 特殊キー

The Request.meta attribute can contain any arbitrary data, but there are some special keys recognized by Scrapy and its built-in extensions.

Those are:

bindaddress

The IP of the outgoing IP address to use for the performing the request.

download_timeout

The amount of time (in secs) that the downloader will wait before timing out. See also: DOWNLOAD_TIMEOUT.

download_latency

The amount of time spent to fetch the response, since the request has been started, i.e. HTTP message sent over the network. This meta key only becomes available when the response has been downloaded. While most other meta keys are used to control Scrapy behavior, this one is supposed to be read-only.

Request subclasses

Here is the list of built-in Request subclasses. You can also subclass it to implement your own custom functionality.

FormRequest objects

The FormRequest class extends the base Request with functionality for dealing with HTML forms. It uses lxml.html forms to pre-populate form fields with form data from Response objects.

class scrapy.http.FormRequest(url[, formdata, ...])

The FormRequest class adds a new argument to the constructor. The remaining arguments are the same as for the Request class and are not documented here.

パラメータ:formdata (dict or iterable of tuples) – is a dictionary (or iterable of (key, value) tuples) containing HTML Form data which will be url-encoded and assigned to the body of the request.

The FormRequest objects support the following class method in addition to the standard Request methods:

classmethod from_response(response[, formname=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ...])

Returns a new FormRequest object with its form field values pre-populated with those found in the HTML <form> element contained in the given response. For an example see FormRequest.from_response()を使用してユーザーログインをシミュレートする.

The policy is to automatically simulate a click, by default, on any form control that looks clickable, like a <input type="submit">. Even though this is quite convenient, and often the desired behaviour, sometimes it can cause problems which could be hard to debug. For example, when working with forms that are filled and/or submitted using javascript, the default from_response() behaviour may not be the most appropriate. To disable this behaviour you can set the dont_click argument to True. Also, if you want to change the control clicked (instead of disabling it) you can also use the clickdata argument.

パラメータ:
  • response (Response object) – the response containing a HTML form which will be used to pre-populate the form fields
  • formname (string) – if given, the form with name attribute set to this value will be used.
  • formxpath (string) – if given, the first form that matches the xpath will be used.
  • formcss (string) – if given, the first form that matches the css selector will be used.
  • formnumber (integer) – the number of form to use, when the response contains multiple forms. The first one (and also the default) is 0.
  • formdata (dict) – fields to override in the form data. If a field was already present in the response <form> element, its value is overridden by the one passed in this parameter.
  • clickdata (dict) – attributes to lookup the control clicked. If it’s not given, the form data will be submitted simulating a click on the first clickable element. In addition to html attributes, the control can be identified by its zero-based index relative to other submittable inputs inside the form, via the nr attribute.
  • dont_click (boolean) – If True, the form data will be submitted without clicking in any element.

The other parameters of this class method are passed directly to the FormRequest constructor.

バージョン 0.10.3 で追加: The formname parameter.

バージョン 0.17 で追加: The formxpath parameter.

バージョン 1.1.0 で追加: The formcss parameter.

Request 使用例
FormRequestを使用したHTTP POSTによるデータの送信

If you want to simulate a HTML Form POST in your spider and send a couple of key-value fields, you can return a FormRequest object (from your spider) like this:

return [FormRequest(url="http://www.example.com/post/action",
                    formdata={'name': 'John Doe', 'age': '27'},
                    callback=self.after_post)]
FormRequest.from_response()を使用してユーザーログインをシミュレートする

It is usual for web sites to provide pre-populated form fields through <input type="hidden"> elements, such as session related data or authentication tokens (for login pages). When scraping, you’ll want these fields to be automatically pre-populated and only override a couple of them, such as the user name and password. You can use the FormRequest.from_response() method for this job. Here’s an example spider which uses it:

import scrapy

class LoginSpider(scrapy.Spider):
    name = 'example.com'
    start_urls = ['http://www.example.com/users/login.php']

    def parse(self, response):
        return scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'john', 'password': 'secret'},
            callback=self.after_login
        )

    def after_login(self, response):
        # check login succeed before going on
        if "authentication failed" in response.body:
            self.logger.error("Login failed")
            return

        # continue scraping with authenticated session...

Response オブジェクト

class scrapy.http.Response(url[, status=200, headers=None, body=b'', flags=None, request=None])

A Response object represents an HTTP response, which is usually downloaded (by the Downloader) and fed to the Spiders for processing.

パラメータ:
  • url (string) – the URL of this response
  • status (integer) – the HTTP status of the response. Defaults to 200.
  • headers (dict) – the headers of this response. The dict values can be strings (for single valued headers) or lists (for multi-valued headers).
  • body (str) – the response body. It must be str, not unicode, unless you’re using a encoding-aware Response subclass, such as TextResponse.
  • flags (list) – is a list containing the initial values for the Response.flags attribute. If given, the list will be shallow copied.
  • request (Request object) – the initial value of the Response.request attribute. This represents the Request that generated this response.
url

A string containing the URL of the response.

This attribute is read-only. To change the URL of a Response use replace().

status

An integer representing the HTTP status of the response. Example: 200, 404.

headers

A dictionary-like object which contains the response headers. Values can be accessed using get() to return the first header value with the specified name or getlist() to return all header values with the specified name. For example, this call will give you all cookies in the headers:

response.headers.getlist('Set-Cookie')
body

The body of this Response. Keep in mind that Response.body is always a bytes object. If you want the unicode version use TextResponse.text (only available in TextResponse and subclasses).

This attribute is read-only. To change the body of a Response use replace().

request

The Request object that generated this response. This attribute is assigned in the Scrapy engine, after the response and the request have passed through all Downloader Middlewares. In particular, this means that:

  • HTTP redirections will cause the original request (to the URL before redirection) to be assigned to the redirected response (with the final URL after redirection).
  • Response.request.url doesn’t always equal Response.url
  • This attribute is only available in the spider code, and in the Spider Middlewares, but not in Downloader Middlewares (although you have the Request available there by other means) and handlers of the response_downloaded signal.
meta

A shortcut to the Request.meta attribute of the Response.request object (ie. self.request.meta).

Unlike the Response.request attribute, the Response.meta attribute is propagated along redirects and retries, so you will get the original Request.meta sent from your spider.

参考

Request.meta attribute

flags

A list that contains flags for this response. Flags are labels used for tagging Responses. For example: ‘cached’, ‘redirected‘, etc. And they’re shown on the string representation of the Response (__str__ method) which is used by the engine for logging.

copy()

Returns a new Response which is a copy of this Response.

replace([url, status, headers, body, request, flags, cls])

Returns a Response object with the same members, except for those members given new values by whichever keyword arguments are specified. The attribute Response.meta is copied by default.

urljoin(url)

Constructs an absolute url by combining the Response’s url with a possible relative url.

This is a wrapper over urlparse.urljoin, it’s merely an alias for making this call:

urlparse.urljoin(response.url, url)

Response サブクラス

Here is the list of available built-in Response subclasses. You can also subclass the Response class to implement your own functionality.

TextResponse オブジェクト
class scrapy.http.TextResponse(url[, encoding[, ...]])

TextResponse objects adds encoding capabilities to the base Response class, which is meant to be used only for binary data, such as images, sounds or any media file.

TextResponse objects support a new constructor argument, in addition to the base Response objects. The remaining functionality is the same as for the Response class and is not documented here.

パラメータ:encoding (string) – is a string which contains the encoding to use for this response. If you create a TextResponse object with a unicode body, it will be encoded using this encoding (remember the body attribute is always a string). If encoding is None (default value), the encoding will be looked up in the response headers and body instead.

TextResponse objects support the following attributes in addition to the standard Response ones:

text

Response body, as unicode.

The same as response.body.decode(response.encoding), but the result is cached after the first call, so you can access response.text multiple times without extra overhead.

注釈

unicode(response.body) is not a correct way to convert response body to unicode: you would be using the system default encoding (typically ascii) instead of the response encoding.

encoding

A string with the encoding of this response. The encoding is resolved by trying the following mechanisms, in order:

  1. the encoding passed in the constructor encoding argument
  2. the encoding declared in the Content-Type HTTP header. If this encoding is not valid (ie. unknown), it is ignored and the next resolution mechanism is tried.
  3. the encoding declared in the response body. The TextResponse class doesn’t provide any special functionality for this. However, the HtmlResponse and XmlResponse classes do.
  4. the encoding inferred by looking at the response body. This is the more fragile method but also the last one tried.
selector

A Selector instance using the response as target. The selector is lazily instantiated on first access.

TextResponse objects support the following methods in addition to the standard Response ones:

xpath(query)

A shortcut to TextResponse.selector.xpath(query):

response.xpath('//p')
css(query)

A shortcut to TextResponse.selector.css(query):

response.css('p')
body_as_unicode()

The same as text, but available as a method. This method is kept for backwards compatibility; please prefer response.text.

HtmlResponse オブジェクト
class scrapy.http.HtmlResponse(url[, ...])

The HtmlResponse class is a subclass of TextResponse which adds encoding auto-discovering support by looking into the HTML meta http-equiv attribute. See TextResponse.encoding.

XmlResponse オブジェクト
class scrapy.http.XmlResponse(url[, ...])

The XmlResponse class is a subclass of TextResponse which adds encoding auto-discovering support by looking into the XML declaration line. See TextResponse.encoding.

LinkExtractor

LinkExtractorは, 最終的に追跡されるウェブページ (scrapy.http.Response オブジェクト) からリンクを抽出することのみを目的とするオブジェクトです.

scrapy.linkextractors import LinkExtractor によって Scrapy で有効化されますが, シンプルなインターフェースを実装することで, 独自のカスタム LinkExtractor を作成してニーズに合わせることができます.

すべてのリンク抽出プログラムが持つ唯一のパブリックメソッドは extract_links です. これは, Response オブジェクトを受け取り scrapy.link.Link オブジェクトのリストを返します. LinkExtractor は一度インスタンス化されることを意図されており,  extract_links メソッドは異なる応答で数回呼び出され, 続くリンクを抽出します.

LinkExtractor は, 一連のルールを通じて CrawlSpider クラス (Scrapyで利用可能) で使用されますが, CrawlSpider からサブクラス化しない場合でも、スパイダーで使用することができます.

設定

Scrapyの設定では, コア, 拡張機能, パイプライン, スパイダー自体を含むすべての Scrapy コンポーネントの動作をカスタマイズすることができます.

設定のインフラストラクチャは, コードで値を取得するために使用できる, キーと値のマッピングのグローバルな名前空間を提供します. この設定は, 以下で説明するさまざまなメカニズムによって設定できます.

この設定は, 現在アクティブなScrapyプロジェクトを選択するためのメカニズムでもあります.

使用可能なビルトイン設定のリストについては,  ビルトイン設定リファレンス を参照してください.

設定を指定する

Scrapyを使用するときは, 環境変数 SCRAPY_SETTINGS_MODULE を使用して, 設定を Scrapy に教えてください.

SCRAPY_SETTINGS_MODULE の値はPythonのパス構文でなければなりません, e.g. myproject.settings. 設定モジュールは, Pythonの インポートサーチパス 上にある必要があります.

設定を入力する

設定は, それぞれ異なる優先順位を持つ異なるメカニズムを使用しています. 以下は優先順位の高い順にリストされています:

  1. コマンドラインオプション(優先度高)
  2. スパイダーごとの設定
  3. プロジェクト設定モジュール
  4. コマンドごとのデフォルト設定
  5. デフォルトのグローバル設定(優先度低)

これら設定の優先度は内部的に処理されますが, API呼び出しを使用して手動で処理することが可能です. 設定 API を参照してください.

これらのメカニズムは, 以下でより詳細に説明します.

1. コマンドラインオプション

コマンドラインで提供される引数は, 他のオプションよりも優先されます. -s (または --set) コマンドラインオプションを使用して, 1つ(またはそれ以上)の設定を明示的に上書きすることができます.

例:

scrapy crawl myspider -s LOG_FILE=scrapy.log
2. スパイダーごとの設定

スパイダー ( スパイダー を参照) は, 独自の設定を定義して優先順位を上げることができます. これは, custom_settings 属性に設定することで可能です:

class MySpider(scrapy.Spider):
    name = 'myspider'

    custom_settings = {
        'SOME_SETTING': 'some value',
    }
3. プロジェクト設定モジュール

プロジェクト設定モジュールは, Scrapyプロジェクトの標準設定ファイルです. カスタム設定の大部分が設定されます. 標準の Scrapy プロジェクトでは, プロジェクト用に作成された settings.py ファイルの設定を追加または変更することになります.

4. コマンドごとのデフォルト設定

Scrapy ツール コマンドには独自のデフォルト設定があり, グローバルなデフォルト設定を上書きします. これらのカスタムコマンド設定は,  default_settings 属性で指定されます.

5. デフォルトのグローバル設定

グローバルなデフォルト設定は scrapy.settings.default_settings モジュールにあり, ビルトイン設定リファレンス に記載されています.

設定にアクセスする方法

スパイダーでは, 設定は self.settings で取得することができます:

class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = ['http://example.com']

    def parse(self, response):
        print("Existing settings: %s" % self.settings.attributes.keys())

注釈

settings 属性は, スパイダーが初期化された後, ベース Spider クラスに設定されます. 初期化の前に設定を使用する場合(たとえば, スパイダーの __init__() メソッド), from_crawler() メソッドをオーバーライドする必要があります.

設定は, 拡張機能, ミドルウェアおよびアイテムパイプラインの from_crawler メソッドに渡される, クローラの scrapy.crawler.Crawler.settings 属性を介してアクセスできます.

class MyExtension(object):
    def __init__(self, log_is_enabled=False):
        if log_is_enabled:
            print("log is enabled!")

    @classmethod
    def from_crawler(cls, crawler):
        settings = crawler.settings
        return cls(settings.getbool('LOG_ENABLED'))

設定オブジェクトは dict'' (例: ``settings['LOG_ENABLED'] ) のように使用できますが, Settings API で提供されるメソッドの1つを使用して, タイプエラーを回避するために必要な形式で設定を抽出することをお勧めします.

名前を設定する理由

設定名には通常, 構成するコンポーネントの接頭辞が付いています. 例えば, 架空の robots.txt 拡張子の適切な設定名は ROBOTSTXT_ENABLED, ROBOTSTXT_OBEY, ROBOTSTXT_CACHEDIR などです.

ビルトイン設定リファレンス

ここでは, アルファベット順に使用可能なすべての Scrapy 設定のリストと, デフォルト値, 適用されるスコープが示されています.

使用可能な場合スコープは, 特定のコンポーネントに関連付けられていれば, 設定が使用されている場所を示します. その場合, そのコンポーネントのモジュール, 通常は拡張モジュール, ミドルウェアまたはパイプラインが表示されます. また, 設定を有効にするためにコンポーネントを有効にする必要があることも意味します.

AWS_ACCESS_KEY_ID

デフォルト: None

S3フィードストレージバックエンド など,   Amazon Web services のアクセスを必要とするコードで使用されるAWSアクセスキー.

AWS_SECRET_ACCESS_KEY

デフォルト: None

S3フィードストレージバックエンド など,  Amazon Web services へのアクセスを必要とするコードで使用されるAWS秘密鍵.

BOT_NAME

デフォルト: 'scrapybot'

このScrapyプロジェクトによって実装されたボットの名前(プロジェクト名とも呼ばれます). これは, デフォルトで User-Agent, ロギングに使用されます.

startproject コマンドを使用してプロジェクトを作成すると, プロジェクト名が自動的に入力されます.

CONCURRENT_ITEMS

デフォルト: 100

アイテムプロセッサ ( アイテムパイプライン とも呼ばれます) で並列処理する同時アイテムの最大数(応答あたり).

CONCURRENT_REQUESTS

デフォルト: 16

Scrapyダウンローダによって実行される並行(つまり同時の)リクエストの最大数.

CONCURRENT_REQUESTS_PER_DOMAIN

デフォルト: 8

単一のドメインに対して実行される並行(つまり同時)リクエストの最大数.

AutoThrottle 拡張機能AUTOTHROTTLE_TARGET_CONCURRENCY オプションも参照してください.

CONCURRENT_REQUESTS_PER_IP

デフォルト: 0

単一の IP に対して実行される並行(つまり同時)要求の最大数. 0以外の場合, CONCURRENT_REQUESTS_PER_DOMAIN 設定は無視され, 代わりにこの設定が使用されます. つまり, ドメインごとではなく, IPごとに並行処理の制限が適用されます.

この設定は, DOWNLOAD_DELAY 及び AutoThrottle 拡張機能: にも影響します. CONCURRENT_REQUESTS_PER_IP がゼロ以外の場合, ドメインごとではなくIPごとにダウンロード遅延が強制されます.

DEFAULT_ITEM_CLASS

デフォルト: 'scrapy.item.Item'

Scrapy shell でアイテムをインスタンス化するために使用されるデフォルトクラスです.

DEFAULT_REQUEST_HEADERS

デフォルト:

{
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}

Scrapy HTTP Request に使用されるデフォルトのヘッダー. これらは, DefaultHeadersMiddleware に設定されています.

DEPTH_LIMIT

デフォルト: 0

スコープ: scrapy.spidermiddlewares.depth.DepthMiddleware

どのサイトでもクロールできる最大の深さ. ゼロの場合, 制限は課されません.

DEPTH_PRIORITY

デフォルト: 0

スコープ: scrapy.spidermiddlewares.depth.DepthMiddleware

深さに基づいてリクエストの優先度を調整するために使用される整数:

  • 0(デフォルト)の場合, 深度からの優先調整は行われません
  • 正の値は優先度を下げます. つまり, 深度の高い要求が後で処理されます. これは, 幅優先のクロール(BFO)を行うときによく使用されます
  • 負の値は優先度を増加させます. すなわち, より深い深度要求がより早く処理されます(DFO)

BFO または DFO のチューニングに関しては Scrapyは幅優先, 深さ優先どちらでクロールしますか? を参照してください.

注釈

この設定は, 他の優先度設定である REDIRECT_PRIORITY_ADJUST 及び RETRY_PRIORITY_ADJUST と比較して, 優先度を調整します.

DEPTH_STATS

デフォルト: True

スコープ: scrapy.spidermiddlewares.depth.DepthMiddleware

最大深度統計を収集するかどうか.

DEPTH_STATS_VERBOSE

デフォルト: False

スコープ: scrapy.spidermiddlewares.depth.DepthMiddleware

冗長な深さ統計を収集するかどうか. これを有効にすると, 各深さのリクエスト数が統計情報に収集されます.

DNSCACHE_ENABLED

デフォルト: True

DNSインメモリキャッシュを有効にするかどうか.

DNSCACHE_SIZE

デフォルト: 10000

DNSのインメモリキャッシュサイズ.

DNS_TIMEOUT

デフォルト: 60

DNSクエリを処理するための秒単位タイムアウト. フロートがサポートされています.

DOWNLOADER

デフォルト: 'scrapy.core.downloader.Downloader'

クロールに使用するダウンローダ.

DOWNLOADER_HTTPCLIENTFACTORY

デフォルト: 'scrapy.core.downloader.webclient.ScrapyHTTPClientFactory'

HTTP / 1.0接続( HTTP10DownloadHandler の場合)に使用する, Twisted の protocol.ClientFactory クラスを定義します.

注釈

Twisted < 11.1 を使用しない限り, HTTP/1.0 は今日めったに使われないので, この設定を無視しても問題ありません. HTTP/1.0 を使用し, http(s) スキームに応じて DOWNLOAD_HANDLERS_BASE をオーバーライドする, すなわち 'scrapy.core.downloader.handlers.http.HTTP10DownloadHandler'.

DOWNLOADER_CLIENTCONTEXTFACTORY

デフォルト: 'scrapy.core.downloader.contextfactory.ScrapyClientContextFactory'

使用する ContextFactory へのクラスパスを表します.

ContextFactory は, SSL / TLS コンテキストの Twisted の用語で, 使用するTLS / SSLプロトコルのバージョン, 証明書の検証の有無, クライアント側の認証(およびその他のさまざまなもの)の有効化を定義します.

注釈

Scrapy のデフォルトコンテキストファクトリは, リモートサーバー証明書の検証を実行しません. これは, 通常のウェブスクレーピングでは問題ありません.

リモートサーバー証明書の検証が有効になっている必要がある場合は, プラットフォームの証明書を使用してリモートエンドポイントを検証する 'scrapy.core.downloader.contextfactory.BrowserLikeContextFactory' という別のコンテキストファクトリクラスを使用することもできます. Twisted>=14.0. で使用することができます.

カスタム ContextFactory を使用する場合は, init で method パラメータを受け入れるようにしてください (これは OpenSSL.SSL メソッドの DOWNLOADER_CLIENT_TLS_METHOD のマッピングです).

DOWNLOADER_CLIENT_TLS_METHOD

デフォルト: 'TLS'

この設定を使用して, デフォルトの HTTP/1.1 ダウンローダが使用する TLS/SSL 方式をカスタマイズします.

この設定は, これらの文字列値のいずれかでなければなりません:

  • 'TLS': OpenSSLの TLS_method() (a.k.a SSLv23_method()), にマップされています. これにより, プラットフォームでサポートされている最高位から始まる プロトコルネゴシエーションが可能になります; デフォルト, 推奨
  • 'TLSv1.0': この値を指定すると, HTTPS接続はTLSバージョン1.0を使用します. Scrapy < 1.1 の動作が必要な場合にこれを設定します
  • 'TLSv1.1': TLS バージョン 1.1 を強制します
  • 'TLSv1.2': TLS バージョン 1.2 を強制します
  • 'SSLv3': SSL バージョン 3 を強制します (非推奨)

注釈

PyOpenSSL >= 0.13, Twisted >= 0.13 以上を使用することをお勧めします(出来れば Twisted> = 14.0).

DOWNLOADER_MIDDLEWARES

デフォルト: {}

あなたのプロジェクトで有効になっているダウンローダミドルウェアとその注文を含む辞書. 詳細については ダウンローダーミドルウェアの有効化 を参照してください.

DOWNLOADER_MIDDLEWARES_BASE

デフォルト:

{
    'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
    'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
    'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
    'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
    'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
    'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
    'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
    'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
    'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
    'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
    'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}

Scrapyでデフォルトで有効になっているダウンローダミドルウェアを含む辞書. ローオーダーはエンジンに近く, ハイオーダーはダウンローダーに近くなっています. プロジェクトでこの設定を変更しないでください. 代わりに DOWNLOADER_MIDDLEWARES を変更してください. 詳細については, ダウンローダーミドルウェアの有効化 を参照してください.

DOWNLOADER_STATS

デフォルト: True

ダウンローダの統計情報収集を有効にするかどうか.

DOWNLOAD_DELAY

デフォルト: 0

ダウンローダが同じWebサイトから連続したページをダウンロードするまで待機する時間(秒). これは, サーバに負荷がかかることを避けるために, クロール速度を抑えるために使用できます. 10進数がサポートされています. 例:

DOWNLOAD_DELAY = 0.25    # 250 ms of delay

この設定は, RANDOMIZE_DOWNLOAD_DELAY 設定の影響を受けます(デフォルトで有効). 既定では, Scrapyは要求間の固定時間を待機しませんが, 0.5 * DOWNLOAD_DELAY から 1.5 * DOWNLOAD_DELAY までのランダムな間隔を使用します.

CONCURRENT_REQUESTS_PER_IP がゼロ以外の場合, 遅延はドメインごとではなくIPアドレスごとに適用されます.

download_delay スパイダー属性を設定することで, スパイダーごとにこの設定を変更することもできます.

DOWNLOAD_HANDLERS

デフォルト: {}

プロジェクトで有効になっているリクエストダウンローダーハンドラを含む dict. 形式のサンプルについては DOWNLOAD_HANDLERS_BASE を参照してください.

DOWNLOAD_HANDLERS_BASE

デフォルト:

{
    'file': 'scrapy.core.downloader.handlers.file.FileDownloadHandler',
    'http': 'scrapy.core.downloader.handlers.http.HTTPDownloadHandler',
    'https': 'scrapy.core.downloader.handlers.http.HTTPDownloadHandler',
    's3': 'scrapy.core.downloader.handlers.s3.S3DownloadHandler',
    'ftp': 'scrapy.core.downloader.handlers.ftp.FTPDownloadHandler',
}

デフォルトで Scrapy で有効になっているリクエストハンドラを含む dict . プロジェクトでこの設定を変更しないでください. 代わりに DOWNLOAD_HANDLERS を変更してください.

これらのダウンロードハンドラのいずれかを無効にするには, DOWNLOAD_HANDLERS の URI スキームにNoneを割り当てます. たとえば, 組み込みのFTPハンドラを無効にするには(置き換えずに), これを settings.py に記述子ます:

DOWNLOAD_HANDLERS = {
    'ftp': None,
}
DOWNLOAD_TIMEOUT

デフォルト: 180

ダウンローダーがタイムアウトするまで待機する時間(秒単位).

注釈

このタイムアウトは, download_timeout スパイダ属性と download_timeout Request.meta キーを使用したリクエストごとに設定できます.

DOWNLOAD_MAXSIZE

デフォルト: 1073741824 (1024MB)

ダウンローダがダウンロードする最大応答サイズ(バイト単位).

無効にする場合は, 0に設定します.

注釈

このサイズは, download_maxsize Request.metaキーを使用して, download_maxsize スパイダ属性とper-requestを使用してスパイダごとに設定できます.

この機能には Twisted >= 11.1 が必要です.

DOWNLOAD_WARNSIZE

デフォルト: 33554432 (32MB)

ダウンローダが警告を開始するレスポンスサイズ(バイト単位).

無効にする場合は, 0に設定します.

注釈

このサイズは, download_warnsize Request.metaキーを使用して, download_warnsize スパイダ属性およびper-requestを使用してスパイダごとに設定できます.

これには, Twisted >= 11.1 が必要です.

DUPEFILTER_CLASS

デフォルト: 'scrapy.dupefilters.RFPDupeFilter'

重複リクエストを検出してフィルタリングするために使用されるクラス.

デフォルトのフィルタ (RFPDupeFilter) は, scrapy.utils.request.request_fingerprint 関数を使用してリクエストフィンガープリントに基づいてフィルタリングします. 重複をチェックする方法を変更するには, RFPDupeFilter をサブクラス化し, request_fingerprint メソッドをオーバーライドすることができます. このメソッドは Request オブジェクトを受け入れ, そのフィンガープリント(文字列)を返す必要があります.

DUPEFILTER_DEBUG

デフォルト: False

デフォルトでは, RFPDupeFilter は最初の重複要求のみを記録します. DUPEFILTER_DEBUG を True に設定すると, すべての重複リクエストが記録されます.

EDITOR

デフォルト: 環境依存

edit マンドでスパイダーを編集するために使用するエディター. EDITOR 環境変数が設定されていると, デフォルトで使用されます. それ以外の場合, デフォルトは vi Unixシステムの場合)または IDLE エディター(Windowsの場合)になります.

EXTENSIONS

デフォルト:: {}

プロジェクトで有効になっている拡張機能と, そのオーダーを含む dict.

EXTENSIONS_BASE

デフォルト:

{
    'scrapy.extensions.corestats.CoreStats': 0,
    'scrapy.extensions.telnet.TelnetConsole': 0,
    'scrapy.extensions.memusage.MemoryUsage': 0,
    'scrapy.extensions.memdebug.MemoryDebugger': 0,
    'scrapy.extensions.closespider.CloseSpider': 0,
    'scrapy.extensions.feedexport.FeedExporter': 0,
    'scrapy.extensions.logstats.LogStats': 0,
    'scrapy.extensions.spiderstate.SpiderState': 0,
    'scrapy.extensions.throttle.AutoThrottle': 0,
}

Scrapy のデフォルトで利用可能な拡張機能とそのオーダーを含む dict . この設定には, すべての安定したビルトイン拡張機能が含まれています. そのうちのいくつかは設定によって有効にする必要があることに注意してください.

詳細については, 拡張機能ユーザーガイド 及び 使用可能な拡張機能の一覧 を参照してください.

FEED_TEMPDIR

Feed Temp ディレクトリでは, FTP フィードストレージAmazon S3 にアップロードする前に, クローラの一時ファイルを保存するカスタムフォルダを設定できます.

ITEM_PIPELINES

デフォルト: {}

使用するアイテムパイプラインとその注文を含む dict . 順序値は任意ですが, 0〜1000の範囲で定義するのが通例です. 下位の注文は高次のオーダーの前に処理されます.

例:

ITEM_PIPELINES = {
    'mybot.pipelines.validate.ValidateMyItem': 300,
    'mybot.pipelines.validate.StoreMyItem': 800,
}
ITEM_PIPELINES_BASE

デフォルト: {}

Scrapy でデフォルトで有効になっているパイプラインを含む dict . プロジェクトでこの設定を変更しないでください. 代わりに ITEM_PIPELINES を変更してください

LOG_ENABLED

デフォルト: True

ロギングを有効にするかどうか.

LOG_ENCODING

デフォルト: 'utf-8'

ロギングに使用するエンコーディング.

LOG_FILE

デフォルト: None

ログ出力に使用するファイル名. None の場合, 標準エラーが使用されます.

LOG_FORMAT

デフォルト: '%(asctime)s [%(name)s] %(levelname)s: %(message)s'

ログメッセージのフォーマット用の文字列. 使用可能なプレースホルダの一覧については, Python logging documentation を参照してください.

LOG_DATEFORMAT

デフォルト: '%Y-%m-%d %H:%M:%S'

日付/時刻の書式設定の文字列,  LOG_FORMAT%(asctime)s のプレースホルダの展開. 使用可能なディレクティブの一覧については, Python datetime documentation を参照してください.

LOG_LEVEL

デフォルト: 'DEBUG'

ログに記録する最小レベル. 使用可能なレベルは, CRITICAL, ERROR, WARNING, INFO, DEBUGです. 詳細は ロギング を参照してください.

LOG_STDOUT

デフォルト: False

True の場合, プロセスのすべての標準出力(およびエラー)がログにリダイレクトされます. たとえば, print 'hello' を実行すると, Scrapyログに表示されます.

MEMDEBUG_ENABLED

デフォルト: False

メモリデバッグを有効にするかどうか.

MEMDEBUG_NOTIFY

デフォルト: []

メモリデバッグが有効になっている場合, この設定が空でなければ, 指定されたアドレスにメモリレポートが送信されます. そうでない場合, レポートはログに書き込まれます.

例:

MEMDEBUG_NOTIFY = ['user@example.com']
MEMUSAGE_ENABLED

デフォルト: False

スコープ: scrapy.extensions.memusage

メモリ使用量の拡張を有効にして, メモリ制限を超えた場合に Scrapy プロセスをシャットダウンするかどうか. また, その場合は電子メールで通知されます.

メモリ使用量拡張機能 を参照してください.

MEMUSAGE_LIMIT_MB

デフォルト: 0

スコープ: scrapy.extensions.memusage

(MEMUSAGE_ENABLEDがTrueの場合)Scrapy をシャットダウンする前に許容される メモリの最大量(メガバイト単位). ゼロの場合, チェックは実行されません.

メモリ使用量拡張機能 を参照してください.

MEMUSAGE_CHECK_INTERVAL_SECONDS

バージョン 1.1 で追加.

デフォルト: 60.0

スコープ: scrapy.extensions.memusage

メモリ使用量拡張機能 は, MEMUSAGE_LIMIT_MB および MEMUSAGE_WARNING_MB によって設定された制限と現在のメモリ使用量を, 一定の時間間隔でチェックします.

これは, これらの間隔の長さを秒単位で設定します.

メモリ使用量拡張機能 を参照してください.

MEMUSAGE_NOTIFY_MAIL

デフォルト: False

スコープ: scrapy.extensions.memusage

モリー制限に達したことを通知する電子メールのリスト.

例:

MEMUSAGE_NOTIFY_MAIL = ['user@example.com']

メモリ使用量拡張機能 を参照してください.

MEMUSAGE_REPORT

デフォルト: False

スコープ: scrapy.extensions.memusage

各スパイダーが閉じられた後にメモリ使用量レポートを送信するかどうか.

メモリ使用量拡張機能 を参照してください.

MEMUSAGE_WARNING_MB

デフォルト: 0

スコープ: scrapy.extensions.memusage

通知する警告電子メールを送信する前に許容されるメモリの最大量(メガバイト単位). ゼロの場合, 警告は生成されません.

NEWSPIDER_MODULE

デフォルト: ''

genspider コマンドを使用して新しいスパイダーを作成する場所.

例:

NEWSPIDER_MODULE = 'mybot.spiders_dev'
RANDOMIZE_DOWNLOAD_DELAY

デフォルト: True

有効にすると, 同じWebサイトからリクエストを取得する間, Scrapyはランダムな時間 (0.5 * DOWNLOAD_DELAY から 1.5 * DOWNLOAD_DELAY の間まで) 待機します.

このランダム化は, リクエスト間の時間から統計的に有意な類似性を探し, リクエストを分析するサイトによってクローラが検出される(およびその後ブロックされる)可能性を低下させます.

ランダム化ポリシーは, wget --random-wait オプションで使用されるものと同じです.

DOWNLOAD_DELAY が 0(デフォルト)の場合, このオプションは無効です.

REACTOR_THREADPOOL_MAXSIZE

デフォルト: 10

Twisted Reactor スレッドプールサイズの最大限度. これは, さまざまな Scrapy コンポーネントで使用される汎用スレッドプールです. スレッド化された DNS リゾルバ, BlockingFeedStorage, S3 FilesStore などです. ブロッキング IO が不十分で問題が発生している場合は, この値を大きくしてください.

REDIRECT_MAX_TIMES

デフォルト: 20

要求をリダイレクトできる最大時間を定義します. この最大値の後, 要求の応答はそのまま返されます. 私達は, Firefoxのデフォルト値を同じタスクに使用しました.

REDIRECT_PRIORITY_ADJUST

デフォルト: +2

スコープ: scrapy.downloadermiddlewares.redirect.RedirectMiddleware

元のリクエストに対するリダイレクトリクエストの優先度を調整します.

  • 正の優先度調整(デフォルト)はより高い優先度を意味します.
  • 負の優先順位調整は低い優先順位を意味します.
RETRY_PRIORITY_ADJUST

デフォルト: -1

スコープ: scrapy.downloadermiddlewares.retry.RetryMiddleware

オリジナルのリクエストに対するリトライリクエストの優先度を調整する:

  • 正の優先度調整はより高い優先度を意味します.
  • 負の優先順位調整(デフォルト)は低い優先順位を意味します.
ROBOTSTXT_OBEY

デフォルト: False

スコープ: scrapy.downloadermiddlewares.robotstxt

有効にすると, Scrapy は robots.txt ポリシーを尊重します. 詳細については, RobotsTxtMiddleware を参照してください.

注釈

歴史的な理由からデフォルト値は False ですが, このオプションは scrapy startproject コマンドで生成される settings.py ファイルではデフォルトで有効になっています.

SCHEDULER

デフォルト: 'scrapy.core.scheduler.Scheduler'

クロールに使用するスケジューラ.

SCHEDULER_DEBUG

デフォルト: False

True に設定すると, 要求スケジューラに関するデバッグ情報が記録されます. 要求をディスクにシリアライズできない場合は, 現在ログに記録されます(1回のみ). Stats カウンター (scheduler/unserializable) は, これが発生する回数を追跡します.

ログのエントリ例:

1956-01-31 00:00:00+0800 [scrapy] ERROR: Unable to serialize request:
<GET http://example.com> - reason: cannot serialize <Request at 0x9a7c7ec>
(type Request)> - no more unserializable requests will be logged
(see 'scheduler/unserializable' stats counter)
SCHEDULER_DISK_QUEUE

デフォルト: 'scrapy.squeues.PickleLifoDiskQueue'

スケジューラーが使用するディスクキューのタイプ. その他の使用可能なタイプは, scrapy.squeues.PickleFifoDiskQueue, scrapy.squeues.MarshalFifoDiskQueue, scrapy.squeues.MarshalLifoDiskQueue.

SCHEDULER_MEMORY_QUEUE

デフォルト: 'scrapy.squeues.LifoMemoryQueue'

スケジューラーが使用するインメモリー・キューのタイプ. その他の使用可能なタイプは, scrapy.squeues.FifoMemoryQueue です.

SCHEDULER_PRIORITY_QUEUE

デフォルト: 'queuelib.PriorityQueue'

スケジューラーが使用する優先度キューのタイプ.

SPIDER_CONTRACTS

デフォルト:: {}

あなたのプロジェクトで有効にされたスパイダー契約を含む辞書. スパイダーのテストに使用されます. 詳細は スパイダーコントラクト を参照してください.

SPIDER_CONTRACTS_BASE

デフォルト:

{
    'scrapy.contracts.default.UrlContract' : 1,
    'scrapy.contracts.default.ReturnsContract': 2,
    'scrapy.contracts.default.ScrapesContract': 3,
}

Scrapyでデフォルトで有効になっているコントラクトを含む辞書. プロジェクトでこの設定を変更しないでください,  SPIDER_CONTRACTS を変更してください. 詳細は, スパイダーコントラクト を参照してください.

これらのコントラクトを無効にするには SPIDER_CONTRACTS のクラスパスに None を割り当ててください. 例えば, 組み込みの ScrapesContract を無効にするには, settings.py に, この様に記述します:

SPIDER_CONTRACTS = {
    'scrapy.contracts.default.ScrapesContract': None,
}
SPIDER_LOADER_CLASS

デフォルト: 'scrapy.spiderloader.SpiderLoader'

スパイダーローダー API を実装する必要があるスパイダーのロードに使用されるクラス.

SPIDER_MIDDLEWARES

デフォルト:: {}

なたのプロジェクトで利用可能なスパイダーミドルウェアとその注文を含む辞書. 詳細は スパイダーミドルウェアの有効化 を参照してください.

SPIDER_MIDDLEWARES_BASE

デフォルト:

{
    'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
    'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
    'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,
    'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
}

Scrapy でデフォルトで有効になっているスパイダーミドルウェアとそのオーダーを含む辞書. オーダーの値が低いものはエンジンに近く, 高いものははスパイダーに近くなっています. 詳細については, スパイダーミドルウェアの有効化 を参照してください.

SPIDER_MODULES

デフォルト: []

Scrapy がスパイダーを探すモジュールのリスト.

例:

SPIDER_MODULES = ['mybot.spiders_prod', 'mybot.spiders_dev']
STATS_CLASS

デフォルト: 'scrapy.statscollectors.MemoryStatsCollector'

統計コレクター API を実装する必要がある, 統計を収集するために使用するクラス.

STATS_DUMP

デフォルト: True

スパイダーが終了したときに Scrapy stats (Scrapy ログに) をダンプします.

詳細については, 統計コレクション を参照してください.

STATSMAILER_RCPTS

デフォルト: [] (empty list)

スパイダーがスクレイピングを終えた後, Scrapyの統計情報を送信します. 詳細は StatsMailer を参照してください.

TELNETCONSOLE_ENABLED

デフォルト: True

telnet console を有効にするかどうかを指定する真偽値です (拡張機能が有効な場合).

TELNETCONSOLE_PORT

デフォルト: [6023, 6073]

Telnet コンソールに使用するポート範囲. None または 0 を設定すると, 動的に割り当てられたポートが使用されます. 詳細については, Telnet コンソール を参照してください.

TEMPLATES_DIR

デフォルト: Scrapy モジュール内の templates ディレクトリ

startproject コマンドで新しいプロジェクトを作成するときにテンプレートを検索するディレクトリと, genspider コマンドで新しいスパイダーを検索するディレクトリ.

プロジェクト名は, プロジェクトサブディレクトリ内のカスタムファイルまたはディレクトリの名前と競合してはいけません.

URLLENGTH_LIMIT

デフォルト: 2083

スコープ: spidermiddlewares.urllength

クロールを許可するURLの最大長. この設定のデフォルト値の詳細については, http://www.boutell.com/newfaq/misc/urllength.html を参照してください.

USER_AGENT

デフォルト: "Scrapy/VERSION (+http://scrapy.org)"

オーバーライドされない限り, クロール時に使用されるデフォルトの User-Agent.

他の場所で文書化された設定:

次の設定は他の場所で文書化されています. それぞれのケースを確認して使用する方法を確認してください.

エクセプション

ビルトインエクセプションリファレンス

Scrapyに含まれるすべての例外のリストとその使用法.

DropItem
exception scrapy.exceptions.DropItem

アイテムの処理を停止するためにアイテムパイプラインステージで発生させる必要がある例外. 詳細については、項目 アイテムパイプライン を参照してください.

CloseSpider
exception scrapy.exceptions.CloseSpider(reason='cancelled')

この例外は、スパイダーのコールバックからクローズ/ストップを要求することができます. サポートされている引数:

パラメータ:reason (str) – クローズした理由

例:

def parse_page(self, response):
    if 'Bandwidth exceeded' in response.body:
        raise CloseSpider('bandwidth_exceeded')
IgnoreRequest
exception scrapy.exceptions.IgnoreRequest

この例外は、スケジューラまたは任意のダウンローダミドルウェアによって, リクエストを無視すべきであることを示すために発生させることができます.

NotConfigured
exception scrapy.exceptions.NotConfigured

この例外は, 一部のコンポーネントによって, それらが無効のままであることを示すために発生させることができます. コンポーネントは以下を含んでいます:

  • 拡張機能
  • アイテムパイプライン
  • ダウンローダミドルウェア
  • スパイダーミドルウェア

コンポーネントの __init__ メソッドで例外を発生させる必要があります.

NotSupported
exception scrapy.exceptions.NotSupported

この例外は、サポートされていない機能を示すために発生します.

コマンドラインツール
Scrapy プロジェクトの管理に使用するコマンドラインツールについて学ぶ.
スパイダー
ウェブサイトをクロールするためのルールを書く.
セレクタ
XPathを使用してWebページからデータを抽出する.
Scrapy シェル
インタラクティブな環境で抽出コードをテストする.
アイテム
スクレイプしたいデータを定義する.
アイテムローダー
抽出したデータをアイテムに埋め込む.
アイテムパイプライン
後処理してスクラップしたデータを保存する.
フィードのエクスポート
さまざまなフォーマットとストレージを使用してスクラップしたデータを出力する.
リクエストとレスポンス
HTTP要求と応答を表すために使用されるクラスを理解する.
LinkExtractor
ページから続くリンクを抽出するための便利なクラス.
設定
Scrapyを設定方法を学び, 利用可能な設定 をすべて見る.
エクセプション
使用可能な例外とその意味をすべて表示する.

Built-in サービス

ロギング

注釈

scrapy.log has been deprecated alongside its functions in favor of explicit calls to the Python standard logging. Keep reading to learn more about the new logging system.

Scrapy uses Python’s builtin logging system for event logging. We’ll provide some simple examples to get you started, but for more advanced use-cases it’s strongly suggested to read thoroughly its documentation.

Logging works out of the box, and can be configured to some extent with the Scrapy settings listed in ロギング設定.

Scrapy calls scrapy.utils.log.configure_logging() to set some reasonable defaults and handle those settings in ロギング設定 when running commands, so it’s recommended to manually call it if you’re running Scrapy from scripts as described in スクリプトから Scrapy を実行する.

ログレベル

Python’s builtin logging defines 5 different levels to indicate severity on a given log message. Here are the standard ones, listed in decreasing order:

  1. logging.CRITICAL - for critical errors (highest severity)
  2. logging.ERROR - for regular errors
  3. logging.WARNING - for warning messages
  4. logging.INFO - for informational messages
  5. logging.DEBUG - for debugging messages (lowest severity)

ログメッセージの使用方法

Here’s a quick example of how to log a message using the logging.WARNING level:

import logging
logging.warning("This is a warning")

There are shortcuts for issuing log messages on any of the standard 5 levels, and there’s also a general logging.log method which takes a given level as argument. If you need so, last example could be rewrote as:

import logging
logging.log(logging.WARNING, "This is a warning")

On top of that, you can create different “loggers” to encapsulate messages (For example, a common practice it’s to create different loggers for every module). These loggers can be configured independently, and they allow hierarchical constructions.

Last examples use the root logger behind the scenes, which is a top level logger where all messages are propagated to (unless otherwise specified). Using logging helpers is merely a shortcut for getting the root logger explicitly, so this is also an equivalent of last snippets:

import logging
logger = logging.getLogger()
logger.warning("This is a warning")

You can use a different logger just by getting its name with the logging.getLogger function:

import logging
logger = logging.getLogger('mycustomlogger')
logger.warning("This is a warning")

Finally, you can ensure having a custom logger for any module you’re working on by using the __name__ variable, which is populated with current module’s path:

import logging
logger = logging.getLogger(__name__)
logger.warning("This is a warning")

参考

Module logging, HowTo
Basic Logging Tutorial
Module logging, Loggers
Further documentation on loggers

スパイダーからのロギング

Scrapy provides a logger within each Spider instance, that can be accessed and used like this:

import scrapy

class MySpider(scrapy.Spider):

    name = 'myspider'
    start_urls = ['http://scrapinghub.com']

    def parse(self, response):
        self.logger.info('Parse function called on %s', response.url)

That logger is created using the Spider’s name, but you can use any custom Python logger you want. For example:

import logging
import scrapy

logger = logging.getLogger('mycustomlogger')

class MySpider(scrapy.Spider):

    name = 'myspider'
    start_urls = ['http://scrapinghub.com']

    def parse(self, response):
        logger.info('Parse function called on %s', response.url)

ロギング設定

Loggers on their own don’t manage how messages sent through them are displayed. For this task, different “handlers” can be attached to any logger instance and they will redirect those messages to appropriate destinations, such as the standard output, files, emails, etc.

By default, Scrapy sets and configures a handler for the root logger, based on the settings below.

ロギング設定

These settings can be used to configure the logging:

The first couple of settings define a destination for log messages. If LOG_FILE is set, messages sent through the root logger will be redirected to a file named LOG_FILE with encoding LOG_ENCODING. If unset and LOG_ENABLED is True, log messages will be displayed on the standard error. Lastly, if LOG_ENABLED is False, there won’t be any visible log output.

LOG_LEVEL determines the minimum level of severity to display, those messages with lower severity will be filtered out. It ranges through the possible levels listed in ログレベル.

LOG_FORMAT and LOG_DATEFORMAT specify formatting strings used as layouts for all messages. Those strings can contain any placeholders listed in logging’s logrecord attributes docs and datetime’s strftime and strptime directives respectively.

コマンドラインオプション

There are command-line arguments, available for all commands, that you can use to override some of the Scrapy settings regarding logging.

参考

Module logging.handlers
Further documentation on available handlers

scrapy.utils.log モジュール

scrapy.utils.log.configure_logging(settings=None, install_root_handler=True)

Initialize logging defaults for Scrapy.

パラメータ:
  • settings (dict, Settings object or None) – settings used to create and configure a handler for the root logger (default: None).
  • install_root_handler (bool) – whether to install root logging handler (default: True)

This function does:

  • Route warnings and twisted logging through Python standard logging
  • Assign DEBUG and ERROR level to Scrapy and Twisted loggers respectively
  • Route stdout to log if LOG_STDOUT setting is True

When install_root_handler is True (default), this function also creates a handler for the root logger according to given settings (see ロギング設定). You can override default options using settings argument. When settings is empty or None, defaults are used.

configure_logging is automatically called when using Scrapy commands, but needs to be called explicitly when running custom scripts. In that case, its usage is not required but it’s recommended.

If you plan on configuring the handlers yourself is still recommended you call this function, passing install_root_handler=False. Bear in mind there won’t be any log output set by default in that case.

To get you started on manually configuring logging’s output, you can use logging.basicConfig() to set a basic root handler. This is an example on how to redirect INFO or higher messages to a file:

import logging
from scrapy.utils.log import configure_logging

configure_logging(install_root_handler=False)
logging.basicConfig(
    filename='log.txt',
    format='%(levelname)s: %(message)s',
    level=logging.INFO
)

Refer to スクリプトから Scrapy を実行する for more details about using Scrapy this way.

統計コレクション

Scrapy provides a convenient facility for collecting stats in the form of key/values, where values are often counters. The facility is called the Stats Collector, and can be accessed through the stats attribute of the クローラー API, as illustrated by the examples in the 共通統計コレクタを使用する section below.

However, the Stats Collector is always available, so you can always import it in your module and use its API (to increment or set new stat keys), regardless of whether the stats collection is enabled or not. If it’s disabled, the API will still work but it won’t collect anything. This is aimed at simplifying the stats collector usage: you should spend no more than one line of code for collecting stats in your spider, Scrapy extension, or whatever code you’re using the Stats Collector from.

Another feature of the Stats Collector is that it’s very efficient (when enabled) and extremely efficient (almost unnoticeable) when disabled.

The Stats Collector keeps a stats table per open spider which is automatically opened when the spider is opened, and closed when the spider is closed.

共通統計コレクタを使用する

Access the stats collector through the stats attribute. Here is an example of an extension that access stats:

class ExtensionThatAccessStats(object):

    def __init__(self, stats):
        self.stats = stats

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.stats)

Set stat value:

stats.set_value('hostname', socket.gethostname())

Increment stat value:

stats.inc_value('custom_count')

Set stat value only if greater than previous:

stats.max_value('max_items_scraped', value)

Set stat value only if lower than previous:

stats.min_value('min_free_memory_percent', value)

Get stat value:

>>> stats.get_value('custom_count')
1

Get all stats:

>>> stats.get_stats()
{'custom_count': 1, 'start_time': datetime.datetime(2009, 7, 14, 21, 47, 28, 977139)}

利用可能な統計コレクタ

Besides the basic StatsCollector there are other Stats Collectors available in Scrapy which extend the basic Stats Collector. You can select which Stats Collector to use through the STATS_CLASS setting. The default Stats Collector used is the MemoryStatsCollector.

MemoryStatsCollector
class scrapy.statscollectors.MemoryStatsCollector

A simple stats collector that keeps the stats of the last scraping run (for each spider) in memory, after they’re closed. The stats can be accessed through the spider_stats attribute, which is a dict keyed by spider domain name.

This is the default Stats Collector used in Scrapy.

spider_stats

A dict of dicts (keyed by spider name) containing the stats of the last scraping run for each spider.

DummyStatsCollector
class scrapy.statscollectors.DummyStatsCollector

A Stats collector which does nothing but is very efficient (because it does nothing). This stats collector can be set via the STATS_CLASS setting, to disable stats collect in order to improve performance. However, the performance penalty of stats collection is usually marginal compared to other Scrapy workload like parsing pages.

Eメールを送る

Although Python makes sending e-mails relatively easy via the smtplib library, Scrapy provides its own facility for sending e-mails which is very easy to use and it’s implemented using Twisted non-blocking IO, to avoid interfering with the non-blocking IO of the crawler. It also provides a simple API for sending attachments and it’s very easy to configure, with a few settings.

簡単な例

MailSenderをインスタンス化するには2つの方法があります. 標準コンストラクタを使用してインスタンス化できます:

from scrapy.mail import MailSender
mailer = MailSender()

または, settings を尊重する, Scrapy設定オブジェクトを渡してインスタンス化することもできます:

mailer = MailSender.from_settings(settings)

そして、MailSenderを使って電子メールを送信する方法(添付ファイルなし):

mailer.send(to=["someone@example.com"], subject="Some subject", body="Some body", cc=["another@example.com"])

MailSender クラスリファレンス

MailSenderは, フレームワークの他の部分と同様に, Twisted non-blocking IO, を使用するため, Scrapyから電子メールを送信するために使用するのに望ましいクラスです.

class scrapy.mail.MailSender(smtphost=None, mailfrom=None, smtpuser=None, smtppass=None, smtpport=None)
パラメータ:
  • smtphost (str) – the SMTP host to use for sending the emails. If omitted, the MAIL_HOST setting will be used.
  • mailfrom (str) – the address used to send emails (in the From: header). If omitted, the MAIL_FROM setting will be used.
  • smtpuser – the SMTP user. If omitted, the MAIL_USER setting will be used. If not given, no SMTP authentication will be performed.
  • smtppass (str) – the SMTP pass for authentication.
  • smtpport (int) – the SMTP port to connect to
  • smtptls (boolean) – enforce using SMTP STARTTLS
  • smtpssl (boolean) – enforce using a secure SSL connection
classmethod from_settings(settings)

Instantiate using a Scrapy settings object, which will respect these Scrapy settings.

パラメータ:settings (scrapy.settings.Settings object) – the e-mail recipients
send(to, subject, body, cc=None, attachs=(), mimetype='text/plain', charset=None)

Send email to the given recipients.

パラメータ:
  • to (str or list of str) – the e-mail recipients
  • subject (str) – the subject of the e-mail
  • cc (str or list of str) – the e-mails to CC
  • body (str) – the e-mail body
  • attachs (iterable) – an iterable of tuples (attach_name, mimetype, file_object) where attach_name is a string with the name that will appear on the e-mail’s attachment, mimetype is the mimetype of the attachment and file_object is a readable file object with the contents of the attachment
  • mimetype (str) – the MIME type of the e-mail
  • charset (str) – the character encoding to use for the e-mail contents

メール設定

これらの設定は, MailSender クラスのデフォルトのコンストラクタ値を定義し, コードを記述することなくプロジェクト内の電子メール通知を構成するために使用できます(これらの拡張子と MailSender を使用するコード用).

MAIL_FROM

初期値: 'scrapy@localhost'

Eメールの送信に使用する送信者Eメール (From: ヘッダー).

MAIL_HOST

初期値: 'localhost'

Eメールの送信に使用するSMTPホスト.

MAIL_PORT

初期値: 25

Eメールの送信に使用するSMTPポート.

MAIL_USER

初期値: None

SMTP認証に使用するユーザー. 無効にすると、SMTP認証は実行されません.

MAIL_PASS

初期値: None

MAIL_USER とともにSMTP認証に使用するパスワード.

MAIL_TLS

初期値: False

STARTTLSを使用して強制する. STARTTLSは、既存の安全でない接続を取得し, SSL / TLSを使用して安全な接続にアップグレードする方法です.

MAIL_SSL

初期値: False

SSL暗号化接続を使用して接続を強制する.

Telnet コンソール

Scrapyには、Scrapy実行プロセスを検査および制御するための組み込みのTelnetコンソールが付属しています. elnetコンソールは、Scrapyプロセス内で実行されている通常のPythonシェルであるため、文字通りその中から何かを行うことができます.

Telnetコンソールは, デフォルトで有効になっている 組み込みScrapy拡張 ですが, 必要に応じて無効にすることもできます. 拡張機能の詳細については, Telnetコンソール拡張機能 を参照してください.

Telnetコンソールにアクセスする方法

Telnetコンソールは TELNETCONSOLE_PORT 設定で定義されているTCPポートをリッスンします. デフォルトでは 6023 に設定されています. コンソールにアクセスするには:

telnet localhost 6023
>>>

Windowsにデフォルトでインストールされるtelnetプログラムと、ほとんどのLinuxディストリビューションが必要です.

Telnetコンソールで使用可能な変数

elnetコンソールは、Scrapyプロセス内で動作する通常のPythonシェルに似ているので、新しいモジュールのインポートなど、何でもできます.

しかし, telnetコンソールには, 便宜上いくつかのデフォルト変数が定義されています:

Telnetコンソールの使用例

Telnetコンソールで実行できるタスクの例を以下に示します:

エンジンステータスを表示する

Scrapyエンジンの est() メソッドを使用すると、telnetコンソールを使用して状態をすばやく表示できます:

telnet localhost 6023
>>> est()
Execution engine status

time()-engine.start_time                        : 8.62972998619
engine.has_capacity()                           : False
len(engine.downloader.active)                   : 16
engine.scraper.is_idle()                        : False
engine.spider.name                              : followall
engine.spider_is_idle(engine.spider)            : False
engine.slot.closing                             : False
len(engine.slot.inprogress)                     : 16
len(engine.slot.scheduler.dqs or [])            : 0
len(engine.slot.scheduler.mqs)                  : 92
len(engine.scraper.slot.queue)                  : 0
len(engine.scraper.slot.active)                 : 0
engine.scraper.slot.active_size                 : 0
engine.scraper.slot.itemproc_size               : 0
engine.scraper.slot.needs_backout()             : False
Scrapyエンジンの一時停止, 再開, 停止

一時停止:

telnet localhost 6023
>>> engine.pause()
>>>

再開:

telnet localhost 6023
>>> engine.unpause()
>>>

停止:

telnet localhost 6023
>>> engine.stop()
Connection closed by foreign host.

Telnetコンソールシグナル

scrapy.extensions.telnet.update_telnet_vars(telnet_vars)

STelnetコンソールを開く直前に送信されます. この信号に接続して、telnetローカル名前空間で使用できる変数を追加、削除、または更新することができます. そのためには, ハンドラの telnet_vars を更新する必要があります.

パラメータ:telnet_vars (dict) – telnet の辞書型変数

Telnet設定

telnetコンソールの動作を制御する設定です:

TELNETCONSOLE_PORT

デフォルト: [6023, 6073]

Telnetコンソールに使用するポート範囲. None または 0 に設定すると, 動的に割り当てられたポートが使用されます.

TELNETCONSOLE_HOST

デフォルト: '127.0.0.1'

Telnetコンソールが監視すべきインターフェース

ウェブサービス

webservice は別のプロジェクトに移動されました.

以下にホストされています:

ロギング
Pythonの組み込みログをScrapyで使用する方法を学ぶ.
統計コレクション
スクレイピングクローラに関する統計情報を収集する.
Eメールを送る
特定のイベントが発生したときに電子メール通知を送信する.
Telnet コンソール
組み込みのPythonコンソールを使用して実行中のクローラを検査する.
ウェブサービス
Webサービスを使用してクローラを監視および制御する.

特定の問題の解決

よくある質問

ScrapyとBeautifulSoupまたはlxmlとの比較

BeautifulSouplxml はHTMLとXMLを解析するためのライブラリです. Scrapyは, Webサイトをクロールし, Webサイトからデータを抽出するWebスパイダーを作成するためのアプリケーションフレームワークです.

Scrapy には, データを抽出するためのメカニズム ( selectors と呼ばれる) が組み込まれていますが, 使い方が分かりやすい場合は BeautifulSoup (または lxml) をかんたんに使用することができます. 結局のところ, Pythonコードからインポートして使用できるライブラリを用いて解析しているだけです.

つまり,  BeautifulSoup (または lxml) と Scrapy を比較するのは jinja2Django を比較するのと同じことです.

Scrapy で BeautifulSoup を使うことができますか?

使用できます. 上記 のように, BeautifulSoup を Scrapy のコールバックでHTMLレスポンスを解析するために使用できます. レスポンスのボディを BeautifulSoup オブジェクトにフィードし, そこから必要なデータを抽出するだけです.

以下は, BeautifulSoup API を使用したスパイダーの例です. HTMLパーサーとして lxml を使用しています:

from bs4 import BeautifulSoup
import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"
    allowed_domains = ["example.com"]
    start_urls = (
        'http://www.example.com/',
    )

    def parse(self, response):
        # use lxml to get decent HTML parsing speed
        soup = BeautifulSoup(response.text, 'lxml')
        yield {
            "url": response.url,
            "title": soup.h1.string
        }

注釈

BeautifulSoup は複数の HTML/XML パーサーをサポートしています. BeautifulSoup 公式ドキュメント を参照してください.

Scrapy がサポートしているのは Python のどのバージョンですか?

Scrapy は Python 2.7 と Python 3.3+ での動作が確認されています。 Python 2.6 は Scrapy 0.20 からサポート対象から外されています。 Scrapy 1.1 から Python 3 サポートが開始されています。

注釈

Windows では Python 3 はサポートされていません。

Did Scrapy “steal” X from Django?

Probably, but we don’t like that word. We think Django is a great open source project and an example to follow, so we’ve used it as an inspiration for Scrapy.

We believe that, if something is already done well, there’s no need to reinvent it. This concept, besides being one of the foundations for open source and free software, not only applies to software but also to documentation, procedures, policies, etc. So, instead of going through each problem ourselves, we choose to copy ideas from those projects that have already solved them properly, and focus on the real problems we need to solve.

We’d be proud if Scrapy serves as an inspiration for other projects. Feel free to steal from us!

Scrapy はHTTPプロキシで動作しますか?

はい. HTTPプロキシダウンローダミドルウェアを介してHTTPプロキシのサポートが提供されています(Scrapy 0.8以降). HttpProxyMiddleware を参照してください.

異なるページの属性を持つアイテムをスクラップする方法はありますか?

追加データをコールバック関数に渡す を参照してください.

Scrapy がクラッシュする: No module named win32api

Twisted のバグ のために, pywin32 をインストールする必要があります.

スパイダーでユーザーログインをシミュレートする方法はありますか?

FormRequest.from_response()を使用してユーザーログインをシミュレートする 参照してください.

Scrapyは幅優先, 深さ優先どちらでクロールしますか?

By default, Scrapy uses a LIFO queue for storing pending requests, which basically means that it crawls in DFO order. This order is more convenient in most cases. If you do want to crawl in true BFO order, you can do it by setting the following settings:

DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'

私の Scrapy のクローラにはメモリリークがあります。どうしたら良いですか?

メモリリークのデバッグ を参照してください.

また, Pythonには, 漏れのない漏れ で説明されているメモリリークの問題があります.

どうしたらScrapyの消費メモリを少なくすることができますか?

直前の質問を参照してください.

スパイダーで基本的なHTTP認証を使用することはできますか?

はい, HttpAuthMiddleware を確認してください.

どのようにして英語のかわりに, 私の母国語でページをダウンロードするのですか?

Try changing the default Accept-Language request header by overriding the DEFAULT_REQUEST_HEADERS setting.

Scrapyプロジェクトの例はどこにありますか?

を参照してください.

プロジェクトを作成せずにスパイダーを実行することはできますか?

はい.  runspider コマンドを使用することで可能です. 例えば,  my_spider.py ファイルがすでに作成されているのであれば, 以下のように実行することができます:

scrapy runspider my_spider.py

詳細は, runspider コマンドを参照してください.

“Filtered offsite request”メッセージが表示されます. どうすれば修正できますか?

Those messages (logged with DEBUG level) don’t necessarily mean there is a problem, so you may not need to fix them.

Those messages are thrown by the Offsite Spider Middleware, which is a spider middleware (enabled by default) whose purpose is to filter out requests to domains outside the ones covered by the spider.

詳細については, OffsiteMiddleware を参照してください.

プロダクションでScrapyクローラーを導入するための推奨される方法はなんですか?

スパイダーのデプロイ 参照してください.

大量のエクスポートにJSONを使用することはできますか?

It’ll depend on how large your output is. See this warning in JsonItemExporter documentation.

シグナルハンドラから(Twised)遅延を返すことはできますか?

Some signals support returning deferreds from their handlers, others don’t. See the ビルトインシグナルリファレンス to know which ones.

応答ステータスコード999は何を意味しますか?

999 is a custom response status code used by Yahoo sites to throttle requests. Try slowing down the crawling speed by using a download delay of 2 (or higher) in your spider:

class MySpider(CrawlSpider):

    name = 'myspider'

    download_delay = 2

    # [ ... rest of the spider code ... ]

Or by setting a global download delay in your project with the DOWNLOAD_DELAY setting.

スパイダーのデバッグで pdb.set_trace() メソッドを呼ぶことはできますか?

Yes, but you can also use the Scrapy shell which allows you to quickly analyze (and even modify) the response being processed by your spider, which is, quite often, more useful than plain old pdb.set_trace().

詳細については,  応答を調べるためにシェルを起動する を参照してください.

スクレイピングしたデータを JSON/CSV/XML ファイルとして出力する簡単な方法はなんですか?

JSONファイルで出力する:

scrapy crawl myspider -o items.json

CSVファイルで出力する:

scrapy crawl myspider -o items.csv

XMLファイルで出力する:

scrapy crawl myspider -o items.xml

より詳細な情報は フィードのエクスポート を参照してください.

いくつかのフォームで使用される __VIEWSTATE パラメーターは一体何ですか?

The __VIEWSTATE parameter is used in sites built with ASP.NET/VB.NET. For more info on how it works see this page. Also, here’s an example spider which scrapes one of these sites.

大きな XML/CSV データフィードを解析する最適な方法は何ですか?

Parsing big feeds with XPath selectors can be problematic since they need to build the DOM of the entire feed in memory, and this can be quite slow and consume a lot of memory.

In order to avoid parsing all the entire feed at once in memory, you can use the functions xmliter and csviter from scrapy.utils.iterators module. In fact, this is what the feed spiders (see スパイダー) use under the cover.

Scrapy は自動的にクッキーを管理しますか?

はい, Scrapy はサーバーから送信されたCookieを受信して追跡し, 通常のWebブラウザーと同様に後続のリクエストでそれらを送信します.

詳細については,  リクエストとレスポンス と CookiesMiddleware を参照してください.

Scrapyから送受信されるクッキーを確認するにはどうすればよいですか?

COOKIES_DEBUG 設定を有効化してください.

スパイダーに止めるように指示するにはどうすればよいですか?

CloseSpider エクセプションをコールバックから発生させます. 詳細については CloseSpider を参照してください.

私のScrapyボットが禁止されるのを防ぐには?

BANされることを回避する 参照してください.

スパイダーの引数や設定を使用してスパイダーを構成する必要がありますか?

Both spider arguments and settings can be used to configure your spider. There is no strict rule that mandates to use one or the other, but settings are more suited for parameters that, once set, don’t change much, while spider arguments are meant to change more often, even on each spider run and sometimes are required for the spider to run at all (for example, to set the start url of a spider).

To illustrate with an example, assuming you have a spider that needs to log into a site to scrape data, and you only want to scrape data from a certain section of the site (which varies each time). In that case, the credentials to log in would be settings, while the url of the section to scrape would be a spider argument.

XML文書をスクラップしていて、XPathセレクタがアイテムを返さない

ネームスペースを削除する必要があるかもしれません. ネームスペースの削除 参照してください.

スパイダーのデバッグ

このドキュメントでは, スパイダーをデバッグするための最も一般的な手法について説明します. 以下のスパイダーを考えてみましょう:

import scrapy
from myproject.items import MyItem

class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = (
        'http://example.com/page1',
        'http://example.com/page2',
        )

    def parse(self, response):
        # collect `item_urls`
        for item_url in item_urls:
            yield scrapy.Request(item_url, self.parse_item)

    def parse_item(self, response):
        item = MyItem()
        # populate `item` fields
        # and extract item_details_url
        yield scrapy.Request(item_details_url, self.parse_details, meta={'item': item})

    def parse_details(self, response):
        item = response.meta['item']
        # populate more `item` fields
        return item

Basically this is a simple spider which parses two pages of items (the start_urls). Items also have a details page with additional information, so we use the meta functionality of Request to pass a partially populated item.

パースコマンド

The most basic way of checking the output of your spider is to use the parse command. It allows to check the behaviour of different parts of the spider at the method level. It has the advantage of being flexible and simple to use, but does not allow debugging code inside a method.

特定のURLからスクラップしたアイテムを表示するには:

$ scrapy parse --spider=myspider -c parse_item -d 2 <item_url>
[ ... scrapy log lines crawling example.com spider ... ]

>>> STATUS DEPTH LEVEL 2 <<<
# Scraped Items  ------------------------------------------------------------
[{'url': <item_url>}]

# Requests  -----------------------------------------------------------------
[]

Using the --verbose or -v option we can see the status at each depth level:

$ scrapy parse --spider=myspider -c parse_item -d 2 -v <item_url>
[ ... scrapy log lines crawling example.com spider ... ]

>>> DEPTH LEVEL: 1 <<<
# Scraped Items  ------------------------------------------------------------
[]

# Requests  -----------------------------------------------------------------
[<GET item_details_url>]


>>> DEPTH LEVEL: 2 <<<
# Scraped Items  ------------------------------------------------------------
[{'url': <item_url>}]

# Requests  -----------------------------------------------------------------
[]

Checking items scraped from a single start_url, can also be easily achieved using:

$ scrapy parse --spider=myspider -d 3 'http://example.com/page1'

Scrapy シェル

While the parse command is very useful for checking behaviour of a spider, it is of little help to check what happens inside a callback, besides showing the response received and the output. How to debug the situation when parse_details sometimes receives no item?

Fortunately, the shell is your bread and butter in this case (see 応答を調べるためにシェルを起動する):

from scrapy.shell import inspect_response

def parse_details(self, response):
    item = response.meta.get('item', None)
    if item:
        # populate more `item` fields
        return item
    else:
        inspect_response(response, self)

See also: 応答を調べるためにシェルを起動する.

ブラウザで開く

Sometimes you just want to see how a certain response looks in a browser, you can use the open_in_browser function for that. Here is an example of how you would use it:

from scrapy.utils.response import open_in_browser

def parse_details(self, response):
    if "item name" not in response.body:
        open_in_browser(response)

open_in_browser will open a browser with the response received by Scrapy at that point, adjusting the base tag so that images and styles are displayed properly.

ロギング

ロギングはスパイダーの実行に関する情報を得るためのもう1つの便利なオプションです. Although not as convenient, it comes with the advantage that the logs will be available in all future runs should they be necessary again:

def parse_details(self, response):
    item = response.meta.get('item', None)
    if item:
        # populate more `item` fields
        return item
    else:
        self.logger.warning('No item received for %s', response.url)

For more information, check the ロギング section.

スパイダーコントラクト

バージョン 0.15 で追加.

注釈

これは新機能(Scrapy 0.15で導入されました)であり、機能/ APIの軽微な更新の対象となる可能性があります. リリースノート を確認してください.

Testing spiders can get particularly annoying and while nothing prevents you from writing unit tests the task gets cumbersome quickly. Scrapy offers an integrated way of testing your spiders by the means of contracts.

This allows you to test each callback of your spider by hardcoding a sample url and check various constraints for how the callback processes the response. Each contract is prefixed with an @ and included in the docstring. See the following example:

def parse(self, response):
    """ This function parses a sample response. Some contracts are mingled
    with this docstring.

    @url http://www.amazon.com/s?field-keywords=selfish+gene
    @returns items 1 16
    @returns requests 0 0
    @scrapes Title Author Year Price
    """

このコールバックは, 次の3つの組み込みコントラクトを使用してテストされます:

class scrapy.contracts.default.UrlContract

This contract (@url) sets the sample url used when checking other contract conditions for this spider. This contract is mandatory. All callbacks lacking this contract are ignored when running the checks:

@url url
class scrapy.contracts.default.ReturnsContract

This contract (@returns) sets lower and upper bounds for the items and requests returned by the spider. The upper bound is optional:

@returns item(s)|request(s) [min [max]]
class scrapy.contracts.default.ScrapesContract

This contract (@scrapes) checks that all the items returned by the callback have the specified fields:

@scrapes field_1 field_2 ...

Use the check command to run the contract checks.

カスタムコントラクト

If you find you need more power than the built-in scrapy contracts you can create and load your own contracts in the project by using the SPIDER_CONTRACTS setting:

SPIDER_CONTRACTS = {
    'myproject.contracts.ResponseCheck': 10,
    'myproject.contracts.ItemValidate': 10,
}

Each contract must inherit from scrapy.contracts.Contract and can override three methods:

class scrapy.contracts.Contract(method, *args)
パラメータ:
  • method (function) – callback function to which the contract is associated
  • args (list) – list of arguments passed into the docstring (whitespace separated)
adjust_request_args(args)

This receives a dict as an argument containing default arguments for Request object. Must return the same or a modified version of it.

pre_process(response)

This allows hooking in various checks on the response received from the sample request, before it’s being passed to the callback.

post_process(output)

This allows processing the output of the callback. Iterators are converted listified before being passed to this hook.

Here is a demo contract which checks the presence of a custom header in the response received. Raise scrapy.exceptions.ContractFail in order to get the failures pretty printed:

from scrapy.contracts import Contract
from scrapy.exceptions import ContractFail

class HasHeaderContract(Contract):
    """ Demo contract which checks the presence of a custom header
        @has_header X-CustomHeader
    """

    name = 'has_header'

    def pre_process(self, response):
        for header in self.args:
            if header not in response.headers:
                raise ContractFail('X-CustomHeader not present')

一般的なプラクティス

このセクションでは, Scrapyを使用する際の一般的な方法について説明します. これらは, 多くの話題を網羅しており, 他の特定のセクションにはいるのはよくありません.

スクリプトから Scrapy を実行する

scrapy crawl コマンドで Scrapy を実行する従来の方法に加え, API を利用してスクリプトから Scrapy を実行することができます.

Scrapy は Twisted 非同期ネットワーキングライブラリの上に構築されているので, Twisted リアクタ内で実行する必要があります.

スパイダーを実行するために使用できる最初のユーティリティーは scrapy.crawler.CrawlerProcess です. このクラスは, Twisted リアクターを開始し, ロギング・シャットダウンハンドラを設定します. このクラスは, すべてのScrapyコマンドで使用されるクラスです.

ここで, 1つのスパイダーを実行する方法の例を示します.

import scrapy
from scrapy.crawler import CrawlerProcess

class MySpider(scrapy.Spider):
    # 独自のスパイダー定義
    ...

process = CrawlerProcess({
    'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'
})

process.crawl(MySpider)
process.start() # クロールが終了するまでスクリプトはここでブロックされます

CrawlerProcess ドキュメントをチェックして, 使用法の詳細を確認してください.

プロジェクトの内部にいる場合は, プロジェクト内のコンポーネントをインポートするために使用できる追加のヘルパーがいくつかあります. 名前を渡すスパイダーを CrawlerProcess に自動的にインポートし, get_project_settings を使用してプロジェクト設定で Settings インスタンスを取得することができます.

これは,  testspiders プロジェクトを例とし, 実行する方法の実例です.

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

process = CrawlerProcess(get_project_settings())

# 'followall' はプロジェクトのスパイダーの名前です.
process.crawl('followall', domain='scrapinghub.com')
process.start() # クロールが終了するまでスクリプトはここでブロックされます

クロールプロセスをより詳細に制御できるScrapyユーティリティとして scrapy.crawler.CrawlerRunner があります. このクラスは, いくつかのヘルパーをカプセル化して複数のクローラーを実行するかんたんなラッパーですが, 既存のリアクターを開始したり干渉したりすることはありません.

このクラスを使用して, リアクターはスパイダーをスケジュールした後に明示的に実行する必要があります. アプリケーションがすでにTwistedを使用していて, 同じリアクターでScrapyを実行する場合は, CrawlerProcess ではなく, CrawlerRunner を使用することをお勧めします.

スパイダーが完成した後, Twistedリアクターを手動でシャットダウンする必要があります. これは, CrawlerRunner.crawl メソッドによって返された遅延にコールバックを追加することで実現できます.

MySpiderの実行が終了した後, コールバックとともにリアクターを手動で停止する, 使用例を示します.

from twisted.internet import reactor
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider(scrapy.Spider):
    # 独自のスパイダー定義
    ...

configure_logging({'LOG_FORMAT': '%(levelname)s: %(message)s'})
runner = CrawlerRunner()

d = runner.crawl(MySpider)
d.addBoth(lambda _: reactor.stop())
reactor.run() # クロールが終了するまでスクリプトはここでブロックされます

同じプロセスで複数のスパイダーを実行する

デフォルトでは, Scrapy は scrapy crawl を実行するときにプロセスごとに1つのスパイダーを実行します. ただし, Scrapy は 内部 API を使用することでプロセスごとに複数のスパイダーを実行できます.

以下は, 複数のスパイダーを同時に実行する例です:

import scrapy
from scrapy.crawler import CrawlerProcess

class MySpider1(scrapy.Spider):
    # 一番目の独自のスパイダーの定義
    ...

class MySpider2(scrapy.Spider):
    # 二番目の独自のスパイダーの定義
    ...

process = CrawlerProcess()
process.crawl(MySpider1)
process.crawl(MySpider2)
process.start() # すべてのクロールジョブが終了するまでスクリプトはここでブロックされます

CrawlerRunner を使用した同様の例です:

import scrapy
from twisted.internet import reactor
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider1(scrapy.Spider):
    # 一番目の独自のスパイダーの定義
    ...

class MySpider2(scrapy.Spider):
    # 二番目の独自のスパイダーの定義
    ...

configure_logging()
runner = CrawlerRunner()
runner.crawl(MySpider1)
runner.crawl(MySpider2)
d = runner.join()
d.addBoth(lambda _: reactor.stop())

reactor.run() # すべてのクロールジョブが終了するまで, スクリプトはここでブロックされます

同様の例ですが, 遅延を連鎖させてスパイダーを順番に実行しています:

from twisted.internet import reactor, defer
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider1(scrapy.Spider):
    # 一番目の独自のスパイダーの定義
    ...

class MySpider2(scrapy.Spider):
    # 二番目の独自のスパイダーの定義
    ...

configure_logging()
runner = CrawlerRunner()

@defer.inlineCallbacks
def crawl():
    yield runner.crawl(MySpider1)
    yield runner.crawl(MySpider2)
    reactor.stop()

crawl()
reactor.run() # 最後のクロールコールが終了するまで, スクリプトはここでブロックされます

分散クロール

Scrapy は, 配布(マルチサーバー)方式でクロールを実行するための組み込み機能を提供していません. ただし, クロールを配布する方法はいくつかあり, その方法は配布方法によって異なります.

スパイダーがたくさんある場合, 負荷を分散させる明白な方法は, 多くのScrapydインスタンスをセットアップし, スパイダーをその中で実行することです.

多くのマシンで単一の(大きな)スパイダーを実行する場合は, 通常はクロールするURLを分割して別々のスパイダーに送信します. 具体的な例を次に示します:

まず, クロールするURLのリストを用意して, 別々のファイル/URLに入れます:

http://somedomain.com/urls-to-crawl/spider1/part1.list
http://somedomain.com/urls-to-crawl/spider1/part2.list
http://somedomain.com/urls-to-crawl/spider1/part3.list

次に, 3つのScrapydサーバーでスパイダーを実行します. スパイダーは, (spider) 引数 part にクロールするパーティションの番号を渡します:

curl http://scrapy1.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=1
curl http://scrapy2.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=2
curl http://scrapy3.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=3

BANされることを回避する

いくつかのウェブサイトでは, ボットがWebサイトをクロールするのを防ぐために, さまざまな洗練された手段を実装しています. これらの措置を回避することは非常に困難なことがあり, 特別なインフラストラクチャが必要な場合があります. ご不明な点がある場合は, 商用サポート にお問い合わせください.

これらの種類のサイトを扱う際に留意すべきヒントをいくつか紹介します:
  • ユーザーエージェントを, よく知られているブラウザのプールからローテーションします(Googleのリストを取得するにはGoogleを使用します)
  • 一部のサイトでは, クッキーを使用してボットの動作を特定する場合があるため, クッキーを無効にする ( COOKIES_ENABLED を参照してください).
  • ダウンロード遅延 (2 またはそれ以上) を使用する.  DOWNLOAD_DELAY 設定を参照してください.
  • 可能であれば, サイトに直接アクセスするのではなく, Google cache を使用してページを取得する
  • IPプールをローテーションさせ使用します。たとえば, 無料の Tor projectProxyMesh のような有料サービスです. また, あなた自身のプロキシを添付できるスーパープロキシである scrapoxy のようなオープンソースのプロジェクトが有ります.
  • 内部的に禁止を回避する高度に分散されたダウンローダを使用するので, クリーンなページの解析に集中することができます. そのようなダウンローダの一例に Crawlera があります.

それでもあなたのボットが禁止されるのを防ぐことができない場合は, 商用サポート に連絡することを検討してください.

ブロードクロール

Scrapy のデフォルトは, 特定のサイトをクロールするために最適化されています. そのサイトは, 単一の Scrapy スパイダーによって処理されることがよくありますが, これは必須ではありません(たとえば, スローされた任意のサイトを処理するスパイダーがあります).

この「フォーカスクロール」に加えて, ドメインのクロールが完了するまでの間に停止するのではなく, 時間や他の任意の制約によってのみ制限されている(潜在的に無制限の)多数のクロールの一般的なタイプがあります. 実行するリクエストがもうないとき これらは「ブロードクロール」と呼ばれ, 検索エンジンで採用されている一般的なクローラです.

これらは, ブロードクロールでよく見られる共通のプロパティです:

  • 特定のサイトセットではなく, 多くのドメインを無限にクロールする
  • ドメインをクロールする必要はない. そのため, クロールを時間またはクロールされたページ数で制限することは実用的ではない(または不可能)
  • データはしばしば別の段階で後処理されるため, 論理的にはより単純である(多くの抽出規則を持つ非常に複雑なスパイダーとは対照的す)
  • 同時に複数のドメインをクロールするため, 特定のサイトの制約に制限されずに高速なクロール速度を実現できる (各サイトは丁寧さを考慮してゆっくりとクロールされるが, 多くのサイトは並行してクロールされる)

上記のように, Scrapy のデフォルト設定は, ブロードクロールではなく, フォーカスクロールに対して最適化されています. しかし, Scrapy は非同期アーキテクチャであるため, 高速な広範なクロールを実行するのに非常に適しています. このページでは, ブロードクロールを行うために Scrapy を使用する際に留意する必要がある事項と, 効果的なブロードクロールを達成するために調整する Scrapy 設定の具体的な提案をまとめています.

並行性を高める

並行処理は, 並列処理されるリクエストの数です. グローバルごとの制限とドメインごとの制限があります.

Scrapy の既定のグローバル同時実行制限は, 多くの異なるドメインを同時にクロールするのには適していないため, 増やすべきです. どの程度増加させるかは, クローラで使用可能な CPU の量に依存します. 良い出発点は ``100``ですが, 調べる最も良い方法は, いくつかの試行を行い, あなたの Scrapy プロセスがどの CPU に束縛されているかを特定することです. 最適なパフォーマンスを得るには, CPU 使用率が80〜90%の並行処理を選択する必要があります.

グローバルな同時使用を増やすには:

CONCURRENT_REQUESTS = 100

Twisted のIOスレッドプールの最大サイズを増やす

現在のところ, Scrapy は DNS 解決をスレッドプールの使用でブロックします. 並行性レベルが高いと, クロールが遅くなったり, DNS リゾルバのタイムアウトが起こる可能性があります. 解決策としては, DNSクエリを処理するスレッドの数を増やすことです. DNS キューは, 接続の確立と全体的なクロールのスピードアップがされ, 速く処理されます.

スレッドプールの最大サイズを増やすには:

REACTOR_THREADPOOL_MAXSIZE = 20

独自のDNSの設定

複数のクロールプロセスと1つのセントラル DNS がある場合, DNS サーバーに対する DoS 攻撃のように動作し, ネットワーク全体の速度を低下させたり, コンピュータをブロックすることさえあります. このセットアップを避けるために, ローカルキャッシュを持つ独自のDNSサーバーと, OpenDNS や Verizon のような大規模な DNS へのアップストリームをおこなってください.

ログレベルを下げる

ブロードクロールを行うときには, 取得したクロール速度と見つかったエラーにのみ関心があることがよくあります. これらの統計値は, INFO ログレベルを使用して Scrapy によって報告されます. CPU(およびログのストレージ要件)を節約するため, 本番環境での広範なクロールを事前実行するときは DEBUG ログレベルを使用しないでください. クローラを開発するときには DEBUG レベルを使用すると, うまくいくかもしれません.

ログレベルの使用を設定するには:

LOG_LEVEL = 'INFO'

クッキーを無効にする

本当に必要な場合を除き, クッキーを無効にしてください. クッキーはブロードクロール(検索エンジンのクローラーは無視する)の際には必要ではないことが多く, 無効にすることで, CPU サイクルを節約し, Scrapy クローラーのメモリーフットプリントを削減することでパフォーマンスを向上させます.

クッキーを無効にするには:

COOKIES_ENABLED = False

リトライを無効にする

失敗したHTTPリクエストを再試行すると, 特に失敗の原因が非常に遅い ときにクロールが大幅に遅くなるため, 結果タイムアウトエラーが何度も再試行され, 他のドメインでクローラの容量を再利用できないようになります.

リトライを無効にするには:

RETRY_ENABLED = False

ダウンロードタイムアウトを減らす

非常に遅い接続からクロールしない限り(ブロードクロールの場合はそうでないはずです), ダウンロードのタイムアウトを短縮することで, スタックされたリクエストがすぐに破棄され, 次のリクエストを処理できるようになります.

ダウンロードタイムアウトを減らすには:

DOWNLOAD_TIMEOUT = 15

リダイレクトを無効にする

リダイレクトに関心がない限り, リダイレクトを無効にすることを検討してください. ブロードクロールを行うときは, リダイレクトを保存し, 後でサイトに再度アクセスしクロールするときにリダイレクトを解決するのが一般的です. これは, クロールバッチごとに要求の数を一定に保つのに役立ちます. そうしないと, リダイレクトループによってクローラーが特定のドメインのリソースを多すぎるものにする可能性があります.

リダイレクトを無効にするには:

REDIRECT_ENABLED = False

「Ajaxクロール可能なページ」のクロールを有効にする

一部のページ(2013年の実績データに基づいて最大1%)は, クロール可能な ajax として宣言しています. つまり, 通常はAJAX経由でのみ利用可能なプレーンなHTMLバージョンのコンテンツを提供します. 2つの方法でそれを示すことができます:

  1. URL に #! を使用する - これは一般的な方法です.
  2. 特別なメタタグを使用する - この方法は「メイン」「インデックス」ウェブサイトページで使用されます.

Scrapy は, (1) のみ自動的にハンドリングします. (2) をハンドリングするには AjaxCrawlMiddleware を有効化してください:

AJAXCRAWL_ENABLED = True

ブロードクロールを行う場合, 多くの「インデックス」Webページをクロールするのが一般的です. よって, AjaxCrawlMiddleware は正しくクロールすることができます. フォーマンスのオーバーヘッドがあるため, デフォルトではオフになっています. フォーカスクロールで有効にする意味はあまりありません.

スクレイピングにFireFoxを使用する

Here is a list of tips and advice on using Firefox for scraping, along with a list of useful Firefox add-ons to ease the scraping process.

ライブブラウザDOMの検査に関する警告

Since Firefox add-ons operate on a live browser DOM, what you’ll actually see when inspecting the page source is not the original HTML, but a modified one after applying some browser clean up and executing Javascript code. Firefox, in particular, is known for adding <tbody> elements to tables. Scrapy, on the other hand, does not modify the original page HTML, so you won’t be able to extract any data if you use <tbody> in your XPath expressions.

Therefore, you should keep in mind the following things when working with Firefox and XPath:

  • Disable Firefox Javascript while inspecting the DOM looking for XPaths to be used in Scrapy
  • Never use full XPath paths, use relative and clever ones based on attributes (such as id, class, width, etc) or any identifying features like contains(@href, 'image').
  • Never include <tbody> elements in your XPath expressions unless you really know what you’re doing

スクレイピングに便利なFirefoxアドオン

Firebug

Firebug is a widely known tool among web developers and it’s also very useful for scraping. In particular, its Inspect Element feature comes very handy when you need to construct the XPaths for extracting data because it allows you to view the HTML code of each page element while moving your mouse over it.

See スクレイピングにFirebugを使用する for a detailed guide on how to use Firebug with Scrapy.

XPather

XPather allows you to test XPath expressions directly on the pages.

XPath Checker

XPath Checker is another Firefox add-on for testing XPaths on your pages.

Tamper Data

Tamper Data is a Firefox add-on which allows you to view and modify the HTTP request headers sent by Firefox. Firebug also allows to view HTTP headers, but not to modify them.

Firecookie

Firecookie makes it easier to view and manage cookies. You can use this extension to create a new cookie, delete existing cookies, see a list of cookies for the current site, manage cookies permissions and a lot more.

スクレイピングにFirebugを使用する

注釈

Google Directory, the example website used in this guide is no longer available as it has been shut down by Google. The concepts in this guide are still valid though. If you want to update this guide to use a new (working) site, your contribution will be more than welcome!. See Scrapy に貢献する for information on how to do so.

イントロダクション

This document explains how to use Firebug (a Firefox add-on) to make the scraping process easier and more fun. For other useful Firefox add-ons see スクレイピングに便利なFirefoxアドオン. There are some caveats with using Firefox add-ons to inspect pages, see ライブブラウザDOMの検査に関する警告.

In this example, we’ll show how to use Firebug to scrape data from the Google Directory, which contains the same data as the Open Directory Project used in the tutorial but with a different face.

Firebug comes with a very useful feature called Inspect Element which allows you to inspect the HTML code of the different page elements just by hovering your mouse over them. Otherwise you would have to search for the tags manually through the HTML body which can be a very tedious task.

In the following screenshot you can see the Inspect Element tool in action.

Inspecting elements with Firebug

At first sight, we can see that the directory is divided in categories, which are also divided in subcategories.

However, it seems that there are more subcategories than the ones being shown in this page, so we’ll keep looking:

Inspecting elements with Firebug

As expected, the subcategories contain links to other subcategories, and also links to actual websites, which is the purpose of the directory.

フォローするリンクを取得する

By looking at the category URLs we can see they share a pattern:

Once we know that, we are able to construct a regular expression to follow those links. For example, the following one:

directory\.google\.com/[A-Z][a-zA-Z_/]+$

So, based on that regular expression we can create the first crawling rule:

Rule(LinkExtractor(allow='directory.google.com/[A-Z][a-zA-Z_/]+$', ),
    'parse_category',
    follow=True,
),

The Rule object instructs CrawlSpider based spiders how to follow the category links. parse_category will be a method of the spider which will process and extract data from those pages.

This is how the spider would look so far:

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

class GoogleDirectorySpider(CrawlSpider):
    name = 'directory.google.com'
    allowed_domains = ['directory.google.com']
    start_urls = ['http://directory.google.com/']

    rules = (
        Rule(LinkExtractor(allow='directory\.google\.com/[A-Z][a-zA-Z_/]+$'),
            'parse_category', follow=True,
        ),
    )

    def parse_category(self, response):
        # write the category page data extraction code here
        pass

データの抽出

Now we’re going to write the code to extract data from those pages.

With the help of Firebug, we’ll take a look at some page containing links to websites (say http://directory.google.com/Top/Arts/Awards/) and find out how we can extract those links using Selectors. We’ll also use the Scrapy shell to test those XPath’s and make sure they work as we expect.

Inspecting elements with Firebug

As you can see, the page markup is not very descriptive: the elements don’t contain id, class or any attribute that clearly identifies them, so we’ll use the ranking bars as a reference point to select the data to extract when we construct our XPaths.

After using FireBug, we can see that each link is inside a td tag, which is itself inside a tr tag that also contains the link’s ranking bar (in another td).

So we can select the ranking bar, then find its parent (the tr), and then finally, the link’s td (which contains the data we want to scrape).

This results in the following XPath:

//td[descendant::a[contains(@href, "#pagerank")]]/following-sibling::td//a

It’s important to use the Scrapy shell to test these complex XPath expressions and make sure they work as expected.

Basically, that expression will look for the ranking bar’s td element, and then select any td element who has a descendant a element whose href attribute contains the string #pagerank

Of course, this is not the only XPath, and maybe not the simpler one to select that data. Another approach could be, for example, to find any font tags that have that grey colour of the links,

Finally, we can write our parse_category() method:

def parse_category(self, response):
    # The path to website links in directory page
    links = response.xpath('//td[descendant::a[contains(@href, "#pagerank")]]/following-sibling::td/font')

    for link in links:
        item = DirectoryItem()
        item['name'] = link.xpath('a/text()').extract()
        item['url'] = link.xpath('a/@href').extract()
        item['description'] = link.xpath('font[2]/text()').extract()
        yield item

Be aware that you may find some elements which appear in Firebug but not in the original HTML, such as the typical case of <tbody> elements.

or tags which Therefer in page HTML sources may on Firebug inspects the live DOM

メモリリークのデバッグ

Scrapyでは, リクエスト, レスポンス, アイテムなどのオブジェクトの寿命は制限されています. これらは作成され, しばらく使用された後, 最終的に破棄されます.

全オブジェクトの中でも, Requestはおそらく最も寿命の長いものです. スケジューラのキューで待機してから処理するまでです. 詳細は, アーキテクチャの概要 を参照してください.

これらの Scrapy オブジェクトは(かなり長い)存続期間を持つため, 適切に解放されずメモリに蓄積され, 「メモリリーク」を引き起こすリスクが常にあります.

メモリリークをデバッグするのに役立つように, Scrapyには trackref というオブジェクト参照をトラッキングするためのメカニズムが組み込まれています. さらに詳細なメモリデバッグには Guppy というサードパーティのライブラリを使用することもできます. どちらのツールも Telnet コンソール から使用する必要があります.

メモリリークの一般的な原因

頻繁に, Scrapy 開発者はリクエストで参照されるオブジェクト(たとえば, meta 属性または要求コールバック関数を使用して)を渡し, これらの参照されたオブジェクトの有効期間をリクエストの存続期間として効果的に制限します. これは, 今のところScrapyプロジェクトでのメモリリークの最も一般的な原因であり, 新規参入者がデバッグするのは非常に難しいものです.

大きなプロジェクトでは, 一般的にスパイダーは様々な人によって書かれており, そのスパイダーのいくつかは「リーク」の原因になりえ, 他の(よく書かれた)スパイダーの部分に同時に影響を及ぼし, 全体的なクロールプロセスに影響を及ぼす可能性があります.

また, (以前に割り当てられた)リソースを適切に解放していない場合は, 作成したカスタムミドルウェア, パイプライン, または拡張機能からリークが発生する可能性があります. たとえば,  spider_opened にリソースを割り当てても spider_closed でリソースを解放しないと, プロセスごとに複数のスパイダーを実行 していると問題が発生する可能性があります.

リクエストが多すぎ?

デフォルトでは, Scrapyはリクエストのキューをメモリ内に保持します. これは, Request オブジェクトおよび (例えば, meta 内の) リクエスト属性内で参照されるすべてのオブジェクトを含みます. 必ずしもリークとはいえませんが, これには多くのメモリを必要とします. 永続的なジョブ・キュー を有効にすると, メモリー使用量を制御できます.

trackref でメモリリークのデバッグをする

trackref は, メモリリークの最も一般的なケースをデバッグするために Scrapy によって提供されるモジュールです. これは, 基本的にすべての実際のリクエスト, レスポンス, アイテム, およびセレクタオブジェクトへの参照を追跡します.

Telnet コンソールに入り, print_live_refs() 関数のエイリアスである prefs() 関数を使って, 現在生きているオブジェクトの数(上記のクラスのうちどれか)を調べることができます:

telnet localhost 6023

>>> prefs()
Live References

ExampleSpider                       1   oldest: 15s ago
HtmlResponse                       10   oldest: 1s ago
Selector                            2   oldest: 0s ago
FormRequest                       878   oldest: 7s ago

ご覧のとおり, このレポートには, 各クラスの中で最も古いオブジェクトの「年齢」も表示されます. プロセスごとに複数のスパイダーを実行している場合は, 最も古いリクエスト, またはレスポンスを調べることで, どのスパイダーがリークを起こしているか把握できます. Telnetコンソールから get_oldest() 関数を使用して, 各クラスの最も古いオブジェクトを取得できます.

どのオブジェクトが追跡されるの?

trackrefs によって追跡されるオブジェクトは, すべてこれらのクラス(およびそのすべてのサブクラス)のものです:

実際の例

仮想のメモリリークの具体例を見てみましょう. このような行を持つスパイダーがいくつかあるとします:

return Request("http://www.somenastyspider.com/product.php?pid=%d" % product_id,
    callback=self.parse, meta={referer: response})

この行は, リクエスト中にレスポンスの参照を渡しています. このレスポンスの参照は, レスポンスの存続期間とリクエストの存続期間とを結びつけ, メモリリークを引き起こします.

trackref ツールを使用して, 原因を発見する方法を見てみましょう.

クローラが数分間実行され, メモリ使用量が大きく増えたことがわかったら, Telnet コンソールに入り, 生きている参照を確認します:

>>> prefs()
Live References

SomenastySpider                     1   oldest: 15s ago
HtmlResponse                     3890   oldest: 265s ago
Selector                            2   oldest: 0s ago
Request                          3878   oldest: 250s ago

レスポンスはリクエストと比較して, 短い寿命でなければならないので, 非常に多くの生きているレスポンスがあること(そしてそれらは古くなっています)は間違いなく疑わしいです. レスポンスの数とリクエストの数がほぼ同じなので, 何らかの原因で結び付けられているように見えます. この結果から, スパイダーのコードをチェックして, リークを生成している厄介な行を発見することができます(リクエスト内でレスポンス参照を渡す).

生きているオブジェクトに関する追加情報が役立つ場合があります. 最も古いリクエストを確認しましょう:

>>> from scrapy.utils.trackref import get_oldest
>>> r = get_oldest('HtmlResponse')
>>> r.url
'http://www.somenastyspider.com/product.php?pid=123'

最も古いものだけを取得するのではなく, すべてのオブジェクトを繰り返し処理したい場合は scrapy.utils.trackref.iter_all() 関数を使用します:

>>> from scrapy.utils.trackref import iter_all
>>> [r.url for r in iter_all('HtmlResponse')]
['http://www.somenastyspider.com/product.php?pid=123',
 'http://www.somenastyspider.com/product.php?pid=584',
...
スパイダーが多すぎる?

プロジェクトの並列実行数が多すぎると, prefs() の出力を読みにくくなる可能性があります. このため, この関数には, 特定のクラス(およびすべてのサブクラス)を無視できる ignore 引数があります. たとえば, これはスパイダーの参照を表示しません:

>>> from scrapy.spiders import Spider
>>> prefs(ignore=Spider)
scrapy.utils.trackref モジュール

trackref モジュールで利用できる関数は次のとおりです.

class scrapy.utils.trackref.object_ref

trackref モジュールを使用してライブインスタンスをトラッキングする場合は, (オブジェクトではなく)このクラスから継承します.

scrapy.utils.trackref.print_live_refs(class_name, ignore=NoneType)

生きている参照のレポートをクラス名でグループ化して出力します.

パラメータ:ignore (class または classe のタプル) – 与えられた場合, 指定されたクラス(またはクラスのタプル)からのすべてのオブジェクトは無視されます.
scrapy.utils.trackref.get_oldest(class_name)

指定されたクラス名で生存しているすべてのオブジェクトのイテレータを返します. 見つからない場合は None を返します. まず, print_live_refs() を使用して, クラス名ごとに追跡されたすべてのライブオブジェクトのリストを取得します.

scrapy.utils.trackref.iter_all(class_name)

指定されたクラス名で生存しているすべてのオブジェクトのイテレータを返します. 見つからない場合は None を返します. まず, print_live_refs() を使用して, クラス名ごとに追跡されたすべてのライブオブジェクトのリストを取得します.

Guppy でメモリリークのデバッグをする

trackref はメモリリークを追跡する非常に便利なメカニズムを提供しますが, メモリリーク(リクエスト, レスポンス, アイテム, セレクタ)の原因となる可能性の高いオブジェクトのみを追跡します. しかし, メモリリークが他の(多かれ少なかれわかりにくい)オブジェクトから来る場合もあります. trackref を使ってリークを見つけることができない場合は, もう一つのリソース, Guppy ライブラリ があります.

pip を使用している場合は, 次のコマンドでGuppyをインストールできます:

pip install guppy

また, Telnet コンソールには, Guppy ヒープオブジェクトにアクセスするための組み込みショートカット(hpy) が付属しています. 以下は, Guppyを使ってヒープ内で利用可能なすべてのPythonオブジェクトを表示する例です:

>>> x = hpy.heap()
>>> x.bytype
Partition of a set of 297033 objects. Total size = 52587824 bytes.
 Index  Count   %     Size   % Cumulative  % Type
     0  22307   8 16423880  31  16423880  31 dict
     1 122285  41 12441544  24  28865424  55 str
     2  68346  23  5966696  11  34832120  66 tuple
     3    227   0  5836528  11  40668648  77 unicode
     4   2461   1  2222272   4  42890920  82 type
     5  16870   6  2024400   4  44915320  85 function
     6  13949   5  1673880   3  46589200  89 types.CodeType
     7  13422   5  1653104   3  48242304  92 list
     8   3735   1  1173680   2  49415984  94 _sre.SRE_Pattern
     9   1209   0   456936   1  49872920  95 scrapy.http.headers.Headers
<1676 more rows. Type e.g. '_.more' to view.>

ほとんどのスペースは dict によって使用されていることがわかります. これらの dict がどの属性から参照されているかを確認したい場合は, 以下のようにします:

>>> x.bytype[0].byvia
Partition of a set of 22307 objects. Total size = 16423880 bytes.
 Index  Count   %     Size   % Cumulative  % Referred Via:
     0  10982  49  9416336  57   9416336  57 '.__dict__'
     1   1820   8  2681504  16  12097840  74 '.__dict__', '.func_globals'
     2   3097  14  1122904   7  13220744  80
     3    990   4   277200   2  13497944  82 "['cookies']"
     4    987   4   276360   2  13774304  84 "['cache']"
     5    985   4   275800   2  14050104  86 "['meta']"
     6    897   4   251160   2  14301264  87 '[2]'
     7      1   0   196888   1  14498152  88 "['moduleDict']", "['modules']"
     8    672   3   188160   1  14686312  89 "['cb_kwargs']"
     9     27   0   155016   1  14841328  90 '[1]'
<333 more rows. Type e.g. '_.more' to view.>

ご覧のとおり, Guppy モジュールは非常に強力ですが, Python についての深い知識も必要です. Guppyの詳細については, Guppy ドキュメント を参照してください.

漏れのない漏れ

場合によっては, Scrapy プロセスのメモリ使用量が増加するだけで, 減少しないことがあります. この場合残念なことに, Scrapy または, あなたのプロジェクトどちらにも, メモリリークが発生する可能性があります. これは(あまりよく知られていない)Python の問題で, Python がリリースしたメモリをオペレーティングシステムに返さないことにより発生します. 詳細については, 以下を参照してください:

この記事 で詳しく述べられている Evan Jones が提案した改良点は, Python 2.5でマージされましたが, これは問題が軽減されただけで, 完全に修正されたわけではありません. 以下は, 記事の引用です:

Unfortunately, this patch can only free an arena if there are no more objects allocated in it anymore. This means that fragmentation is a large issue. An application could have many megabytes of free memory, scattered throughout all the arenas, but it will be unable to free any of it. This is a problem experienced by all memory allocators. The only way to solve it is to move to a compacting garbage collector, which is able to move objects in memory. This would require significant changes to the Python interpreter.

メモリ消費を合理的に保つために, ジョブを複数の小さなジョブに分割するか, 永続的なジョブ・キュー を有効にし, スパイダーを停止/開始するすることが望ましいです.

ファイルと画像のダウンロードと処理

Scrapy provides reusable item pipelines for downloading files attached to a particular item (for example, when you scrape products and also want to download their images locally). These pipelines share a bit of functionality and structure (we refer to them as media pipelines), but typically you’ll either use the Files Pipeline or the Images Pipeline.

Both pipelines implement these features:

  • Avoid re-downloading media that was downloaded recently
  • Specifying where to store the media (filesystem directory, Amazon S3 bucket)

The Images Pipeline has a few extra functions for processing images:

  • Convert all downloaded images to a common format (JPG) and mode (RGB)
  • Thumbnail generation
  • Check images width/height to make sure they meet a minimum constraint

The pipelines also keep an internal queue of those media URLs which are currently being scheduled for download, and connect those responses that arrive containing the same media to that queue. This avoids downloading the same media more than once when it’s shared by several items.

ファイルパイプラインの使用

The typical workflow, when using the FilesPipeline goes like this:

  1. In a Spider, you scrape an item and put the URLs of the desired into a file_urls field.
  2. The item is returned from the spider and goes to the item pipeline.
  3. When the item reaches the FilesPipeline, the URLs in the file_urls field are scheduled for download using the standard Scrapy scheduler and downloader (which means the scheduler and downloader middlewares are reused), but with a higher priority, processing them before other pages are scraped. The item remains “locked” at that particular pipeline stage until the files have finish downloading (or fail for some reason).
  4. When the files are downloaded, another field (files) will be populated with the results. This field will contain a list of dicts with information about the downloaded files, such as the downloaded path, the original scraped url (taken from the file_urls field) , and the file checksum. The files in the list of the files field will retain the same order of the original file_urls field. If some file failed downloading, an error will be logged and the file won’t be present in the files field.

画像パイプラインの使用

Using the ImagesPipeline is a lot like using the FilesPipeline, except the default field names used are different: you use image_urls for the image URLs of an item and it will populate an images field for the information about the downloaded images.

The advantage of using the ImagesPipeline for image files is that you can configure some extra functions like generating thumbnails and filtering the images based on their size.

The Images Pipeline uses Pillow for thumbnailing and normalizing images to JPEG/RGB format, so you need to install this library in order to use it. Python Imaging Library (PIL) should also work in most cases, but it is known to cause troubles in some setups, so we recommend to use Pillow instead of PIL.

メディアパイプラインの有効化

To enable your media pipeline you must first add it to your project ITEM_PIPELINES setting.

For Images Pipeline, use:

ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}

For Files Pipeline, use:

ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}

注釈

You can also use both the Files and Images Pipeline at the same time.

Then, configure the target storage setting to a valid value that will be used for storing the downloaded images. Otherwise the pipeline will remain disabled, even if you include it in the ITEM_PIPELINES setting.

For the Files Pipeline, set the FILES_STORE setting:

FILES_STORE = '/path/to/valid/dir'

For the Images Pipeline, set the IMAGES_STORE setting:

IMAGES_STORE = '/path/to/valid/dir'

サポートされているストレージ

File system is currently the only officially supported storage, but there is also support for storing files in Amazon S3.

ファイルシステムストレージ

The files are stored using a SHA1 hash of their URLs for the file names.

For example, the following image URL:

http://www.example.com/image.jpg

Whose SHA1 hash is:

3afec3b4765f8f0a07b78f98c07b83f013567a0a

Will be downloaded and stored in the following file:

<IMAGES_STORE>/full/3afec3b4765f8f0a07b78f98c07b83f013567a0a.jpg

Where:

  • <IMAGES_STORE> is the directory defined in IMAGES_STORE setting for the Images Pipeline.
  • full is a sub-directory to separate full images from thumbnails (if used). For more info see 画像のサムネイル生成.
Amazon S3 ストレージ

FILES_STORE and IMAGES_STORE can represent an Amazon S3 bucket. Scrapy will automatically upload the files to the bucket.

For example, this is a valid IMAGES_STORE value:

IMAGES_STORE = 's3://bucket/images'

You can modify the Access Control List (ACL) policy used for the stored files, which is defined by the FILES_STORE_S3_ACL and IMAGES_STORE_S3_ACL settings. By default, the ACL is set to private. To make the files publicly available use the public-read policy:

IMAGES_STORE_S3_ACL = 'public-read'

For more information, see canned ACLs in the Amazon S3 Developer Guide.

使用例

In order to use a media pipeline first, enable it.

Then, if a spider returns a dict with the URLs key (file_urls or image_urls, for the Files or Images Pipeline respectively), the pipeline will put the results under respective key (files or images).

If you prefer to use Item, then define a custom item with the necessary fields, like in this example for Images Pipeline:

import scrapy

class MyItem(scrapy.Item):

    # ... other item fields ...
    image_urls = scrapy.Field()
    images = scrapy.Field()

If you want to use another field name for the URLs key or for the results key, it is also possible to override it.

For the Files Pipeline, set FILES_URLS_FIELD and/or FILES_RESULT_FIELD settings:

FILES_URLS_FIELD = 'field_name_for_your_files_urls'
FILES_RESULT_FIELD = 'field_name_for_your_processed_files'

For the Images Pipeline, set IMAGES_URLS_FIELD and/or IMAGES_RESULT_FIELD settings:

IMAGES_URLS_FIELD = 'field_name_for_your_images_urls'
IMAGES_RESULT_FIELD = 'field_name_for_your_processed_images'

If you need something more complex and want to override the custom pipeline behaviour, see メディアパイプラインの拡張.

If you have multiple image pipelines inheriting from ImagePipeline and you want to have different settings in different pipelines you can set setting keys preceded with uppercase name of your pipeline class. E.g. if your pipeline is called MyPipeline and you want to have custom IMAGES_URLS_FIELD you define setting MYPIPELINE_IMAGES_URLS_FIELD and your custom settings will be used.

その他の機能

ファイルの有効期限

The Image Pipeline avoids downloading files that were downloaded recently. To adjust this retention delay use the FILES_EXPIRES setting (or IMAGES_EXPIRES, in case of Images Pipeline), which specifies the delay in number of days:

# 120 days of delay for files expiration
FILES_EXPIRES = 120

# 30 days of delay for images expiration
IMAGES_EXPIRES = 30

The default value for both settings is 90 days.

If you have pipeline that subclasses FilesPipeline and you’d like to have different setting for it you can set setting keys preceded by uppercase class name. E.g. given pipeline class called MyPipeline you can set setting key:

MYPIPELINE_FILES_EXPIRES = 180

and pipeline class MyPipeline will have expiration time set to 180.

画像のサムネイル生成

The Images Pipeline can automatically create thumbnails of the downloaded images.

In order use this feature, you must set IMAGES_THUMBS to a dictionary where the keys are the thumbnail names and the values are their dimensions.

For example:

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
}

When you use this feature, the Images Pipeline will create thumbnails of the each specified size with this format:

<IMAGES_STORE>/thumbs/<size_name>/<image_id>.jpg

Where:

  • <size_name> is the one specified in the IMAGES_THUMBS dictionary keys (small, big, etc)
  • <image_id> is the SHA1 hash of the image url

Example of image files stored using small and big thumbnail names:

<IMAGES_STORE>/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
<IMAGES_STORE>/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg

The first one is the full image, as downloaded from the site.

小さな画像をフィルタリングする

When using the Images Pipeline, you can drop images which are too small, by specifying the minimum allowed size in the IMAGES_MIN_HEIGHT and IMAGES_MIN_WIDTH settings.

For example:

IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110

注釈

The size constraints don’t affect thumbnail generation at all.

It is possible to set just one size constraint or both. When setting both of them, only images that satisfy both minimum sizes will be saved. For the above example, images of sizes (105 x 105) or (105 x 200) or (200 x 105) will all be dropped because at least one dimension is shorter than the constraint.

By default, there are no size constraints, so all images are processed.

メディアパイプラインの拡張

See here the methods that you can override in your custom Files Pipeline:

class scrapy.pipelines.files.FilesPipeline
get_media_requests(item, info)

As seen on the workflow, the pipeline will get the URLs of the images to download from the item. In order to do this, you can override the get_media_requests() method and return a Request for each file URL:

def get_media_requests(self, item, info):
    for file_url in item['file_urls']:
        yield scrapy.Request(file_url)

Those requests will be processed by the pipeline and, when they have finished downloading, the results will be sent to the item_completed() method, as a list of 2-element tuples. Each tuple will contain (success, file_info_or_error) where:

  • success is a boolean which is True if the image was downloaded successfully or False if it failed for some reason
  • file_info_or_error is a dict containing the following keys (if success is True) or a Twisted Failure if there was a problem.
    • url - the url where the file was downloaded from. This is the url of the request returned from the get_media_requests() method.
    • path - the path (relative to FILES_STORE) where the file was stored
    • checksum - a MD5 hash of the image contents

The list of tuples received by item_completed() is guaranteed to retain the same order of the requests returned from the get_media_requests() method.

Here’s a typical value of the results argument:

[(True,
  {'checksum': '2b00042f7481c7b056c4b410d28f33cf',
   'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg',
   'url': 'http://www.example.com/files/product1.pdf'}),
 (False,
  Failure(...))]

By default the get_media_requests() method returns None which means there are no files to download for the item.

item_completed(results, item, info)

The FilesPipeline.item_completed() method called when all file requests for a single item have completed (either finished downloading, or failed for some reason).

The item_completed() method must return the output that will be sent to subsequent item pipeline stages, so you must return (or drop) the item, as you would in any pipeline.

Here is an example of the item_completed() method where we store the downloaded file paths (passed in results) in the file_paths item field, and we drop the item if it doesn’t contain any files:

from scrapy.exceptions import DropItem

def item_completed(self, results, item, info):
    file_paths = [x['path'] for ok, x in results if ok]
    if not file_paths:
        raise DropItem("Item contains no files")
    item['file_paths'] = file_paths
    return item

By default, the item_completed() method returns the item.

See here the methods that you can override in your custom Images Pipeline:

class scrapy.pipelines.images.ImagesPipeline
The ImagesPipeline is an extension of the FilesPipeline, customizing the field names and adding custom behavior for images.
get_media_requests(item, info)

Works the same way as FilesPipeline.get_media_requests() method, but using a different field name for image urls.

Must return a Request for each image URL.

item_completed(results, item, info)

The ImagesPipeline.item_completed() method is called when all image requests for a single item have completed (either finished downloading, or failed for some reason).

Works the same way as FilesPipeline.item_completed() method, but using a different field names for storing image downloading results.

By default, the item_completed() method returns the item.

カスタムイメージのパイプラインの例

Here is a full example of the Images Pipeline whose methods are examplified above:

import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem

class MyImagesPipeline(ImagesPipeline):

    def get_media_requests(self, item, info):
        for image_url in item['image_urls']:
            yield scrapy.Request(image_url)

    def item_completed(self, results, item, info):
        image_paths = [x['path'] for ok, x in results if ok]
        if not image_paths:
            raise DropItem("Item contains no images")
        item['image_paths'] = image_paths
        return item

スパイダーのデプロイ

このセクションでは, Scrapy のスパイダーを定期的に実行するためのさまざまなオプションについて説明します. あなたのローカルマシンで Scrapy のスパイダーを実行するは,(早期の)開発段階にとっては非常に便利ですが, ロングランニングスパイダーを実行したり, スパイダーを継続的に稼動させたりするには現実的ではありません. ここでは, Scrapy のスパイダーをデプロイするためのソリューションを提案します.

Scrapyのスパイダーを展開するためのオプティカルな選択肢は:

Scrapyd サーバーへのデプロイ

Scrapyd は Scrapy のスパイダーを実行するためのオープンソースのアプリケーションです. これは, HTTP API を備えたサーバーを提供し, Scrapy スパイダーの実行および監視することができます.

スパイダーを Scrapyd にデプロイするには,  scrapyd-client パッケージが提供する scrapyd-deploy ツールを使用します. 詳細については, scrapyd-deploy documentation を参照してください.

Scrapyd は何人かの Scrapy 開発者によって維持されています.

Scrapy Cloud へのデプロイ

Scrapy Cloud はScrapyの背後にある Scrapinghub, のホスティング型のクラウドベースのサービスです.

Scrapy Cloud は, サーバーのセットアップと監視の必要性を排除し, スパイダーを管理し, スクラップされたアイテム, ログ, 統計情報を確認するための優れたUIを提供します.

Scrapy Cloud にスパイダーをデプロイするには,  shub コマンドラインツールを使用します. 詳細については, Scrapy Cloud documentation を参照してください.

Scrapy Cloud はScrapydと互換性があり, 必要に応じてそれらの間を切り替えることができます. scrapyd-deploy と同様に scrapy.cfg ファイルから読みこまれます.

AutoThrottle 拡張機能

これは, ScrapyサーバーとクロールしているWebサイトの両方の負荷に基づいてクロール速度を自動的に調整する拡張機能です.

設計目標

  1. 既定のダウンロード遅延ゼロを使用する代わりに, サイトをより良くする
  2. 自動的に最適なクロール速度に調整し, ユーザーはダウンロード遅延をチューニングして最適なものを見つける必要がない. ユーザーは許可する同時要求の最大数を指定するだけで, 残りの部分は拡張機能で処理する.

使い方

AutoThrottle拡張機能はダウンロード遅延を動的に調整して, スパイダーが AUTOTHROTTLE_TARGET_CONCURRENCY の同時リクエストを各リモートWebサイトに送信します.

ダウンロード待ち時間を使用して遅延を計算します. 主な考え方は次のとおりです: サーバが応答するのに 待ち時間 が必要な場合, クライアントは N 個の要求を並行して処理するために, 各 待ち時間/ N 秒間に要求を送信する必要があります.

遅延を調整する代わりに, 一定のダウンロード遅延を設定し, CONCURRENT_REQUESTS_PER_DOMAIN または CONCURRENT_REQUESTS_PER_IP オプションを使用して並行性に厳しい制限を課すことができます. 同様の効果が得られますが, いくつかの重要な違いがあります:

  • ダウンロードの遅延が小さいため, 時折リクエストが激しくなります.
  • 多くの場合, non-200 (エラー) レスポンスは通常の応答より速く返される可能性があるため, サーバーがエラーを返すようになると, ダウンロードの遅延が少なく, 並行処理の制限が厳しいため, クローラはサーバーに要求を高速に送信します. しかし, これはクローラが行うべきこととは反対です. エラーの場合, 遅くするのがより理にかなっています.

オートスロットルにはこれらの問題はありません.

スロットルアルゴリズム

オートスロットルアルゴリズムは, 次のルールに基づいてダウンロードの遅延を調整します:

  1. スパイダーは常に AUTOTHROTTLE_START_DELAY のダウンロード遅延で始まります.
  2. レスポンスが受信されると, ターゲットダウンロード遅延は, latency がレスポンスのレイテンシである latency / N として計算されます. N は AUTOTHROTTLE_TARGET_CONCURRENCY の値です.
  3. 次の要求のダウンロード遅延は, 前回のダウンロード遅延と目標のダウンロード遅延の平均値に設定されます.
  4. レイテンシの non-200 レスポンス待ち時間は遅延を減少させることができません.
  5. ダウンロードの遅延が DOWNLOAD_DELAY より小さくなることも,  AUTOTHROTTLE_MAX_DELAY より大きくなることもできません.

注釈

AutoThrottle拡張機能は, 標準的なScrapyの設定の並行性と遅延を優先します. これは, CONCURRENT_REQUESTS_PER_DOMAIN オプションと CONCURRENT_REQUESTS_PER_IP オプションを尊重し, ダウンロード遅延を DOWNLOAD_DELAY よりも低く設定しないことを意味します.

Scrapyでは, ダウンロードの待ち時間は, TCP接続を確立してからHTTPヘッダーを受信するまでの経過時間として測定されます.

これらのレイテンシは, Scrapyがスパイダーコールバックを処理するのに忙しく, ダウンロードに参加できないために, 協調的なマルチタスク環境では正確に測定することが非常に難しいことに注意してください. しかし, これらのレイテンシは, Scrapy(そして最終的にはサーバー)がどの程度忙しいかについての妥当な見積もりを与えるはずであり, この拡張はその前提に基づいています.

設定

オートスロットルエクステンションを制御するための設定以下です:

詳細については,  使い方 を参照してください.

AUTOTHROTTLE_ENABLED

デフォルト: False

オートスロットル拡張機能を有効にする.

AUTOTHROTTLE_START_DELAY

デフォルト: 5.0

最初のダウンロードの遅延(秒単位)

AUTOTHROTTLE_MAX_DELAY

デフォルト: 60.0

レイテンシが高い場合に設定される最大ダウンロード遅延(秒単位)

AUTOTHROTTLE_TARGET_CONCURRENCY

バージョン 1.1 で追加.

デフォルト: 1.0

リモートWebサイトと並行して送信する必要があるリクエストの平均数.

デフォルトでは, AutoThrottleは, 1つの同時リクエストを各リモートWebサイトに送信する遅延を調整します. このオプションをより高い値(たとえば 2.0 )に設定すると, リモートサーバーのスループットと負荷が増加します. AUTOTHROTTLE_TARGET_CONCURRENCY が小さいほど( 0.5 など), クローラはより控えめで丁寧なものになります.

AutoThrottle拡張機能が有効な場合,  CONCURRENT_REQUESTS_PER_DOMAIN および CONCURRENT_REQUESTS_PER_IP オプションは引き続き考慮されます. This means that if AUTOTHROTTLE_TARGET_CONCURRENCYCONCURRENT_REQUESTS_PER_DOMAIN または CONCURRENT_REQUESTS_PER_IP より高い値に設定されていると, クローラはこの数の同時要求に達しません.

与えられたすべての時点で, Scrapyは AUTOTHROTTLE_TARGET_CONCURRENCY よりも多かれ少なかれ並行した要求を送ることができます. クローラがアプローチしようとする推奨値であり, ハードな制限ではありません.

AUTOTHROTTLE_DEBUG

デフォルト: False

受信したすべてのレスポンスの統計情報を表示する AutoThrottle デバッグモードを有効にすると, 調整パラメータがリアルタイムでどのように調整されているかがわかります.

ベンチマーク

バージョン 0.17 で追加.

Scrapyには, ローカルのHTTPサーバーを生成し, 最大限の速度でクロールする単純なベンチマークスイートが付属しています. このベンチマークの目的は, 比較のための共通ベースラインを得るために, Scrapyがハードウェアでどのように機能するかを理解することです. ベンチマークには, 何もせず, リンクをたどる単純なスパイダーが使用されます.

実行するには:

scrapy bench

このような出力が表示されるはずです:

2013-05-16 13:08:46-0300 [scrapy] INFO: Scrapy 0.17.0 started (bot: scrapybot)
2013-05-16 13:08:47-0300 [scrapy] INFO: Spider opened
2013-05-16 13:08:47-0300 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:48-0300 [scrapy] INFO: Crawled 74 pages (at 4440 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:49-0300 [scrapy] INFO: Crawled 143 pages (at 4140 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:50-0300 [scrapy] INFO: Crawled 210 pages (at 4020 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:51-0300 [scrapy] INFO: Crawled 274 pages (at 3840 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:52-0300 [scrapy] INFO: Crawled 343 pages (at 4140 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:53-0300 [scrapy] INFO: Crawled 410 pages (at 4020 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:54-0300 [scrapy] INFO: Crawled 474 pages (at 3840 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:55-0300 [scrapy] INFO: Crawled 538 pages (at 3840 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:56-0300 [scrapy] INFO: Crawled 602 pages (at 3840 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:57-0300 [scrapy] INFO: Closing spider (closespider_timeout)
2013-05-16 13:08:57-0300 [scrapy] INFO: Crawled 666 pages (at 3840 pages/min), scraped 0 items (at 0 items/min)
2013-05-16 13:08:57-0300 [scrapy] INFO: Dumping Scrapy stats:
    {'downloader/request_bytes': 231508,
     'downloader/request_count': 682,
     'downloader/request_method_count/GET': 682,
     'downloader/response_bytes': 1172802,
     'downloader/response_count': 682,
     'downloader/response_status_count/200': 682,
     'finish_reason': 'closespider_timeout',
     'finish_time': datetime.datetime(2013, 5, 16, 16, 8, 57, 985539),
     'log_count/INFO': 14,
     'request_depth_max': 34,
     'response_received_count': 682,
     'scheduler/dequeued': 682,
     'scheduler/dequeued/memory': 682,
     'scheduler/enqueued': 12767,
     'scheduler/enqueued/memory': 12767,
     'start_time': datetime.datetime(2013, 5, 16, 16, 8, 47, 676539)}
2013-05-16 13:08:57-0300 [scrapy] INFO: Spider closed (closespider_timeout)

これは, Scrapyを実行しているハードウェアで毎分約3900ページをクロールできることを示しています. これはリンクをたどることを目的とした非常にシンプルなスパイダーであることに注意してください. カスタムスパイダーを作成すると, クロール速度が遅くなるほど多くの処理が行われます. どのくらい遅くなるかは, あなたのスパイダーがどれくらいのことをしているか, どの程度うまく書かれているかによって決まります.

将来, 他の一般的なシナリオをカバーするために, ベンチマークスイートにさらに多くのケースが追加される予定です.

ジョブ: クロールの一時停止と再開

大規模なサイトでは, クロールを一時停止してから後で再開できるようにすることが望ましい場合があります.

Scrapy は, 以下の機能により, この機能を使用できます:

  • スケジュールされた要求をディスクに保存するスケジューラ
  • ディスクに訪問要求を継続する重複フィルタ
  • バッチ間で永続的なスパイダー状態(キーと値のペア)を維持する拡張機能

ジョブディレクトリ

永続性サポートを有効にするには, JOBDIR 設定を使用してジョブディレクトリを定義するだけです. このディレクトリは, 単一のジョブ(つまり, スパイダ実行)の状態を保持するために必要なすべてのデータを格納するためのディレクトリです. このディレクトリは, 単一のジョブの状態を格納するために使用されるため, 異なるスパイダ, または同じスパイダの異なるジョブ・実行間で共有されてはならないことに注意することが重要です.

使用方法

持続性がサポートされているスパイダーを起動するには, 次のように実行します:

scrapy crawl somespider -s JOBDIR=crawls/somespider-1

その後, いつでも(Ctrl-Cを押すかシグナルを送信することによって)スパイダーを安全に停止し, 後で同じコマンドを発行して再開することができます:

scrapy crawl somespider -s JOBDIR=crawls/somespider-1

バッチ間で永続的な状態を維持する

一時停止/再開バッチの間にスパイダーの状態を維持したい場合があります. これには, spider.state 属性を使用できます. これは dict でなければなりません. スパイダーの起動と停止時に, ジョブディレクトリからその属性をシリアル化, 格納, ロードするための拡張機能が組み込まれています.

以下は, スパイダーの状態を使用するコールバックの例です(簡潔にするために他のスパイダーコードは省略されています):

def parse_item(self, response):
    # parse item here
    self.state['items_count'] = self.state.get('items_count', 0) + 1

持続性の落とし穴

Scrapyの永続性サポートを使用できるようにするには, 次の点に注意してください.

シリアル化を要求する

永続性を機能させるためには, リクエストが pickle モジュールによって直列化可能でなければならないため, リクエストが直列化可能であることを確認する必要があります.

最も一般的な問題は, 永続化できない要求コールバックに対して lambda 関数を使用することです. 語 例えば, これはうまくいきません:

def some_callback(self, response):
    somearg = 'test'
    return scrapy.Request('http://www.example.com', callback=lambda r: self.other_callback(r, somearg))

def other_callback(self, response, somearg):
    print "the argument passed is:", somearg

しかし, これはうまくいきます:

def some_callback(self, response):
    somearg = 'test'
    return scrapy.Request('http://www.example.com', callback=self.other_callback, meta={'somearg': somearg})

def other_callback(self, response):
    somearg = response.meta['somearg']
    print "the argument passed is:", somearg

シリアライズできなかったリクエストを記録する場合は, プロジェクトの設定ページで SCHEDULER_DEBUG 設定を True に設定します. デフォルトでは False です.

よくある質問
最もよく寄せられる質問への回答を得る.
スパイダーのデバッグ
スパイダーの一般的な問題をデバッグする方法を学ぶ.
スパイダーコントラクト
スパイダーをテストのために使用する方法を学ぶ.
一般的なプラクティス
いくつかの Scrapy の共通プラクティスを理解する.
ブロードクロール
多くのドメインを並行してクロールするための調整.
スクレイピングにFireFoxを使用する
Firefoxといくつかの便利なアドオンを使用してスクラップする方法を学ぶ.
スクレイピングにFirebugを使用する
Firebugを使って効率的にスクレイプする方法を学ぶ.
メモリリークのデバッグ
クローラでメモリリークを見つけて取り除く方法を学ぶ.
ファイルと画像のダウンロードと処理
スクラップしたアイテムに関連するファイルや画像をダウンロードする.
スパイダーのデプロイ
Scrapyスパイダーをデプロイしてリモートサーバーで実行する.
AutoThrottle 拡張機能
負荷に基づいて動的にクロール速度を調整する.
ベンチマーク
あなたのハードウェアでScrapyがどのように機能するかを調べる.
ジョブ: クロールの一時停止と再開
大きなスパイダーのクロールを一時停止して再開する方法を学ぶ.

Scrapy を拡張する

アーキテクチャの概要

このドキュメントでは, Scrapyのアーキテクチャとそのコンポーネントがどのように相互作用するかについて説明します.

概要

次の図は, Scrapy アーキテクチャの概要とそのコンポーネントと, システム内部で実行されるデータフローの概要を示しています(緑色の矢印). コンポーネントの簡単な説明は, それらの詳細な情報については, 以下のリンクを参照してください. データフローも以下で説明します.

データフロー

Scrapy architecture

Scrapy のデータフローは実行エンジンによって制御され, 以下のようになります:

  1. Engine は, Spider からクロールする最初のリクエストを取得します.
  2. Engine は, Scheduler 内のリクエストをスケジュールし, 次にクロールするリクエストを要求します.
  3. Scheduler は, 次のリクエストを Engine に渡します.
  4. Engine は, Downloader Middlewares を経由して Downloader にリクエストを送信します (process_request() を参照してください).
  5. ページのダウンロードが終了すると, Downloader はそのページでレスポンスを生成し, Downloader Middlewares (process_response() を参照してください) を経由して Engine に送信します.
  6. Engine は, レスポンスを Downloader から受信し, Spider Middleware (process_spider_input() を参照してください) を経由する処理のために Spider に送信します.
  7. Spider はレスポンスを処理し, Spider Middleware (process_spider_output() を参照してください) を経由して, 収集したアイテムと新しいリクエストを Engine に返します.
  8. The Engine は, 処理されたアイテムを Item Pipelines に渡し, 処理されたリクエストを Scheduler に渡した後, 次のクロールリクエストを要求します.
  9. このプロセスは, Scheduler からの要求がなくなるまで(ステップ1から)繰り返されます.

コンポーネント

Scrapy エンジン

エンジンは, システムのすべてのコンポーネント間のデータフローを制御し, 特定のアクションが発生したときにイベントをトリガーします. 詳細については, 上記の データフロー のセクションを参照してください.

スケジューラー

スケジューラは, エンジンからリクエストを受信し, エンジンが要求したときに後で(エンジンにも)それらを供給するためにそれらのキューを実行します.

ダウンローダー

ダウンローダーは, Web ページを取得してエンジンに供給し, そのエンジンをスパイダーにフィードします.

スパイダー

スパイダーとは, Scrapy ユーザーがレスポンスを解析し, それらから収集したアイテム を抽出するためのカスタムクラスです. 詳細は スパイダー を参照してください.

アイテムパイプライン

アイテムパイプラインは, アイテムがスパイダーによって抽出されるとアイテムの処理を行います. 典型的なタスクには, クレンジング, 検証, 永続性(アイテムをデータベースに格納するなど)が含まれます. 詳細は, アイテムパイプライン を参照してください.

ダウンローダーミドルウェア

ダウンローダーミドルウェアは, エンジンとダウンローダーの間に位置し, エンジンからダウンローダーに渡されたリクエストと, ダウンローダーからエンジンに渡すレスポンスを処理する特定のフックです.

次のいずれかを実行する必要がある場合は, ダウンローダーミドルウェアを使用します:

  • ダウンローダに送信される直前のリクエスト(つまり, Scrapy がリクエストをウェブサイトに送信する直前)を処理する
  • 受信したレスポンスをスパイダーに渡す前に変更する
  • 受け取ったレスポンスをスパイダーに渡す代わりに新しいリクエストを送信する
  • ウェブページを取得せずにスパイダーにレスポンスを渡す
  • いくつかのリクエストを実行しない

詳細については, ダウンローダーミドルウェア を参照してください.

スパイダーミドルウェア

スパイダーミドルウェアは, エンジンとスパイダーの間に位置し, スパイダーの入力(レスポンス)と出力(アイテムとリクエスト)を処理する特定のフックです.

次のいずれかを実行する必要がある場合は, スパイダーミドルウェアを使用します:

  • スパイダーコールバックの後処理出力 - リクエストまたはアイテムの変更/追加/削除
  • start_requests の後処理
  • スパイダーの例外処理
  • レスポンスの内容に基づいてリクエストの一部をコールバックする代わりにerrbackを呼び出す.

詳細については, スパイダーミドルウェア を参照してください.

イベントドリブンネットワーキング

Scrapy は, Python の有名なイベント駆動型ネットワークフレームワークである Twisted で書かれています. したがって, 非同期コードを使用して同時実行性を実現しています.

非同期プログラミングと Twisted の詳細については, これらのリンクを参照してください:

ダウンローダーミドルウェア

ダウンローダーミドルウェアは, Scrapy のリクエスト/レスポンス処理へフックするフレームワークです. Scrapy のリクエストとレスポンスをグローバルに変更するための軽量で低レベルのシステムです.

ダウンローダーミドルウェアの有効化

ダウンローダーミドルウェアの有効化をするためには、DOWNLOADER_MIDDLEWARES に設定してください。 これは辞書であり、キーがミドルウェアクラスパスであり、値はミドルウェアのオーダーであることに注意してください。

例:

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
}

DOWNLOADER_MIDDLEWARES 設定はScrapy(オーバーリデンではない)で決められている :setting:`DOWNLOADER_MIDDLEWARES_BASE`とマージされます。その後、ソートされ実行される順番が決められます。 最初のミドルウェアがエンジンに近く、最後がダウンローダーに近いです。言い換えると、それぞれの:meth:`~scrapy.downloadermiddlewares.DownloaderMiddleware.process_request`メソッドが昇順で(100,200,300..)で実行されていき、それぞれの:meth:`~scrapy.downloadermiddlewares.DownloaderMiddleware.process_response`メソッドが降順で実行されます。

ミドルウェアの順番を決めるためには:setting:`DOWNLOADER_MIDDLEWARES_BASE`を参考にし、値を決める必要があります。順番はそれぞれのミドルウェアの関係性を考慮し決めなくてはなりません。

もしあなたが標準のミドルウェアを実行したくない場合は、DOWNLOADER_MIDDLEWARES`None`を設定してください。 例えばあなたがuser-agent middlewareを実行したくない場合は

DOWNLOADER_MIDDLEWARES = {
‘myproject.middlewares.CustomDownloaderMiddleware’: 543, ‘scrapy.downloadermiddlewares.useragent.UserAgentMiddleware’: None,

}

ミドルウェアの中には特別な設定が必要なものもあります。 詳しくはドキュメントを参照してください。

独自のダウンローダーミドルウェアの作成

各ミドルウェアコンポーネントは, 以下のメソッドの1つ以上を定義する Python クラスです:

class scrapy.downloadermiddlewares.DownloaderMiddleware

注釈

ダウンローダーミドルウェアメソッドのいずれも, 遅延したものを返す可能性があります.

process_request(request, spider)

This method is called for each request that goes through the download middleware.

process_request() should either: return None, return a Response object, return a Request object, or raise IgnoreRequest.

If it returns None, Scrapy will continue processing this request, executing all other middlewares until, finally, the appropriate downloader handler is called the request performed (and its response downloaded).

If it returns a Response object, Scrapy won’t bother calling any other process_request() or process_exception() methods, or the appropriate download function; it’ll return that response. The process_response() methods of installed middleware is always called on every response.

If it returns a Request object, Scrapy will stop calling process_request methods and reschedule the returned request. Once the newly returned request is performed, the appropriate middleware chain will be called on the downloaded response.

If it raises an IgnoreRequest exception, the process_exception() methods of installed downloader middleware will be called. If none of them handle the exception, the errback function of the request (Request.errback) is called. If no code handles the raised exception, it is ignored and not logged (unlike other exceptions).

パラメータ:
  • request (Request object) – the request being processed
  • spider (Spider object) – the spider for which this request is intended
process_response(request, response, spider)

process_response() should either: return a Response object, return a Request object or raise a IgnoreRequest exception.

If it returns a Response (it could be the same given response, or a brand-new one), that response will continue to be processed with the process_response() of the next middleware in the chain.

If it returns a Request object, the middleware chain is halted and the returned request is rescheduled to be downloaded in the future. This is the same behavior as if a request is returned from process_request().

If it raises an IgnoreRequest exception, the errback function of the request (Request.errback) is called. If no code handles the raised exception, it is ignored and not logged (unlike other exceptions).

パラメータ:
  • request (is a Request object) – the request that originated the response
  • response (Response object) – the response being processed
  • spider (Spider object) – the spider for which this response is intended
process_exception(request, exception, spider)

Scrapy calls process_exception() when a download handler or a process_request() (from a downloader middleware) raises an exception (including an IgnoreRequest exception)

process_exception() should return: either None, a Response object, or a Request object.

If it returns None, Scrapy will continue processing this exception, executing any other process_exception() methods of installed middleware, until no middleware is left and the default exception handling kicks in.

If it returns a Response object, the process_response() method chain of installed middleware is started, and Scrapy won’t bother calling any other process_exception() methods of middleware.

If it returns a Request object, the returned request is rescheduled to be downloaded in the future. This stops the execution of process_exception() methods of the middleware the same as returning a response would.

パラメータ:
  • request (is a Request object) – the request that generated the exception
  • exception (an Exception object) – the raised exception
  • spider (Spider object) – the spider for which this request is intended

ビルトインダウンローダーミドルウェアリファレンス

This page describes all downloader middleware components that come with Scrapy. For information on how to use them and how to write your own downloader middleware, see the downloader middleware usage guide.

デフォルトで有効になっているコンポーネントの一覧(およびそのオーダー)については, DOWNLOADER_MIDDLEWARES_BASE 設定を参照してください.

CookiesMiddleware
class scrapy.downloadermiddlewares.cookies.CookiesMiddleware

This middleware enables working with sites that require cookies, such as those that use sessions. It keeps track of cookies sent by web servers, and send them back on subsequent requests (from that spider), just like web browsers do.

The following settings can be used to configure the cookie middleware:

COOKIES_ENABLED

デフォルト: True

クッキーミドルウェアを有効にするかどうか. 無効にすると, Webサーバーにクッキーは送信されません.

COOKIES_DEBUG

デフォルト: False

If enabled, Scrapy will log all cookies sent in requests (ie. Cookie header) and all cookies received in responses (ie. Set-Cookie header).

Here’s an example of a log with COOKIES_DEBUG enabled:

2011-04-06 14:35:10-0300 [scrapy] INFO: Spider opened
2011-04-06 14:35:10-0300 [scrapy] DEBUG: Sending cookies to: <GET http://www.diningcity.com/netherlands/index.html>
        Cookie: clientlanguage_nl=en_EN
2011-04-06 14:35:14-0300 [scrapy] DEBUG: Received cookies from: <200 http://www.diningcity.com/netherlands/index.html>
        Set-Cookie: JSESSIONID=B~FA4DC0C496C8762AE4F1A620EAB34F38; Path=/
        Set-Cookie: ip_isocode=US
        Set-Cookie: clientlanguage_nl=en_EN; Expires=Thu, 07-Apr-2011 21:21:34 GMT; Path=/
2011-04-06 14:49:50-0300 [scrapy] DEBUG: Crawled (200) <GET http://www.diningcity.com/netherlands/index.html> (referer: None)
[...]
DefaultHeadersMiddleware
class scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware

This middleware sets all default requests headers specified in the DEFAULT_REQUEST_HEADERS setting.

DownloadTimeoutMiddleware
class scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware

This middleware sets the download timeout for requests specified in the DOWNLOAD_TIMEOUT setting or download_timeout spider attribute.

注釈

You can also set download timeout per-request using download_timeout Request.meta key; this is supported even when DownloadTimeoutMiddleware is disabled.

HttpAuthMiddleware
class scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware

このミドルウェアは, Basic access authentication (別名HTTP認証)を使用して, 特定のスパイダーから生成されたすべてのリクエストを認証します.

特定のスパイダーからHTTP認証を有効にするには, これらのスパイダーの http_user および http_pass 属性を設定します.

例:

from scrapy.spiders import CrawlSpider

class SomeIntranetSiteSpider(CrawlSpider):

    http_user = 'someuser'
    http_pass = 'somepass'
    name = 'intranet.example.com'

    # .. 残りのスパイダーコードは省略されています ...
HttpCacheMiddleware
class scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware

このミドルウェアは, すべてのHTTPリクエストとレスポンスに低レベルのキャッシュを提供します. これはキャッシュストレージバックエンドとキャッシュポリシーとを組み合わせなければなりません.

2つのHTTPキャッシュストレージバックエンドを持つ Scrapy:

HTTPキャッシュストレージバックエンドは, HTTPCACHE_STORAGE 設定で変更できます. また, 独自のストレージバックエンドを実装することもできます.

2つのHTTPキャッシュポリシーを持つ Scrapy:

HTTPCACHE_POLICY 設定を使用してHTTPキャッシュポリシーを変更できます. あるいは独自のポリシーを実装することもできます.

また, dont_cache メタキーを True とすると, すべてのポリシーで応答をキャッシュすることを避けることができます.

ダミーポリシー (デフォルト)

このポリシーは, HTTP Cache-Control ディレクティブを意識していません. すべてのリクエストとそれに対応するレスポンスがキャッシュされます. 同じリクエストが再び見られると, インターネットから何も転送せずにレスポンスが返されます.

ダミーポリシーは, スパイダーを素早くテストする(毎回ダウンロードを待たずに), または, インターネット接続が利用できないときにスパイダーをオフラインで試すのに便利です. 目標は, 以前に実行されたとおりにスパイダーの実行を「再生」できるようにすることです.

このポリシーを使用するには:

  • HTTPCACHE_POLICY に scrapy.extensions.httpcache.DummyPolicy を設定します.
RFC2616 ポリシー

このポリシーは, HTTPキャッシュ制御の認識を備えた RFC2616 準拠の HTTP キャッシュを提供し, 生産を目的とし, 変更なしのデータのダウンロードを避けるために連続実行で 使用します(帯域幅を節約し, クロールを高速化します).

実装されているもの:

  • no-store キャッシュ制御ディレクティブセットで, レスポンス/リクエストを格納しない

  • 新しいレスポンスに対しても no-cache キャッシュコントロール指令が設定されている場合, キャッシュからの応答を提供しない

  • max-age キャッシュ制御命令からフレッシュネスライフタイムを計算する

  • Expires レスポンスヘッダーからフレッシュネスライフタイムを計算する

  • Last-Modified レスポンスヘッダ(Firefoxで使用されるヒューリスティック)からフレッシュネスライフタイムを計算

  • Age レスポンスヘッダから現在の年齢を計算する

  • Date ヘッダから現在の年齢を計算する

  • Last-Modified レスポンスヘッダに基づいて失効したレスポンスを再確認する

  • ETag レスポンスヘッダーにもとづいて失効した応答を再検証する

  • 受け取らなかったレスポンスの Date` ヘッダーを設定しない

  • リクエストにおける max-stale キャッシュ制御命令をサポート

    これにより, スパイダーを完全なRFC2616キャッシュポリシーで構成することができますが, HTTP仕様に準拠したままで, リクエストごとに再検証は行われません.

    例:

    Cache-Control: max-stale=600 を追加して, 有効期限を超過したリクエストを600秒以下で受け入れるようにヘッダーに要求します.

    参照: RFC2616, 14.9.3

何が無くなったか:

このポリシーを使用するには:

  • HTTPCACHE_POLICY に scrapy.extensions.httpcache.RFC2616Policy を設定します.
ファイルシステムストレージバックエンド (デフォルト)

ファイルシステムストレージバックエンドは, HTTPキャッシュミドルウェアで使用できます.

このストレージバックエンドを使用するには:

  • HTTPCACHE_STORAGEscrapy.extensions.httpcache.FilesystemCacheStorage を設定します.

各 request/response のペアは, 次のファイルを含む別のディレクトリに格納されます:

  • request_body - the plain request body
  • request_headers - the request headers (in raw HTTP format)
  • response_body - the plain response body
  • response_headers - the request headers (in raw HTTP format)
  • meta - some metadata of this cache resource in Python repr() format (grep-friendly format)
  • pickled_meta - the same metadata in meta but pickled for more efficient deserialization

ディレクトリ名はリクエストフィンガープリント ( scrapy.utils.request.fingerprint を参照)から作成され, 1つのレベルのサブディレクトリが, 同じディレクトリにあまりにも多くのファイルを作成することを 避けるために使用されます(多くのファイルシステムでは非効率的です):

/path/to/cache/dir/example.com/72/72811f648e718090f041317756c03adb0ada46c7
DBM ストレージバックエンド

バージョン 0.13 で追加.

DBM ストレージバックエンドは, HTTPキャッシュミドルウェアでも使用できます.

デフォルトでは, anydbm モジュールを使用しますが, HTTPCACHE_DBM_MODULE 設定で変更することができます.

このストレージバックエンドを使用するには:

  • HTTPCACHE_STORAGEscrapy.extensions.httpcache.DbmCacheStorage を設定します.
LevelDB ストレージバックエンド

バージョン 0.23 で追加.

LevelDB ストレージバックエンドは, HTTPキャッシュミドルウェアでも使用できます.

このバックエンドは開発プロセスにはお勧めできません. これは, 同時に1つのプロセスしか LevelDB データベースにアクセスできないためです. そのため, 同じスパイダーに対して並列に Scrapy シェルを開くことはできません.

このストレージバックエンドを使用するには:

HTTPCache middleware 設定

HttpCacheMiddleware は, 次の設定で構成されています:

HTTPCACHE_ENABLED

バージョン 0.11 で追加.

デフォルト: False

HTTPキャッシュを有効にするかどうか.

バージョン 0.11 で変更: 0.11 より前では, キャッシュを有効にするために HTTPCACHE_DIR が使用されていました.

HTTPCACHE_EXPIRATION_SECS

デフォルト: 0

キャッシュされたリクエストの有効期限(秒単位).

この時間より古いキャッシュされたリクエストは再ダウンロードされます. 0の場合, キャッシュされたリクエストは期限切れになりません.

バージョン 0.11 で変更: 0.11 以前は, 0 はキャッシュされたリクエストが常に期限切れになることを意味しました.

HTTPCACHE_DIR

デフォルト: 'httpcache'

(低レベル)HTTPキャッシュを格納するために使用するディレクトリ. 空の場合, HTTPキャッシュは無効になります. 相対パスが指定されている場合は, プロジェクトデータディレクトリに対して相対パスが使用されます. 詳細は, Scrapyプロジェクトのデフォルト構造 を参照してください.

HTTPCACHE_IGNORE_HTTP_CODES

バージョン 0.10 で追加.

デフォルト: []

キャッシュしないレスポンスの HTTP コード.

HTTPCACHE_IGNORE_MISSING

デフォルト: False

有効にすると, キャッシュにないリクエストはダウンロードされずに無視されます.

HTTPCACHE_IGNORE_SCHEMES

バージョン 0.10 で追加.

デフォルト: ['file']

キャッシュしないレスポンスのURIスキーム.

HTTPCACHE_STORAGE

デフォルト: 'scrapy.extensions.httpcache.FilesystemCacheStorage'

キャッシュストレージバックエンドを実装するクラス.

HTTPCACHE_DBM_MODULE

バージョン 0.13 で追加.

デフォルト: 'anydbm'

DBMストレージバックエンド で使用するデータベースモジュール. この設定は, DBMバックエンド特有です.

HTTPCACHE_POLICY

バージョン 0.18 で追加.

デフォルト: 'scrapy.extensions.httpcache.DummyPolicy'

キャッシュポリシーを実装するクラス.

HTTPCACHE_GZIP

バージョン 1.0 で追加.

デフォルト: False

有効にすると, キャッシュされたすべてのデータがgzipで圧縮されます. この設定はファイルシステムのバックエンド特有です.

HTTPCACHE_ALWAYS_STORE

バージョン 1.1 で追加.

デフォルト: False

有効にすると, 無条件にページをキャッシュします.

スパイダーは, Cache-Control: max-stale などを将来使用するために, すべてのレスポンスをキャッシュで利用できるようにすることができます. DummyPolicy はすべてのレスポンスをキャッシュしますが, それを再検証することはありません. また, 別のポリシーが望ましい場合もあります.

この設定は, 依然として Cache-Control: no-store ディレクティブを尊重します. 必要がない場合は, キャッシュミドルウェアにフィードしたレスポンスの Cache-Control ヘッダーから no-store を除外します.

HTTPCACHE_IGNORE_RESPONSE_CACHE_CONTROLS

バージョン 1.1 で追加.

デフォルト: []

無視されるレスポンスのキャッシュ制御ディレクティブのリスト.

サイトはしばしば “no-store”, “no-cache”, “must-revalidate”, などを設定しますが, スパイダーがそれらのディレクティブを尊重するならば生成できるトラフィックで動揺します. これにより, クロールしているサイトの重要でないことがわかっている Cache-Control ディレクティブを選択的に無視することができます.

スパイダーは実際に Cache-Control ディレクティブを必要としない限り, Cache-Control ディレクティブを発行しないので, リクエスト内のディレクティブはフィルタリングされません.

HttpCompressionMiddleware
class scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware

このミドルウェアは, 圧縮された(gzip, deflate)トラフィックをWebサイトから送受信できるようにします.

HttpCompressionMiddleware 設定
COMPRESSION_ENABLED

デフォルト: True

HttpCompressionMiddleware を有効にするかどうか.

HttpProxyMiddleware

バージョン 0.8 で追加.

class scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware

This middleware sets the HTTP proxy to use for requests, by setting the proxy meta value for Request objects.

Like the Python standard library modules urllib and urllib2, it obeys the following environment variables:

  • http_proxy
  • https_proxy
  • no_proxy

You can also set the meta key proxy per-request, to a value like http://some_proxy_server:port.

RedirectMiddleware
class scrapy.downloadermiddlewares.redirect.RedirectMiddleware

このミドルウェアは, 応答ステータスに基づいてリクエストのリダイレクトを処理します.

The urls which the request goes through (while being redirected) can be found in the redirect_urls Request.meta key.

The RedirectMiddleware can be configured through the following settings (see the settings documentation for more info):

If Request.meta has dont_redirect key set to True, the request will be ignored by this middleware.

If you want to handle some redirect status codes in your spider, you can specify these in the handle_httpstatus_list spider attribute.

For example, if you want the redirect middleware to ignore 301 and 302 responses (and pass them through to your spider) you can do this:

class MySpider(CrawlSpider):
    handle_httpstatus_list = [301, 302]

The handle_httpstatus_list key of Request.meta can also be used to specify which response codes to allow on a per-request basis. You can also set the meta key handle_httpstatus_all to True if you want to allow any response code for a request.

RedirectMiddleware 設定
REDIRECT_ENABLED

バージョン 0.13 で追加.

デフォルト: True

RedirectMiddleware を有効にするかどうか.

REDIRECT_MAX_TIMES

デフォルト: 20

1回のリクエストで実行されるリダイレクトの最大数.

MetaRefreshMiddleware
class scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware

このミドルウェアは, メタリフレッシュhtmlタグに基づいてリクエストのリダイレクトを処理します.

The MetaRefreshMiddleware can be configured through the following settings (see the settings documentation for more info):

This middleware obey REDIRECT_MAX_TIMES setting, dont_redirect and redirect_urls request meta keys as described for RedirectMiddleware

MetaRefreshMiddleware 設定
METAREFRESH_ENABLED

バージョン 0.17 で追加.

デフォルト: True

MetaRefreshMiddleware を有効にするかどうか.

METAREFRESH_MAXDELAY

デフォルト: 100

The maximum meta-refresh delay (in seconds) to follow the redirection. Some sites use meta-refresh for redirecting to a session expired page, so we restrict automatic redirection to the maximum delay.

RetryMiddleware
class scrapy.downloadermiddlewares.retry.RetryMiddleware

A middleware to retry failed requests that are potentially caused by temporary problems such as a connection timeout or HTTP 500 error.

Failed pages are collected on the scraping process and rescheduled at the end, once the spider has finished crawling all regular (non failed) pages. Once there are no more failed pages to retry, this middleware sends a signal (retry_complete), so other extensions could connect to that signal.

The RetryMiddleware can be configured through the following settings (see the settings documentation for more info):

If Request.meta has dont_retry key set to True, the request will be ignored by this middleware.

RetryMiddleware 設定
RETRY_ENABLED

バージョン 0.13 で追加.

デフォルト: True

RetryMiddleware を有効にするかどうか.

RETRY_TIMES

デフォルト: 2

最初のダウンロードに加えて, 再試行の最大回数.

RETRY_HTTP_CODES

デフォルト: [500, 502, 503, 504, 408]

Which HTTP response codes to retry. Other errors (DNS lookup issues, connections lost, etc) are always retried.

In some cases you may want to add 400 to RETRY_HTTP_CODES because it is a common code used to indicate server overload. It is not included by default because HTTP specs say so.

RobotsTxtMiddleware
class scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware

This middleware filters out requests forbidden by the robots.txt exclusion standard.

To make sure Scrapy respects robots.txt make sure the middleware is enabled and the ROBOTSTXT_OBEY setting is enabled.

If Request.meta has dont_obey_robotstxt key set to True the request will be ignored by this middleware even if ROBOTSTXT_OBEY is enabled.

DownloaderStats
class scrapy.downloadermiddlewares.stats.DownloaderStats

Middleware that stores stats of all requests, responses and exceptions that pass through it.

To use this middleware you must enable the DOWNLOADER_STATS setting.

UserAgentMiddleware
class scrapy.downloadermiddlewares.useragent.UserAgentMiddleware

スパイダーがデフォルトのユーザーエージェントをオーバーライドできるミドルウェア.

In order for a spider to override the default user agent, its user_agent attribute must be set.

AjaxCrawlMiddleware
class scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware

Middleware that finds ‘AJAX crawlable’ page variants based on meta-fragment html tag. See https://developers.google.com/webmasters/ajax-crawling/docs/getting-started for more info.

注釈

Scrapy finds ‘AJAX crawlable’ pages for URLs like 'http://example.com/!#foo=bar' even without this middleware. AjaxCrawlMiddleware is necessary when URL doesn’t contain '!#'. This is often a case for ‘index’ or ‘main’ website pages.

AjaxCrawlMiddleware 設定
AJAXCRAWL_ENABLED

バージョン 0.21 で追加.

デフォルト: False

Whether the AjaxCrawlMiddleware will be enabled. You may want to enable it for broad crawls.

HttpProxyMiddleware 設定
HTTPPROXY_AUTH_ENCODING

デフォルト: "latin-1"

The default encoding for proxy authentication on HttpProxyMiddleware.

スパイダーミドルウェア

スパイダーミドルウェアは, Scrapyのスパイダー処理機構へのフックのフレームワークで, スパイダーに送信された応答を処理し, スパイダー から生成されたリクエストとアイテムを処理するカスタム機能をプラグインできます.

スパイダーミドルウェアの有効化

スパイダーミドルウェアコンポーネントをアクティブにするには, SPIDER_MIDDLEWARES 設定に追加します. これは, キーがミドルウェアクラスのパスであり, その値がミドルウェアオーダーです.

例:

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
}

SPIDER_MIDDLEWARES 設定は, Scrapyで定義された SPIDER_MIDDLEWARES_BASE 設定にマージされます(上書きされるという意味ではありません). 次に, 使用可能なミドルウェアの最終的なリストを取得するために, 並べ替えられます. 値の小さいミドルウェアはエンジンに近いもので,  大きいものはスパイダーに近いです. 言い換えれば, process_spider_input() メソッドは, ミドルウェアの順番が増加する (100, 200, 300, ...), ように呼び出され, 各ミドルウェアの process_spider_output() メソッドが降順で呼び出されます.

ミドルウェアに割り当てる順序を決定するには, SPIDER_MIDDLEWARES_BASE 設定を参照し, ミドルウェアを挿入する場所に応じて値を選択します. 各ミドルウェアが異なるアクションを実行し, ミドルウェアが適用されている以前の (または後続の)ミドルウェアに依存する可能性があるため, 順序は重要です.

組み込みミドルウェア ( SPIDER_MIDDLEWARES_BASE で定義され, デフォルトで有効になっているもの) を無効にするには, プロジェクトの SPIDER_MIDDLEWARES 設定で定義し, その値にNoneを割り当てる必要があります. たとえば, off-site ミドルウェアを無効にする場合は:

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}

最後に, 特定の設定によっていくつかのミドルウェアを有効にする必要があるかもしれないことに留意してください. 詳細は各ミドルウェアのドキュメントを参照してください.

独自のスパイダーミドルウェアの作成

各ミドルウェアコンポーネントは, 以下のメソッドの1つ以上を定義する Python クラスです:

class scrapy.spidermiddlewares.SpiderMiddleware
process_spider_input(response, spider)

このメソッドは, 処理のためにスパイダーミドルウェアを経由してスパイダーに入る各レスポンスに対して呼び出されます.

process_spider_input() は必ず None を返すか, または例外を発生させなければいけません.

None を返すと, Scrapy はこのレスポンスの処理を続行し, 最終的にレスポンスが処理のためにスパイダーに渡されるまで他のすべてのミドルウェアを実行します.

例外が発生した場合, Scrapy は他のミドルウェアの process_spider_input() メソッド呼び出しを無視して errback リクエストを呼び出します. errback の出力は, process_spider_output() で処理されるか, エラーが発生した場合は process_spider_exception() メソッドにチェーンされて戻ります.

パラメータ:
  • response (Response オブジェクト) – 処理されているレスポンス
  • spider (Spider オブジェクト) – このレスポンスを処理しているスパイダー
process_spider_output(response, result, spider)

このメソッドは, レスポンスを処理した後にスパイダーから返された結果にたいして呼び出されます.

process_spider_output() は必ずイテラブルな Request, dict または Item オブジェクトのいずれかを返す必要があります.

パラメータ:
  • response (Response オブジェクト) – スパイダーによって出力されたレスポンス
  • result (イテラブルな Request, dict または Item オブジェクト) – スパイダーによって返された結果
  • spider (Spider オブジェクト) – 結果が処理されているスパイダー
process_spider_exception(response, exception, spider)

このメソッドは, スパイダーまたは process_spider_input() メソッド (他のスパイダーミドルウェアのもの) が例外を発生させたときに呼び出されます.

process_spider_exception()None , イテラブルな Response, dict または Item オブジェクトを返さなければいけません.

None を返すと, Scrapy は, ミドルウェアコンポーネントが残っていない, かつ例外がエンジンに到達するまで, この例外の処理を続け, 次のミドルウェアコンポーネントで process_spider_exception() を実行します.

イテラブルを返すと process_spider_output() パイプラインが起動し, ほかの process_spider_exception() は呼び出されません.

パラメータ:
  • response (Response オブジェクト) – 例外が発生したときに処理されるレスポンス
  • exception (Exception オブジェクト) – 発生した例外
  • spider (Spider オブジェクト) – 例外が発生したスパイダー
process_start_requests(start_requests, spider)

バージョン 0.15 で追加.

このメソッドはスパイダーの開始要求とともに呼び出され, process_spider_output() メソッドと同様に動作します. ただし, レスポンスが関連付けられておらず, リクエスト(項目ではない)のみを返さなければなりません.

イテラブル ( start_requests パラメーター無い) を受取り, イテラブルな Request オブジェを返さなければいけません.

注釈

このメソッドをスパイダー・ミドルウェアに実装する場合は, 常にiterableを返し, start_requests をすべて消費しないようにする必要があります. 非常に大きい(または制限なし)ことがあり, メモリーがオーバーフローする可能性があるからです. Scrapy エンジンは, 開始要求を処理する能力を持っている間に開始要求を引き出すように設計されているため, 開始要求イテレータは, スパイダーを停止するためのその他の条件(時間制限や項目/ページ数など)がある場合には効果的に無限になります.

パラメータ:
  • start_requests (イテラブルな Request) – スタートリクエスト
  • spider (Spider オブジェクト) – スタートリクエストが属するスパイダー

ビルトインスパイダーミドルウェアリファレンス

このページでは, Scrapyに付属するすべてのスパイダーミドルウェアコンポーネントについて説明します. それらの使用方法と独自のスパイダーミドルウェアの作成方法については, スパイダーミドルウェア使用方法ガイド を参照してください.

デフォルトで有効になっているコンポーネントの一覧(およびそのオーダー)については, SPIDER_MIDDLEWARES_BASE 設定を参照してください.

DepthMiddleware
class scrapy.spidermiddlewares.depth.DepthMiddleware

DepthMiddleware は, スクレイプされているサイト内の各リクエストの深さを追跡するために使用されるスクレイプミドルウェアです. これは, スクレイピングなのどの最大深さを制限するために使用することができます.

DepthMiddleware は以下の設定で設定することができます(詳細については各設定を参照してください):

  • DEPTH_LIMIT - クロールできる最大の深さ. ゼロの場合, 制限は課されません.
  • DEPTH_STATS - 深度統計を収集するかどうか.
  • DEPTH_PRIORITY - リクエストを深さに基づいて優先順位付けするかどうか.
HttpErrorMiddleware
class scrapy.spidermiddlewares.httperror.HttpErrorMiddleware

スパイダーが対処する必要がないように, 失敗した(誤った)HTTP レスポンスを除外します. ほとんどの場合, オーバーヘッドがかかり, より多くのリソースを消費し, スパイダーロジックがより複雑になります.

HTTP standard によれば, 成功したレスポンスは, ステータスコードが200〜300の範囲にあるものです.

その範囲外のレスポンスコードを処理したい場合は, handle_httpstatus_list 属性または HTTPERROR_ALLOWED_CODES 設定を使用して, スパイダーが処理できるレスポンスコードを指定できます.

たとえば, スパイダーが 404 レスポンスを処理するようにするには, 以下のようにします:

class MySpider(CrawlSpider):
    handle_httpstatus_list = [404]

Request.metahandle_httpstatus_list キーを使用して, リクエストごとに許可するレスポンスコードを指定することもできます. リクエストにすべてのレスポンスコードを許可させる場合は, meta キー handle_httpstatus_allTrue に設定することもできます.

しかし, あなた自身が何を行いたいかを本当に理解していない限り, 200以外の回答をむやみに処理することは, 通常は悪い考えです.

詳細については, HTTP Status Code Definitions を参照してください.

HttpErrorMiddleware 設定
HTTPERROR_ALLOWED_CODES

Default: []

このリストに含まれる200以外のステータスコードを含むすべての応答を渡します.

HTTPERROR_ALLOW_ALL

Default: False

ステータスコードに関係なくすべての応答を渡します.

OffsiteMiddleware
class scrapy.spidermiddlewares.offsite.OffsiteMiddleware

スパイダーがカバーするドメイン外のURLに対するリクエストをフィルタリングします.

このミドルウェアは, スパイダーの allowed_domains 属性にないホスト名を持つすべてのリクエストをフィルタリングします. リスト内の任意のドメインのすべてのサブドメインも許可されます. 例えば, ルールに www.example.org が指定されている場合でも bob.www.example.org は許可されますが, www2.example.com や example.com は許可されません.

スパイダーが許可されていないドメインのリクエストを返すと, このミドルウェアはこのようなデバッグメッセージを記録します:

DEBUG: Filtered offsite request to 'www.othersite.com': <GET http://www.othersite.com/some/page.html>

あまりにも多くのノイズをログに書き込まないようにするため, フィルタリングされた新しいドメインごとにこれらのメッセージの1つのみを出力します. たとえば, www.othersite.com の別のリクエストがフィルタリングされた場合, ログメッセージは出力されません. しかし, someothersite.com のリクエストがフィルタリングされると, メッセージが出力されます(最初のリクエストがフィルタリングされた場合のみ).

スパイダーが allowed_domains 属性を定義していないか, 属性が空の場合, オフサイトミドルウェアはすべてのリクエストを許可します.

リクエストに dont_filter 属性が設定されている場合, オフサイトミドルウェアはドメインが許可リストになくてもリクエストを許可します.

RefererMiddleware
class scrapy.spidermiddlewares.referer.RefererMiddleware

リクエストの Referer ヘッダーに, レスポンスのURLに基づいてヘッダーを挿入します.

RefererMiddleware 設定
REFERER_ENABLED

バージョン 0.15 で追加.

デフォルト: True

リファラーミドルウェアを有効にするかどうか.

UrlLengthMiddleware
class scrapy.spidermiddlewares.urllength.UrlLengthMiddleware

URL が URLLENGTH_LIMIT より長い場合, リクエストをフィルタリングします

The UrlLengthMiddleware は, 以下の設定によって構成することができます(詳細については, 設定ドキュメントを参照してください):

  • URLLENGTH_LIMIT - クロールするURLで許可されるURLの最大長.

拡張機能

The extensions framework provides a mechanism for inserting your own custom functionality into Scrapy.

Extensions are just regular classes that are instantiated at Scrapy startup, when extensions are initialized.

拡張機能設定

Extensions use the Scrapy settings to manage their settings, just like any other Scrapy code.

It is customary for extensions to prefix their settings with their own name, to avoid collision with existing (and future) extensions. For example, a hypothetic extension to handle Google Sitemaps would use settings like GOOGLESITEMAP_ENABLED, GOOGLESITEMAP_DEPTH, and so on.

拡張機能の読み込みと有効化

Extensions are loaded and activated at startup by instantiating a single instance of the extension class. Therefore, all the extension initialization code must be performed in the class constructor (__init__ method).

To make an extension available, add it to the EXTENSIONS setting in your Scrapy settings. In EXTENSIONS, each extension is represented by a string: the full Python path to the extension’s class name. For example:

EXTENSIONS = {
    'scrapy.extensions.corestats.CoreStats': 500,
    'scrapy.extensions.telnet.TelnetConsole': 500,
}

As you can see, the EXTENSIONS setting is a dict where the keys are the extension paths, and their values are the orders, which define the extension loading order. The EXTENSIONS setting is merged with the EXTENSIONS_BASE setting defined in Scrapy (and not meant to be overridden) and then sorted by order to get the final sorted list of enabled extensions.

As extensions typically do not depend on each other, their loading order is irrelevant in most cases. This is why the EXTENSIONS_BASE setting defines all extensions with the same order (0). However, this feature can be exploited if you need to add an extension which depends on other extensions already loaded.

利用可能, 有効, 無効な拡張機能

Not all available extensions will be enabled. Some of them usually depend on a particular setting. For example, the HTTP Cache extension is available by default but disabled unless the HTTPCACHE_ENABLED setting is set.

拡張機能の無効化

In order to disable an extension that comes enabled by default (ie. those included in the EXTENSIONS_BASE setting) you must set its order to None. For example:

EXTENSIONS = {
    'scrapy.extensions.corestats.CoreStats': None,
}

独自の拡張機能を書く

Each extension is a Python class. The main entry point for a Scrapy extension (this also includes middlewares and pipelines) is the from_crawler class method which receives a Crawler instance. Through the Crawler object you can access settings, signals, stats, and also control the crawling behaviour.

Typically, extensions connect to signals and perform tasks triggered by them.

Finally, if the from_crawler method raises the NotConfigured exception, the extension will be disabled. Otherwise, the extension will be enabled.

かんたんな拡張機能

Here we will implement a simple extension to illustrate the concepts described in the previous section. This extension will log a message every time:

  • a spider is opened
  • a spider is closed
  • a specific number of items are scraped

The extension will be enabled through the MYEXT_ENABLED setting and the number of items will be specified through the MYEXT_ITEMCOUNT setting.

Here is the code of such extension:

import logging
from scrapy import signals
from scrapy.exceptions import NotConfigured

logger = logging.getLogger(__name__)

class SpiderOpenCloseLogging(object):

    def __init__(self, item_count):
        self.item_count = item_count
        self.items_scraped = 0

    @classmethod
    def from_crawler(cls, crawler):
        # first check if the extension should be enabled and raise
        # NotConfigured otherwise
        if not crawler.settings.getbool('MYEXT_ENABLED'):
            raise NotConfigured

        # get the number of items from settings
        item_count = crawler.settings.getint('MYEXT_ITEMCOUNT', 1000)

        # instantiate the extension object
        ext = cls(item_count)

        # connect the extension object to signals
        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
        crawler.signals.connect(ext.item_scraped, signal=signals.item_scraped)

        # return the extension object
        return ext

    def spider_opened(self, spider):
        logger.info("opened spider %s", spider.name)

    def spider_closed(self, spider):
        logger.info("closed spider %s", spider.name)

    def item_scraped(self, item, spider):
        self.items_scraped += 1
        if self.items_scraped % self.item_count == 0:
            logger.info("scraped %d items", self.items_scraped)

ビルトイン拡張機能リファレンス

汎用的な拡張機能
ログ統計拡張機能
class scrapy.extensions.logstats.LogStats

Log basic stats like crawled pages and scraped items.

コア統計拡張機能
class scrapy.extensions.corestats.CoreStats

Enable the collection of core statistics, provided the stats collection is enabled (see 統計コレクション).

Telnetコンソール拡張機能
class scrapy.extensions.telnet.TelnetConsole

Provides a telnet console for getting into a Python interpreter inside the currently running Scrapy process, which can be very useful for debugging.

The telnet console must be enabled by the TELNETCONSOLE_ENABLED setting, and the server will listen in the port specified in TELNETCONSOLE_PORT.

メモリ使用量拡張機能
class scrapy.extensions.memusage.MemoryUsage

注釈

This extension does not work in Windows.

Monitors the memory used by the Scrapy process that runs the spider and:

  1. sends a notification e-mail when it exceeds a certain value
  2. closes the spider when it exceeds a certain value

The notification e-mails can be triggered when a certain warning value is reached (MEMUSAGE_WARNING_MB) and when the maximum value is reached (MEMUSAGE_LIMIT_MB) which will also cause the spider to be closed and the Scrapy process to be terminated.

This extension is enabled by the MEMUSAGE_ENABLED setting and can be configured with the following settings:

メモリデバッガ拡張機能
class scrapy.extensions.memdebug.MemoryDebugger

An extension for debugging memory usage. It collects information about:

To enable this extension, turn on the MEMDEBUG_ENABLED setting. The info will be stored in the stats.

クローズスパイダー拡張機能
class scrapy.extensions.closespider.CloseSpider

Closes a spider automatically when some conditions are met, using a specific closing reason for each condition.

The conditions for closing a spider can be configured through the following settings:

CLOSESPIDER_TIMEOUT

Default: 0

An integer which specifies a number of seconds. If the spider remains open for more than that number of second, it will be automatically closed with the reason closespider_timeout. If zero (or non set), spiders won’t be closed by timeout.

CLOSESPIDER_ITEMCOUNT

Default: 0

An integer which specifies a number of items. If the spider scrapes more than that amount if items and those items are passed by the item pipeline, the spider will be closed with the reason closespider_itemcount. If zero (or non set), spiders won’t be closed by number of passed items.

CLOSESPIDER_PAGECOUNT

バージョン 0.11 で追加.

Default: 0

An integer which specifies the maximum number of responses to crawl. If the spider crawls more than that, the spider will be closed with the reason closespider_pagecount. If zero (or non set), spiders won’t be closed by number of crawled responses.

CLOSESPIDER_ERRORCOUNT

バージョン 0.11 で追加.

Default: 0

An integer which specifies the maximum number of errors to receive before closing the spider. If the spider generates more than that number of errors, it will be closed with the reason closespider_errorcount. If zero (or non set), spiders won’t be closed by number of errors.

StatsMailer 拡張機能
class scrapy.extensions.statsmailer.StatsMailer

This simple extension can be used to send a notification e-mail every time a domain has finished scraping, including the Scrapy stats collected. The email will be sent to all recipients specified in the STATSMAILER_RCPTS setting.

デバッギング拡張機能
スタックトレースダンプ拡張
class scrapy.extensions.debug.StackTraceDump

Dumps information about the running process when a SIGQUIT or SIGUSR2 signal is received. The information dumped is the following:

  1. engine status (using scrapy.utils.engine.get_engine_status())
  2. live references (see trackref でメモリリークのデバッグをする)
  3. stack trace of all threads

After the stack trace and engine status is dumped, the Scrapy process continues running normally.

This extension only works on POSIX-compliant platforms (ie. not Windows), because the SIGQUIT and SIGUSR2 signals are not available on Windows.

There are at least two ways to send Scrapy the SIGQUIT signal:

  1. By pressing Ctrl-while a Scrapy process is running (Linux only?)

  2. By running this command (assuming <pid> is the process id of the Scrapy process):

    kill -QUIT <pid>
    
デバッガ拡張機能
class scrapy.extensions.debug.Debugger

Invokes a Python debugger inside a running Scrapy process when a SIGUSR2 signal is received. After the debugger is exited, the Scrapy process continues running normally.

For more info see Debugging in Python.

This extension only works on POSIX-compliant platforms (ie. not Windows).

コア API

バージョン 0.15 で追加.

このセクションは, ScrapyコアAPIについて説明します. 拡張APIやミドルウェアの開発者向けです.

クローラー API

Scrapy API の主なエントリポイントは, from_crawler メソッドを使用して拡張機能に渡される Crawler オブジェクトです. このオブジェクトはすべての Scrapy コアコンポーネントへのアクセスを提供し, 拡張機能がそれらにアクセスして機能を Scrapy に引き込む唯一の方法です.

拡張機能マネージャーは, インストールされている拡張機能の読み込みと追跡を担当しており, 使用可能なすべての拡張機能の辞書と, ダウンローダーミドルウェアの設定 方法と同様の命令を含む EXTENSIONS 設定によって構成されています.

class scrapy.crawler.Crawler(spidercls, settings)

Crawler オブジェクトは, scrapy.spiders.Spider サブクラスと scrapy.settings.Settings オブジェクトでインスタンス化する必要があります.

settings

クローラの設定マネージャ.

これは, このクローラーのScrapy設定にアクセスするために拡張機能とミドルウェアによって使用されます.

詳細については,  設定 を参照してください.

APIについては, Settings クラスを参照してください.

signals

クローラーの信号マネージャ.

これは, 拡張機能とミドルウェアがScrapy機能にフックするために使用されます.

詳細については, シグナル を参照してください.

APIについては, SignalManager クラスを参照してください.

stats

クローラーの統計コレクタ.

これは, エクステンションとミドルウェアから自分の行動の統計情報を記録したり, 他の拡張機能によって収集された統計情報にアクセスするために使用されます.

詳細については 統計コレクション を参照してください.

API については StatsCollector クラスを参照してください.

extensions

有効な拡張機能を追跡する拡張マネージャー.

ほとんどの拡張機能はこの属性にアクセスする必要はありません.

拡張機能の紹介とScrapyの利用可能な拡張機能のリストについては, 拡張機能 を参照してください.

engine

実行エンジン. スケジューラ, ダウンローダ, およびスパイダ間のコアクロールロジックを調整します.

Scrapy エンジンにアクセスして, ダウンローダとスケジューラの動作を検査または変更したい場合にしようできます. ただし, これは高度な使い方であり, APIはまだ安定していません.

spider

Spider currently being crawled. クローラの構築中に提供されるspiderクラスのインスタンスであり, crawl() メソッドで指定された引数の後に作成されます.

crawl(*args, **kwargs)

クローラー開始時に, 指定された args および kwargs 引数を使用してスパイダーをインスタンス化し, 実行エンジンの動作を設定します.

クロールが終了したときに発生する遅延を返します.

class scrapy.crawler.CrawlerRunner(settings=None)

This is a convenient helper class that keeps track of, manages and runs crawlers inside an already setup Twisted reactor.

The CrawlerRunner object must be instantiated with a Settings object.

This class shouldn’t be needed (since Scrapy is responsible of using it accordingly) unless writing scripts that manually handle the crawling process. See スクリプトから Scrapy を実行する for an example.

crawl(crawler_or_spidercls, *args, **kwargs)

Run a crawler with the provided arguments.

It will call the given Crawler’s crawl() method, while keeping track of it so it can be stopped later.

If crawler_or_spidercls isn’t a Crawler instance, this method will try to create one using this parameter as the spider class given to it.

Returns a deferred that is fired when the crawling is finished.

パラメータ:
  • crawler_or_spidercls (Crawler instance, Spider subclass or string) – already created crawler, or a spider class or spider’s name inside the project to create it
  • args (list) – arguments to initialize the spider
  • kwargs (dict) – keyword arguments to initialize the spider
crawlers

Set of crawlers started by crawl() and managed by this class.

create_crawler(crawler_or_spidercls)

Return a Crawler object.

  • If crawler_or_spidercls is a Crawler, it is returned as-is.
  • If crawler_or_spidercls is a Spider subclass, a new Crawler is constructed for it.
  • If crawler_or_spidercls is a string, this function finds a spider with this name in a Scrapy project (using spider loader), then creates a Crawler instance for it.
join()

Returns a deferred that is fired when all managed crawlers have completed their executions.

stop()

Stops simultaneously all the crawling jobs taking place.

Returns a deferred that is fired when they all have ended.

class scrapy.crawler.CrawlerProcess(settings=None)

ベースクラス: scrapy.crawler.CrawlerRunner

A class to run multiple scrapy crawlers in a process simultaneously.

This class extends CrawlerRunner by adding support for starting a Twisted reactor and handling shutdown signals, like the keyboard interrupt command Ctrl-C. It also configures top-level logging.

This utility should be a better fit than CrawlerRunner if you aren’t running another Twisted reactor within your application.

The CrawlerProcess object must be instantiated with a Settings object.

This class shouldn’t be needed (since Scrapy is responsible of using it accordingly) unless writing scripts that manually handle the crawling process. See スクリプトから Scrapy を実行する for an example.

crawl(crawler_or_spidercls, *args, **kwargs)

Run a crawler with the provided arguments.

It will call the given Crawler’s crawl() method, while keeping track of it so it can be stopped later.

If crawler_or_spidercls isn’t a Crawler instance, this method will try to create one using this parameter as the spider class given to it.

Returns a deferred that is fired when the crawling is finished.

パラメータ:
  • crawler_or_spidercls (Crawler instance, Spider subclass or string) – already created crawler, or a spider class or spider’s name inside the project to create it
  • args (list) – arguments to initialize the spider
  • kwargs (dict) – keyword arguments to initialize the spider
crawlers

Set of crawlers started by crawl() and managed by this class.

create_crawler(crawler_or_spidercls)

Return a Crawler object.

  • If crawler_or_spidercls is a Crawler, it is returned as-is.
  • If crawler_or_spidercls is a Spider subclass, a new Crawler is constructed for it.
  • If crawler_or_spidercls is a string, this function finds a spider with this name in a Scrapy project (using spider loader), then creates a Crawler instance for it.
join()

Returns a deferred that is fired when all managed crawlers have completed their executions.

start(stop_after_crawl=True)

This method starts a Twisted reactor, adjusts its pool size to REACTOR_THREADPOOL_MAXSIZE, and installs a DNS cache based on DNSCACHE_ENABLED and DNSCACHE_SIZE.

If stop_after_crawl is True, the reactor will be stopped after all crawlers have finished, using join().

パラメータ:stop_after_crawl (boolean) – stop or not the reactor when all crawlers have finished
stop()

Stops simultaneously all the crawling jobs taking place.

Returns a deferred that is fired when they all have ended.

設定 API

scrapy.settings.SETTINGS_PRIORITIES

Scrapyで使用されるデフォルトの設定優先度のキー名と優先度を辞書形式で設定します.

各項目は設定エントリポイントを定義し, 識別のためのコード名と整数の優先順位を与えます. Settings クラスで値を設定したり取得したりするときは, 優先度が高いほど優先度が低くなります.

SETTINGS_PRIORITIES = {
    'default': 0,
    'command': 10,
    'project': 20,
    'spider': 30,
    'cmdline': 40,
}

各設定の詳細については, 設定 を参照してください.

scrapy.settings.get_settings_priority(priority)

Small helper function that looks up a given string priority in the SETTINGS_PRIORITIES dictionary and returns its numerical value, or directly returns a given numerical priority.

class scrapy.settings.Settings(values=None, priority='project')

ベースクラス: scrapy.settings.BaseSettings

This object stores Scrapy settings for the configuration of internal components, and can be used for any further customization.

It is a direct subclass and supports all methods of BaseSettings. Additionally, after instantiation of this class, the new object will have the global default settings described on ビルトイン設定リファレンス already populated.

class scrapy.settings.BaseSettings(values=None, priority='project')

Instances of this class behave like dictionaries, but store priorities along with their (key, value) pairs, and can be frozen (i.e. marked immutable).

Key-value entries can be passed on initialization with the values argument, and they would take the priority level (unless values is already an instance of BaseSettings, in which case the existing priority levels will be kept). If the priority argument is a string, the priority name will be looked up in SETTINGS_PRIORITIES. Otherwise, a specific integer should be provided.

Once the object is created, new settings can be loaded or updated with the set() method, and can be accessed with the square bracket notation of dictionaries, or with the get() method of the instance and its value conversion variants. When requesting a stored key, the value with the highest priority will be retrieved.

copy()

Make a deep copy of current settings.

This method returns a new instance of the Settings class, populated with the same values and their priorities.

Modifications to the new object won’t be reflected on the original settings.

copy_to_dict()

Make a copy of current settings and convert to a dict.

This method returns a new dict populated with the same values and their priorities as the current settings.

Modifications to the returned dict won’t be reflected on the original settings.

This method can be useful for example for printing settings in Scrapy shell.

freeze()

Disable further changes to the current settings.

After calling this method, the present state of the settings will become immutable. Trying to change values through the set() method and its variants won’t be possible and will be alerted.

frozencopy()

Return an immutable copy of the current settings.

Alias for a freeze() call in the object returned by copy().

get(name, default=None)

Get a setting value without affecting its original type.

パラメータ:
  • name (string) – the setting name
  • default (any) – the value to return if no setting is found
getbool(name, default=False)

Get a setting value as a boolean.

1, '1', and True return True, while 0, '0', False and None return False.

For example, settings populated through environment variables set to '0' will return False when using this method.

パラメータ:
  • name (string) – the setting name
  • default (any) – the value to return if no setting is found
getdict(name, default=None)

Get a setting value as a dictionary. If the setting original type is a dictionary, a copy of it will be returned. If it is a string it will be evaluated as a JSON dictionary. In the case that it is a BaseSettings instance itself, it will be converted to a dictionary, containing all its current settings values as they would be returned by get(), and losing all information about priority and mutability.

パラメータ:
  • name (string) – the setting name
  • default (any) – the value to return if no setting is found
getfloat(name, default=0.0)

Get a setting value as a float.

パラメータ:
  • name (string) – the setting name
  • default (any) – the value to return if no setting is found
getint(name, default=0)

Get a setting value as an int.

パラメータ:
  • name (string) – the setting name
  • default (any) – the value to return if no setting is found
getlist(name, default=None)

Get a setting value as a list. If the setting original type is a list, a copy of it will be returned. If it’s a string it will be split by ”,”.

For example, settings populated through environment variables set to 'one,two' will return a list [‘one’, ‘two’] when using this method.

パラメータ:
  • name (string) – the setting name
  • default (any) – the value to return if no setting is found
getpriority(name)

Return the current numerical priority value of a setting, or None if the given name does not exist.

パラメータ:name (string) – the setting name
getwithbase(name)

Get a composition of a dictionary-like setting and its _BASE counterpart.

パラメータ:name (string) – name of the dictionary-like setting
maxpriority()

Return the numerical value of the highest priority present throughout all settings, or the numerical value for default from SETTINGS_PRIORITIES if there are no settings stored.

set(name, value, priority='project')

Store a key/value attribute with a given priority.

Settings should be populated before configuring the Crawler object (through the configure() method), otherwise they won’t have any effect.

パラメータ:
  • name (string) – the setting name
  • value (any) – the value to associate with the setting
  • priority (string or int) – the priority of the setting. Should be a key of SETTINGS_PRIORITIES or an integer
setmodule(module, priority='project')

Store settings from a module with a given priority.

This is a helper function that calls set() for every globally declared uppercase variable of module with the provided priority.

パラメータ:
  • module (module object or string) – the module or the path of the module
  • priority (string or int) – the priority of the settings. Should be a key of SETTINGS_PRIORITIES or an integer
update(values, priority='project')

Store key/value pairs with a given priority.

This is a helper function that calls set() for every item of values with the provided priority.

If values is a string, it is assumed to be JSON-encoded and parsed into a dict with json.loads() first. If it is a BaseSettings instance, the per-key priorities will be used and the priority parameter ignored. This allows inserting/updating settings with different priorities with a single command.

パラメータ:
  • values (dict or string or BaseSettings) – the settings names and values
  • priority (string or int) – the priority of the settings. Should be a key of SETTINGS_PRIORITIES or an integer

スパイダーローダー API

class scrapy.loader.SpiderLoader

このクラスは, プロジェクト全体で定義されたスパイダークラスの取得と処理を担当します.

SPIDER_LOADER_CLASS プロジェクト設定でパスを指定することで, カスタムスパイダーローダーを使用できます. エラーのない実行を保証するために scrapy.interfaces.ISpiderLoader インタフェースを完全に実装する必要があります.

from_settings(settings)

このクラスメソッドは, クラスのインスタンスを作成するためにScrapyによって使用されます. プロジェクト設定で呼び出され,  SPIDER_MODULES 設定で指定したモジュール内で再帰的に見つかったスパイダーをロードします.

パラメータ:settings (Settings インスタンス) – プロジェクト設定
load(spider_name)

Spider クラスを指定された名前で取得します. 以前に読み込まれたスパイダーのうち, spider_name という名前の Spider クラスが検索され, 見つからなければ KeyError が発生します.

パラメータ:spider_name (str) – Spider クラス名
list()

プロジェクトで利用可能なスパイダーの名前を取得する.

find_by_request(request)

指定されたリクエストを処理できるスパイダーの名前をリストします. 要求のURLとスパイダーのドメインを照合しようとします.

パラメータ:request (Request インスタンス) – 問い合わされたリクエスト

シグナル API

class scrapy.signalmanager.SignalManager(sender=_Anonymous)
connect(receiver, signal, **kwargs)

Connect a receiver function to a signal.

The signal can be any object, although Scrapy comes with some predefined signals that are documented in the シグナル section.

パラメータ:
  • receiver (callable) – the function to be connected
  • signal (object) – the signal to connect to
disconnect(receiver, signal, **kwargs)

Disconnect a receiver function from a signal. This has the opposite effect of the connect() method, and the arguments are the same.

disconnect_all(signal, **kwargs)

Disconnect all receivers from the given signal.

パラメータ:signal (object) – the signal to disconnect from
send_catch_log(signal, **kwargs)

Send a signal, catch exceptions and log them.

The keyword arguments are passed to the signal handlers (connected through the connect() method).

send_catch_log_deferred(signal, **kwargs)

Like send_catch_log() but supports returning deferreds from signal handlers.

Returns a Deferred that gets fired once all signal handlers deferreds were fired. Send a signal, catch exceptions and log them.

The keyword arguments are passed to the signal handlers (connected through the connect() method).

統計コレクター API

scrapy.statscollectors モジュールの下に利用可能ないくつかの統計コレクタがあり, それらはすべて StatsCollector クラスで定義されたStatsコレクタAPIを実装しています(すべて継承しています).

class scrapy.statscollectors.StatsCollector
get_value(key, default=None)

指定されたstatsキーの値を返します. 存在しない場合はdefaultを返します.

get_stats()

現在実行中のスパイダーからすべての統計情報を dict として取得します.

set_value(key, value)

与えられた stats キーに与えられた値を設定します.

set_stats(stats)

stats 引数に渡された dict で現在の統計をオーバーライドします.

inc_value(key, count=1, start=0)

指定された統計値キーの値を与えられた数だけ増やします(設定されていない場合は, 与えられた開始値を仮定する).

max_value(key, value)

指定されたキーの現在の値が value よりも小さい場合にのみ, 指定されたキーの値に value を設定します. 指定されたキーに現在の値がない場合, 値は常に設定されます.

min_value(key, value)

指定されたキーの現在の値が value より大きい場合にのみ, 指定されたキーの値に value を設定します. 指定されたキーに現在の値がない場合, 値は常に設定されます.

clear_stats()

すべての統計情報をクリアします.

次のメソッドは, 統計収集APIの一部ではなく, カスタム統計コレクタを実装するときに使用されます:

open_spider(spider)

統計収集のために指定されたスパイダーを開きます.

close_spider(spider)

指定されたスパイダーを閉じます. これが呼び出された後, 特定の統計情報にアクセスまたは収集することはできません.

シグナル

Scrapy uses signals extensively to notify when certain events occur. You can catch some of those signals in your Scrapy project (using an extension, for example) to perform additional tasks or extend Scrapy to add functionality not provided out of the box.

Even though signals provide several arguments, the handlers that catch them don’t need to accept all of them - the signal dispatching mechanism will only deliver the arguments that the handler receives.

You can connect to signals (or send your own) through the シグナル API.

Here is a simple example showing how you can catch signals and perform some action:

from scrapy import signals
from scrapy import Spider


class DmozSpider(Spider):
    name = "dmoz"
    allowed_domains = ["dmoz.org"]
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/",
    ]


    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = super(DmozSpider, cls).from_crawler(crawler, *args, **kwargs)
        crawler.signals.connect(spider.spider_closed, signal=signals.spider_closed)
        return spider


    def spider_closed(self, spider):
        spider.logger.info('Spider closed: %s', spider.name)


    def parse(self, response):
        pass

遅延シグナルハンドラ

Some signals support returning Twisted deferreds from their handlers, see the ビルトインシグナルリファレンス below to know which ones.

ビルトインシグナルリファレンス

Scrapy のビルトインシグナルのリストとその意味は次のとおりです.

engine_started
scrapy.signals.engine_started()

Sent when the Scrapy engine has started crawling.

This signal supports returning deferreds from their handlers.

注釈

This signal may be fired after the spider_opened signal, depending on how the spider was started. So don’t rely on this signal getting fired before spider_opened.

engine_stopped
scrapy.signals.engine_stopped()

Sent when the Scrapy engine is stopped (for example, when a crawling process has finished).

This signal supports returning deferreds from their handlers.

item_scraped
scrapy.signals.item_scraped(item, response, spider)

Sent when an item has been scraped, after it has passed all the アイテムパイプライン stages (without being dropped).

This signal supports returning deferreds from their handlers.

パラメータ:
  • item (dict or Item object) – the item scraped
  • spider (Spider object) – the spider which scraped the item
  • response (Response object) – the response from where the item was scraped
item_dropped
scrapy.signals.item_dropped(item, response, exception, spider)

Sent after an item has been dropped from the アイテムパイプライン when some stage raised a DropItem exception.

This signal supports returning deferreds from their handlers.

パラメータ:
  • item (dict or Item object) – the item dropped from the アイテムパイプライン
  • spider (Spider object) – the spider which scraped the item
  • response (Response object) – the response from where the item was dropped
  • exception (DropItem exception) – the exception (which must be a DropItem subclass) which caused the item to be dropped
spider_closed
scrapy.signals.spider_closed(spider, reason)

Sent after a spider has been closed. This can be used to release per-spider resources reserved on spider_opened.

This signal supports returning deferreds from their handlers.

パラメータ:
  • spider (Spider object) – the spider which has been closed
  • reason (str) – a string which describes the reason why the spider was closed. If it was closed because the spider has completed scraping, the reason is 'finished'. Otherwise, if the spider was manually closed by calling the close_spider engine method, then the reason is the one passed in the reason argument of that method (which defaults to 'cancelled'). If the engine was shutdown (for example, by hitting Ctrl-C to stop it) the reason will be 'shutdown'.
spider_opened
scrapy.signals.spider_opened(spider)

Sent after a spider has been opened for crawling. This is typically used to reserve per-spider resources, but can be used for any task that needs to be performed when a spider is opened.

This signal supports returning deferreds from their handlers.

パラメータ:spider (Spider object) – the spider which has been opened
spider_idle
scrapy.signals.spider_idle(spider)

Sent when a spider has gone idle, which means the spider has no further:

  • requests waiting to be downloaded
  • requests scheduled
  • items being processed in the item pipeline

If the idle state persists after all handlers of this signal have finished, the engine starts closing the spider. After the spider has finished closing, the spider_closed signal is sent.

You can, for example, schedule some requests in your spider_idle handler to prevent the spider from being closed.

This signal does not support returning deferreds from their handlers.

パラメータ:spider (Spider object) – the spider which has gone idle
spider_error
scrapy.signals.spider_error(failure, response, spider)

Sent when a spider callback generates an error (ie. raises an exception).

This signal does not support returning deferreds from their handlers.

パラメータ:
  • failure (Failure object) – the exception raised as a Twisted Failure object
  • response (Response object) – the response being processed when the exception was raised
  • spider (Spider object) – the spider which raised the exception
request_scheduled
scrapy.signals.request_scheduled(request, spider)

Sent when the engine schedules a Request, to be downloaded later.

The signal does not support returning deferreds from their handlers.

パラメータ:
  • request (Request object) – the request that reached the scheduler
  • spider (Spider object) – the spider that yielded the request
request_dropped
scrapy.signals.request_dropped(request, spider)

Sent when a Request, scheduled by the engine to be downloaded later, is rejected by the scheduler.

The signal does not support returning deferreds from their handlers.

パラメータ:
  • request (Request object) – the request that reached the scheduler
  • spider (Spider object) – the spider that yielded the request
response_received
scrapy.signals.response_received(response, request, spider)

Sent when the engine receives a new Response from the downloader.

This signal does not support returning deferreds from their handlers.

パラメータ:
  • response (Response object) – the response received
  • request (Request object) – the request that generated the response
  • spider (Spider object) – the spider for which the response is intended
response_downloaded
scrapy.signals.response_downloaded(response, request, spider)

Sent by the downloader right after a HTTPResponse is downloaded.

This signal does not support returning deferreds from their handlers.

パラメータ:
  • response (Response object) – the response downloaded
  • request (Request object) – the request that generated the response
  • spider (Spider object) – the spider for which the response is intended

アイテムエクスポーター

アイテムを集めたら, そのアイテムを永続化またはエクスポートして, 他のアプリケーションでデータを使用することがしばしばあります. つまり, 結局のところ, スクレイピング工程の全目的です.

この目的のため, Scrapy は, XML, CSV, JSON などのさまざまな出力形式に対しアイテムエクスポーターのコレクションを提供します.

アイテムエクスポーターを使う

あなたが急いでいて, アイテムエクスポータを使用してスクレイピングしたデータを出力したい場合は, フィードのエクスポート を参照してください. そうではなく, アイテムエクスポータがどのように機能するかを知りたい, もしくは (デフォルトのエクスポートでカバーされていない)カスタム機能をもっと必要とする場合は以下をお読みください.

アイテムエクスポーターを使用するには, 必要な引数でインスタンス化する必要があります. 各エクスポーターは異なる引数を必要とするため, in ビルトインアイテムエクスポーターリファレンス を参照してください. エクスポーターのインスタンスを作成した後は, 以下の作業を行う必要があります:

  1. エクスポートプロセスの開始を知らせるために start_exporting() メソッドを呼び出します.
  2. エクスポートする各アイテムの export_item() メソッドを呼び出します.
  3. 最後に finish_exporting() メソッドを呼び出してエクスポートプロセスの終了を知らせます.

ここで アイテムパイプライン を使用して, スクレイピングしたアイテムを異なるファイルにエクスポートするアイテムパイプライン(スパイダーごとに1つ)の例を紹介します:

from scrapy import signals
from scrapy.exporters import XmlItemExporter

class XmlExportPipeline(object):

    def __init__(self):
        self.files = {}

     @classmethod
     def from_crawler(cls, crawler):
         pipeline = cls()
         crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
         crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
         return pipeline

    def spider_opened(self, spider):
        file = open('%s_products.xml' % spider.name, 'w+b')
        self.files[spider] = file
        self.exporter = XmlItemExporter(file)
        self.exporter.start_exporting()

    def spider_closed(self, spider):
        self.exporter.finish_exporting()
        file = self.files.pop(spider)
        file.close()

    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

アイテムフィールドのシリアル化

デフォルトでは, フィールド値は未変更のシリアル化ライブラリに渡され, シリアル化する方法の決定権は各シリアル化ライブラリに委譲されます.

ただし, 各フィールドの値がシリアライゼーションライブラリに渡される前に, シリアライズされる方法をカスタマイズできます.

フィールドのシリアル化方法をカスタマイズするには, 次の2つの方法があります.

1. フィールド内のシリアライザの宣言

Item を使用する場合, フィールドメタデータ でシリアライザを宣言できます. シリアライザは, 呼び出し可能な値を受け取ってシリアル化された値を返す形式でなければなりません.

例:

import scrapy

def serialize_price(value):
    return '$ %s' % str(value)

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field(serializer=serialize_price)
2. serialize_field() メソッドのオーバーライド

serialize_field() メソッドをオーバーライドして、フィールド値のエクスポート方法をカスタマイズすることもできます.

カスタムコードの後に, 基本クラスの serialize_field() メソッドを呼び出すようにしてください.

例:

from scrapy.exporter import XmlItemExporter

class ProductXmlExporter(XmlItemExporter):

    def serialize_field(self, field, name, value):
        if field == 'price':
            return '$ %s' % str(value)
        return super(Product, self).serialize_field(field, name, value)

ビルトインアイテムエクスポーターリファレンス

以下に, Scrapy にバンドルされているアイテムエクスポータのリストがあります. これらの中には, 2つのアイテムをエクスポートしていると仮定した出力例が含まれています:

Item(name='Color TV', price='1200')
Item(name='DVD player', price='200')
BaseItemExporter
class scrapy.exporters.BaseItemExporter(fields_to_export=None, export_empty_fields=False, encoding='utf-8')

これは, すべてのアイテムエクスポータの(抽象)基本クラスです. エクスポートするフィールドの定義, 空のフィールドのエクスポートの指定, 使用するエンコーディングなど, すべての(具体的な)アイテムエクスポータで使用される共通の機能をサポートしています.

これらの機能は, fields_to_export, export_empty_fields, encoding のそれぞれのインスタンス属性を設定するコンストラクタ引数で設定できます.

export_item(item)

  指定された項目をエクスポートします. このメソッドはサブクラスで実装する必要があります.

serialize_field(field, name, value)

  指定されたフィールドの直列化された値を返します.   特定のフィールドまたは値のシリアライズ/エクスポートの方法を制御する場合は,

カスタムアイテムエクスポータでこのメソッドをオーバーライドできます.
  デフォルトでは、このメソッドは フィールドで宣言された シリアライザを検索し,
そのシリアライザをその値に適用した結果を返します. シリアライザが見つからない場合, encoding 属性で宣言されたエンコーディングを使用して

   エンコードされた strunicode 値を除いて, 値は変更されません.

param field:フィールドはシリアル化されています。

      生の dict がエクスポートされている場合(Item ではなく)*フィールド* 値は空の dict です.   :type field: Field オブジェクトまたは空の dict

param name:シリアル化されているフィールドの名前
type name:str

  :param value: シリアル化された値

start_exporting()
エクスポートプロセスの開始を知らせます.

  一部のエクスポーターは, これを使用して必要なヘッダー(たとえば, XmlItemExporter)を生成することがあります.   アイテムをエクスポートする前に, このメソッドを呼び出す必要があります.

finish_exporting()

エクスポートプロセスの終了を知らせます.

  一部のエクスポーターは, これを使用して必要なフッター (たとえば, XmlItemExporter のような)を生成することがあります.   エクスポートする項目がなくなったら, 必ずこのメソッドを呼び出す必要があります.

fields_to_export

  エクスポートされるフィールドの名前を持つリスト, またはすべてのフィールドをエクスポートする場合は None です.   デフォルトは None です.

  一部のエクスポーター ( CsvItemExporter など) は, この属性で定義されたフィールドの順序を尊重します.

  いくつかのエクスポーターは, スパイダーが ( Item インスタンスでない) dictsを返すとき,   データを適切にエクスポートするために fields_to_export リストを要求することがあります.

export_empty_fields

エクスポートされたデータに空のフィールドフィールドまたは空になっていないアイテムフィールドを含めるかどうか.

  デフォルトは False です. 一部のエクスポーター ( CsvItemExporter など)   はこの属性を無視し、空のフィールドを常にエクスポートします.   このオプションは dict 項目では無視されます.

encoding

  Unicode値をエンコードするために使用されるエンコード.   これはUnicode値にのみ影響します(このエンコーディングを使用してstrに常にシリアル化されます).   他の値の型は, 変更されずに特定の直列化ライブラリに渡されます.

XmlItemExporter
class scrapy.exporters.XmlItemExporter(file, item_element='item', root_element='items', **kwargs)

Exports Items in XML format to the specified file object.

パラメータ:
  • file – the file-like object to use for exporting the data.
  • root_element (str) – The name of root element in the exported XML.
  • item_element (str) – The name of each item element in the exported XML.

The additional keyword arguments of this constructor are passed to the BaseItemExporter constructor.

A typical output of this exporter would be:

<?xml version="1.0" encoding="utf-8"?>
<items>
  <item>
    <name>Color TV</name>
    <price>1200</price>
 </item>
  <item>
    <name>DVD player</name>
    <price>200</price>
 </item>
</items>

Unless overridden in the serialize_field() method, multi-valued fields are exported by serializing each value inside a <value> element. This is for convenience, as multi-valued fields are very common.

For example, the item:

Item(name=['John', 'Doe'], age='23')

Would be serialized as:

<?xml version="1.0" encoding="utf-8"?>
<items>
  <item>
    <name>
      <value>John</value>
      <value>Doe</value>
    </name>
    <age>23</age>
  </item>
</items>
CsvItemExporter
class scrapy.exporters.CsvItemExporter(file, include_headers_line=True, join_multivalued=', ', **kwargs)

Exports Items in CSV format to the given file-like object. If the fields_to_export attribute is set, it will be used to define the CSV columns and their order. The export_empty_fields attribute has no effect on this exporter.

パラメータ:
  • file – the file-like object to use for exporting the data.
  • include_headers_line (str) – If enabled, makes the exporter output a header line with the field names taken from BaseItemExporter.fields_to_export or the first exported item fields.
  • join_multivalued – The char (or chars) that will be used for joining multi-valued fields, if found.

The additional keyword arguments of this constructor are passed to the BaseItemExporter constructor, and the leftover arguments to the csv.writer constructor, so you can use any csv.writer constructor argument to customize this exporter.

A typical output of this exporter would be:

product,price
Color TV,1200
DVD player,200
PickleItemExporter
class scrapy.exporters.PickleItemExporter(file, protocol=0, **kwargs)

Exports Items in pickle format to the given file-like object.

パラメータ:
  • file – the file-like object to use for exporting the data.
  • protocol (int) – The pickle protocol to use.

For more information, refer to the pickle module documentation.

The additional keyword arguments of this constructor are passed to the BaseItemExporter constructor.

Pickle isn’t a human readable format, so no output examples are provided.

PprintItemExporter
class scrapy.exporters.PprintItemExporter(file, **kwargs)

Exports Items in pretty print format to the specified file object.

パラメータ:file – the file-like object to use for exporting the data.

The additional keyword arguments of this constructor are passed to the BaseItemExporter constructor.

A typical output of this exporter would be:

{'name': 'Color TV', 'price': '1200'}
{'name': 'DVD player', 'price': '200'}

Longer lines (when present) are pretty-formatted.

JsonItemExporter
class scrapy.exporters.JsonItemExporter(file, **kwargs)

Exports Items in JSON format to the specified file-like object, writing all objects as a list of objects. The additional constructor arguments are passed to the BaseItemExporter constructor, and the leftover arguments to the JSONEncoder constructor, so you can use any JSONEncoder constructor argument to customize this exporter.

パラメータ:file – the file-like object to use for exporting the data.

A typical output of this exporter would be:

[{"name": "Color TV", "price": "1200"},
{"name": "DVD player", "price": "200"}]

警告

JSON is very simple and flexible serialization format, but it doesn’t scale well for large amounts of data since incremental (aka. stream-mode) parsing is not well supported (if at all) among JSON parsers (on any language), and most of them just parse the entire object in memory. If you want the power and simplicity of JSON with a more stream-friendly format, consider using JsonLinesItemExporter instead, or splitting the output in multiple chunks.

JsonLinesItemExporter
class scrapy.exporters.JsonLinesItemExporter(file, **kwargs)

Exports Items in JSON format to the specified file-like object, writing one JSON-encoded item per line. The additional constructor arguments are passed to the BaseItemExporter constructor, and the leftover arguments to the JSONEncoder constructor, so you can use any JSONEncoder constructor argument to customize this exporter.

パラメータ:file – the file-like object to use for exporting the data.

A typical output of this exporter would be:

{"name": "Color TV", "price": "1200"}
{"name": "DVD player", "price": "200"}

Unlike the one produced by JsonItemExporter, the format produced by this exporter is well suited for serializing large amounts of data.

アーキテクチャの概要
Scrapyアーキテクチャを理解する.
ダウンローダーミドルウェア
ページのリクエスト数とダウンロードのカスタマイズする.
スパイダーミドルウェア
スパイダーの入力と出力をカスタマイズする.
拡張機能
カスタム機能でScrapyを拡張する.
コア API
拡張機能やミドルウェアでそれを使ってScrapy機能を拡張する.
シグナル
利用可能なすべてのシグナルとそれらを操作する方法を見る.
アイテムエクスポーター
スクラップしたアイテムをファイルにすばやくエクスポートする (XML, CSV, etc).

残りのすべて

リリースノート

Scrapy 1.2.2 (2016-12-06)

Bug fixes
  • Fix a cryptic traceback when a pipeline fails on open_spider() (issue 2011)
  • Fix embedded IPython shell variables (fixing issue 396 that re-appeared in 1.2.0, fixed in issue 2418)
  • A couple of patches when dealing with robots.txt:
    • handle (non-standard) relative sitemap URLs (issue 2390)
    • handle non-ASCII URLs and User-Agents in Python 2 (issue 2373)
Documentation
Other changes
  • Advertize conda-forge as Scrapy’s official conda channel (issue 2387)
  • More helpful error messages when trying to use .css() or .xpath() on non-Text Responses (issue 2264)
  • startproject command now generates a sample middlewares.py file (issue 2335)
  • Add more dependencies’ version info in scrapy version verbose output (issue 2404)
  • Remove all *.pyc files from source distribution (issue 2386)

Scrapy 1.2.1 (2016-10-21)

Bug fixes
  • Include OpenSSL’s more permissive default ciphers when establishing TLS/SSL connections (issue 2314).
  • Fix “Location” HTTP header decoding on non-ASCII URL redirects (issue 2321).
Documentation
Other changes
  • Removed www. from start_urls in built-in spider templates (issue 2299).

Scrapy 1.2.0 (2016-10-03)

New Features
  • New FEED_EXPORT_ENCODING setting to customize the encoding used when writing items to a file. This can be used to turn off \uXXXX escapes in JSON output. This is also useful for those wanting something else than UTF-8 for XML or CSV output (issue 2034).
  • startproject command now supports an optional destination directory to override the default one based on the project name (issue 2005).
  • New SCHEDULER_DEBUG setting to log requests serialization failures (issue 1610).
  • JSON encoder now supports serialization of set instances (issue 2058).
  • Interpret application/json-amazonui-streaming as TextResponse (issue 1503).
  • scrapy is imported by default when using shell tools (shell, inspect_response) (issue 2248).
Bug fixes
  • DefaultRequestHeaders middleware now runs before UserAgent middleware (issue 2088). Warning: this is technically backwards incompatible, though we consider this a bug fix.
  • HTTP cache extension and plugins that use the .scrapy data directory now work outside projects (issue 1581). Warning: this is technically backwards incompatible, though we consider this a bug fix.
  • Selector does not allow passing both response and text anymore (issue 2153).
  • Fixed logging of wrong callback name with scrapy parse (issue 2169).
  • Fix for an odd gzip decompression bug (issue 1606).
  • Fix for selected callbacks when using CrawlSpider with scrapy parse (issue 2225).
  • Fix for invalid JSON and XML files when spider yields no items (issue 872).
  • Implement flush() fpr StreamLogger avoiding a warning in logs (issue 2125).
Refactoring
Tests & Requirements

Scrapy’s new requirements baseline is Debian 8 “Jessie”. It was previously Ubuntu 12.04 Precise. What this means in practice is that we run continuous integration tests with these (main) packages versions at a minimum: Twisted 14.0, pyOpenSSL 0.14, lxml 3.4.

Scrapy may very well work with older versions of these packages (the code base still has switches for older Twisted versions for example) but it is not guaranteed (because it’s not tested anymore).

Documentation

Scrapy 1.1.3 (2016-09-22)

Bug fixes
  • Class attributes for subclasses of ImagesPipeline and FilesPipeline work as they did before 1.1.1 (issue 2243, fixes issue 2198)
Documentation

Scrapy 1.1.2 (2016-08-18)

Bug fixes
  • Introduce a missing IMAGES_STORE_S3_ACL setting to override the default ACL policy in ImagesPipeline when uploading images to S3 (note that default ACL policy is “private” – instead of “public-read” – since Scrapy 1.1.0)
  • IMAGES_EXPIRES default value set back to 90 (the regression was introduced in 1.1.1)

Scrapy 1.1.1 (2016-07-13)

Bug fixes
  • Add “Host” header in CONNECT requests to HTTPS proxies (issue 2069)
  • Use response body when choosing response class (issue 2001, fixes issue 2000)
  • Do not fail on canonicalizing URLs with wrong netlocs (issue 2038, fixes issue 2010)
  • a few fixes for HttpCompressionMiddleware (and SitemapSpider):
  • Catch (and ignore with a warning) exception when verifying certificate against IP-address hosts (issue 2094, fixes issue 2092)
  • Make FilesPipeline and ImagesPipeline backward compatible again regarding the use of legacy class attributes for customization (issue 1989, fixes issue 1985)
New features
  • Enable genspider command outside project folder (issue 2052)
  • Retry HTTPS CONNECT TunnelError by default (issue 1974)
Documentation
Tests
  • Upgrade py.test requirement on Travis CI and Pin pytest-cov to 2.2.1 (issue 2095)

Scrapy 1.1.0 (2016-05-11)

This 1.1 release brings a lot of interesting features and bug fixes:

  • Scrapy 1.1 has beta Python 3 support (requires Twisted >= 15.5). See Beta Python 3 Support for more details and some limitations.
  • Hot new features:
  • These bug fixes may require your attention:
    • Don’t retry bad requests (HTTP 400) by default (issue 1289). If you need the old behavior, add 400 to RETRY_HTTP_CODES.
    • Fix shell files argument handling (issue 1710, issue 1550). If you try scrapy shell index.html it will try to load the URL http://index.html, use scrapy shell ./index.html to load a local file.
    • Robots.txt compliance is now enabled by default for newly-created projects (issue 1724). Scrapy will also wait for robots.txt to be downloaded before proceeding with the crawl (issue 1735). If you want to disable this behavior, update ROBOTSTXT_OBEY in settings.py file after creating a new project.
    • Exporters now work on unicode, instead of bytes by default (issue 1080). If you use PythonItemExporter, you may want to update your code to disable binary mode which is now deprecated.
    • Accept XML node names containing dots as valid (issue 1533).
    • When uploading files or images to S3 (with FilesPipeline or ImagesPipeline), the default ACL policy is now “private” instead of “public” Warning: backwards incompatible!. You can use FILES_STORE_S3_ACL to change it.
    • We’ve reimplemented canonicalize_url() for more correct output, especially for URLs with non-ASCII characters (issue 1947). This could change link extractors output compared to previous scrapy versions. This may also invalidate some cache entries you could still have from pre-1.1 runs. Warning: backwards incompatible!.

Keep reading for more details on other improvements and bug fixes.

Beta Python 3 Support

We have been hard at work to make Scrapy run on Python 3. As a result, now you can run spiders on Python 3.3, 3.4 and 3.5 (Twisted >= 15.5 required). Some features are still missing (and some may never be ported).

Almost all builtin extensions/middlewares are expected to work. However, we are aware of some limitations in Python 3:

  • Scrapy does not work on Windows with Python 3
  • Sending emails is not supported
  • FTP download handler is not supported
  • Telnet console is not supported
Additional New Features and Enhancements
Deprecations and Removals
  • Added to_bytes and to_unicode, deprecated str_to_unicode and unicode_to_str functions (issue 778).
  • binary_is_text is introduced, to replace use of isbinarytext (but with inverse return value) (issue 1851)
  • The optional_features set has been removed (issue 1359).
  • The --lsprof command line option has been removed (issue 1689). Warning: backward incompatible, but doesn’t break user code.
  • The following datatypes were deprecated (issue 1720):
    • scrapy.utils.datatypes.MultiValueDictKeyError
    • scrapy.utils.datatypes.MultiValueDict
    • scrapy.utils.datatypes.SiteNode
  • The previously bundled scrapy.xlib.pydispatch library was deprecated and replaced by pydispatcher.
Relocations
Bugfixes

Scrapy 1.0.6 (2016-05-04)

  • FIX: RetryMiddleware is now robust to non-standard HTTP status codes (issue 1857)
  • FIX: Filestorage HTTP cache was checking wrong modified time (issue 1875)
  • DOC: Support for Sphinx 1.4+ (issue 1893)
  • DOC: Consistency in selectors examples (issue 1869)

Scrapy 1.0.5 (2016-02-04)

Scrapy 1.0.4 (2015-12-30)

Scrapy 1.0.3 (2015-08-11)

Scrapy 1.0.2 (2015-08-06)

Scrapy 1.0.1 (2015-07-01)

Scrapy 1.0.0 (2015-06-19)

You will find a lot of new features and bugfixes in this major release. Make sure to check our updated overview to get a glance of some of the changes, along with our brushed tutorial.

Support for returning dictionaries in spiders

Declaring and returning Scrapy Items is no longer necessary to collect the scraped data from your spider, you can now return explicit dictionaries instead.

Classic version

class MyItem(scrapy.Item):
    url = scrapy.Field()

class MySpider(scrapy.Spider):
    def parse(self, response):
        return MyItem(url=response.url)

New version

class MySpider(scrapy.Spider):
    def parse(self, response):
        return {'url': response.url}
Per-spider settings (GSoC 2014)

Last Google Summer of Code project accomplished an important redesign of the mechanism used for populating settings, introducing explicit priorities to override any given setting. As an extension of that goal, we included a new level of priority for settings that act exclusively for a single spider, allowing them to redefine project settings.

Start using it by defining a custom_settings class variable in your spider:

class MySpider(scrapy.Spider):
    custom_settings = {
        "DOWNLOAD_DELAY": 5.0,
        "RETRY_ENABLED": False,
    }

Read more about settings population: 設定

Python Logging

Scrapy 1.0 has moved away from Twisted logging to support Python built in’s as default logging system. We’re maintaining backward compatibility for most of the old custom interface to call logging functions, but you’ll get warnings to switch to the Python logging API entirely.

Old version

from scrapy import log
log.msg('MESSAGE', log.INFO)

New version

import logging
logging.info('MESSAGE')

Logging with spiders remains the same, but on top of the log() method you’ll have access to a custom logger created for the spider to issue log events:

class MySpider(scrapy.Spider):
    def parse(self, response):
        self.logger.info('Response received')

Read more in the logging documentation: ロギング

Crawler API refactoring (GSoC 2014)

Another milestone for last Google Summer of Code was a refactoring of the internal API, seeking a simpler and easier usage. Check new core interface in: コア API

A common situation where you will face these changes is while running Scrapy from scripts. Here’s a quick example of how to run a Spider manually with the new API:

from scrapy.crawler import CrawlerProcess

process = CrawlerProcess({
    'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'
})
process.crawl(MySpider)
process.start()

Bear in mind this feature is still under development and its API may change until it reaches a stable status.

See more examples for scripts running Scrapy: 一般的なプラクティス

Module Relocations

There’s been a large rearrangement of modules trying to improve the general structure of Scrapy. Main changes were separating various subpackages into new projects and dissolving both scrapy.contrib and scrapy.contrib_exp into top level packages. Backward compatibility was kept among internal relocations, while importing deprecated modules expect warnings indicating their new place.

Full list of relocations

Outsourced packages

注釈

These extensions went through some minor changes, e.g. some setting names were changed. Please check the documentation in each new repository to get familiar with the new usage.

Old location New location
scrapy.commands.deploy scrapyd-client (See other alternatives here: スパイダーのデプロイ)
scrapy.contrib.djangoitem scrapy-djangoitem
scrapy.webservice scrapy-jsonrpc

scrapy.contrib_exp and scrapy.contrib dissolutions

Old location New location
scrapy.contrib_exp.downloadermiddleware.decompression scrapy.downloadermiddlewares.decompression
scrapy.contrib_exp.iterators scrapy.utils.iterators
scrapy.contrib.downloadermiddleware scrapy.downloadermiddlewares
scrapy.contrib.exporter scrapy.exporters
scrapy.contrib.linkextractors scrapy.linkextractors
scrapy.contrib.loader scrapy.loader
scrapy.contrib.loader.processor scrapy.loader.processors
scrapy.contrib.pipeline scrapy.pipelines
scrapy.contrib.spidermiddleware scrapy.spidermiddlewares
scrapy.contrib.spiders scrapy.spiders
  • scrapy.contrib.closespider
  • scrapy.contrib.corestats
  • scrapy.contrib.debug
  • scrapy.contrib.feedexport
  • scrapy.contrib.httpcache
  • scrapy.contrib.logstats
  • scrapy.contrib.memdebug
  • scrapy.contrib.memusage
  • scrapy.contrib.spiderstate
  • scrapy.contrib.statsmailer
  • scrapy.contrib.throttle
scrapy.extensions.*

Plural renames and Modules unification

Old location New location
scrapy.command scrapy.commands
scrapy.dupefilter scrapy.dupefilters
scrapy.linkextractor scrapy.linkextractors
scrapy.spider scrapy.spiders
scrapy.squeue scrapy.squeues
scrapy.statscol scrapy.statscollectors
scrapy.utils.decorator scrapy.utils.decorators

Class renames

Old location New location
scrapy.spidermanager.SpiderManager scrapy.spiderloader.SpiderLoader

Settings renames

Old location New location
SPIDER_MANAGER_CLASS SPIDER_LOADER_CLASS
Changelog

New Features and Enhancements

Deprecations and Removals

  • Deprecate htmlparser link extractor (issue 1205)
  • remove deprecated code from FeedExporter (issue 1155)
  • a leftover for.15 compatibility (issue 925)
  • drop support for CONCURRENT_REQUESTS_PER_SPIDER (issue 895)
  • Drop old engine code (issue 911)
  • Deprecate SgmlLinkExtractor (issue 777)

Relocations

Documentation

Bugfixes

  • Item multi inheritance fix (issue 353, issue 1228)
  • ItemLoader.load_item: iterate over copy of fields (issue 722)
  • Fix Unhandled error in Deferred (RobotsTxtMiddleware) (issue 1131, issue 1197)
  • Force to read DOWNLOAD_TIMEOUT as int (issue 954)
  • scrapy.utils.misc.load_object should print full traceback (issue 902)
  • Fix bug for ”.local” host name (issue 878)
  • Fix for Enabled extensions, middlewares, pipelines info not printed anymore (issue 879)
  • fix dont_merge_cookies bad behaviour when set to false on meta (issue 846)

Python 3 In Progress Support

  • disable scrapy.telnet if twisted.conch is not available (issue 1161)
  • fix Python 3 syntax errors in ajaxcrawl.py (issue 1162)
  • more python3 compatibility changes for urllib (issue 1121)
  • assertItemsEqual was renamed to assertCountEqual in Python 3. (issue 1070)
  • Import unittest.mock if available. (issue 1066)
  • updated deprecated cgi.parse_qsl to use six’s parse_qsl (issue 909)
  • Prevent Python 3 port regressions (issue 830)
  • PY3: use MutableMapping for python 3 (issue 810)
  • PY3: use six.BytesIO and six.moves.cStringIO (issue 803)
  • PY3: fix xmlrpclib and email imports (issue 801)
  • PY3: use six for robotparser and urlparse (issue 800)
  • PY3: use six.iterkeys, six.iteritems, and tempfile (issue 799)
  • PY3: fix has_key and use six.moves.configparser (issue 798)
  • PY3: use six.moves.cPickle (issue 797)
  • PY3 make it possible to run some tests in Python3 (issue 776)

Tests

  • remove unnecessary lines from py3-ignores (issue 1243)
  • Fix remaining warnings from pytest while collecting tests (issue 1206)
  • Add docs build to travis (issue 1234)
  • TST don’t collect tests from deprecated modules. (issue 1165)
  • install service_identity package in tests to prevent warnings (issue 1168)
  • Fix deprecated settings API in tests (issue 1152)
  • Add test for webclient with POST method and no body given (issue 1089)
  • py3-ignores.txt supports comments (issue 1044)
  • modernize some of the asserts (issue 835)
  • selector.__repr__ test (issue 779)

Code refactoring

  • CSVFeedSpider cleanup: use iterate_spider_output (issue 1079)
  • remove unnecessary check from scrapy.utils.spider.iter_spider_output (issue 1078)
  • Pydispatch pep8 (issue 992)
  • Removed unused ‘load=False’ parameter from walk_modules() (issue 871)
  • For consistency, use job_dir helper in SpiderState extension. (issue 805)
  • rename “sflo” local variables to less cryptic “log_observer” (issue 775)

Scrapy 0.24.6 (2015-04-20)

Scrapy 0.24.5 (2015-02-25)

Scrapy 0.24.4 (2014-08-09)

Scrapy 0.24.3 (2014-08-09)

Scrapy 0.24.2 (2014-07-08)

  • Use a mutable mapping to proxy deprecated settings.overrides and settings.defaults attribute (commit e5e8133)
  • there is not support for python3 yet (commit 3cd6146)
  • Update python compatible version set to debian packages (commit fa5d76b)
  • DOC fix formatting in release notes (commit c6a9e20)

Scrapy 0.24.1 (2014-06-27)

  • Fix deprecated CrawlerSettings and increase backwards compatibility with .defaults attribute (commit 8e3f20a)

Scrapy 0.24.0 (2014-06-26)

Enhancements
Bugfixes
  • Encode unicode URL value when creating Links in RegexLinkExtractor (issue 561)
  • Ignore None values in ItemLoader processors (issue 556)
  • Fix link text when there is an inner tag in SGMLLinkExtractor and HtmlParserLinkExtractor (issue 485, issue 574)
  • Fix wrong checks on subclassing of deprecated classes (issue 581, issue 584)
  • Handle errors caused by inspect.stack() failures (issue 582)
  • Fix a reference to unexistent engine attribute (issue 593, issue 594)
  • Fix dynamic itemclass example usage of type() (issue 603)
  • Use lucasdemarchi/codespell to fix typos (issue 628)
  • Fix default value of attrs argument in SgmlLinkExtractor to be tuple (issue 661)
  • Fix XXE flaw in sitemap reader (issue 676)
  • Fix engine to support filtered start requests (issue 707)
  • Fix offsite middleware case on urls with no hostnames (issue 745)
  • Testsuite doesn’t require PIL anymore (issue 585)

Scrapy 0.22.2 (released 2014-02-14)

Scrapy 0.22.1 (released 2014-02-08)

  • localhost666 can resolve under certain circumstances (commit 2ec2279)
  • test inspect.stack failure (commit cc3eda3)
  • Handle cases when inspect.stack() fails (commit 8cb44f9)
  • Fix wrong checks on subclassing of deprecated classes. closes #581 (commit 46d98d6)
  • Docs: 4-space indent for final spider example (commit 13846de)
  • Fix HtmlParserLinkExtractor and tests after #485 merge (commit 368a946)
  • BaseSgmlLinkExtractor: Fixed the missing space when the link has an inner tag (commit b566388)
  • BaseSgmlLinkExtractor: Added unit test of a link with an inner tag (commit c1cb418)
  • BaseSgmlLinkExtractor: Fixed unknown_endtag() so that it only set current_link=None when the end tag match the opening tag (commit 7e4d627)
  • Fix tests for Travis-CI build (commit 76c7e20)
  • replace unencodeable codepoints with html entities. fixes #562 and #285 (commit 5f87b17)
  • RegexLinkExtractor: encode URL unicode value when creating Links (commit d0ee545)
  • Updated the tutorial crawl output with latest output. (commit 8da65de)
  • Updated shell docs with the crawler reference and fixed the actual shell output. (commit 875b9ab)
  • PEP8 minor edits. (commit f89efaf)
  • Expose current crawler in the scrapy shell. (commit 5349cec)
  • Unused re import and PEP8 minor edits. (commit 387f414)
  • Ignore None’s values when using the ItemLoader. (commit 0632546)
  • DOC Fixed HTTPCACHE_STORAGE typo in the default value which is now Filesystem instead Dbm. (commit cde9a8c)
  • show ubuntu setup instructions as literal code (commit fb5c9c5)
  • Update Ubuntu installation instructions (commit 70fb105)
  • Merge pull request #550 from stray-leone/patch-1 (commit 6f70b6a)
  • modify the version of scrapy ubuntu package (commit 725900d)
  • fix 0.22.0 release date (commit af0219a)
  • fix typos in news.rst and remove (not released yet) header (commit b7f58f4)

Scrapy 0.22.0 (released 2014-01-17)

Enhancements
  • [Backwards incompatible] Switched HTTPCacheMiddleware backend to filesystem (issue 541) To restore old backend set HTTPCACHE_STORAGE to scrapy.contrib.httpcache.DbmCacheStorage
  • Proxy https:// urls using CONNECT method (issue 392, issue 397)
  • Add a middleware to crawl ajax crawleable pages as defined by google (issue 343)
  • Rename scrapy.spider.BaseSpider to scrapy.spider.Spider (issue 510, issue 519)
  • Selectors register EXSLT namespaces by default (issue 472)
  • Unify item loaders similar to selectors renaming (issue 461)
  • Make RFPDupeFilter class easily subclassable (issue 533)
  • Improve test coverage and forthcoming Python 3 support (issue 525)
  • Promote startup info on settings and middleware to INFO level (issue 520)
  • Support partials in get_func_args util (issue 506, issue:504)
  • Allow running indiviual tests via tox (issue 503)
  • Update extensions ignored by link extractors (issue 498)
  • Add middleware methods to get files/images/thumbs paths (issue 490)
  • Improve offsite middleware tests (issue 478)
  • Add a way to skip default Referer header set by RefererMiddleware (issue 475)
  • Do not send x-gzip in default Accept-Encoding header (issue 469)
  • Support defining http error handling using settings (issue 466)
  • Use modern python idioms wherever you find legacies (issue 497)
  • Improve and correct documentation (issue 527, issue 524, issue 521, issue 517, issue 512, issue 505, issue 502, issue 489, issue 465, issue 460, issue 425, issue 536)
Fixes
  • Update Selector class imports in CrawlSpider template (issue 484)
  • Fix unexistent reference to engine.slots (issue 464)
  • Do not try to call body_as_unicode() on a non-TextResponse instance (issue 462)
  • Warn when subclassing XPathItemLoader, previously it only warned on instantiation. (issue 523)
  • Warn when subclassing XPathSelector, previously it only warned on instantiation. (issue 537)
  • Multiple fixes to memory stats (issue 531, issue 530, issue 529)
  • Fix overriding url in FormRequest.from_response() (issue 507)
  • Fix tests runner under pip 1.5 (issue 513)
  • Fix logging error when spider name is unicode (issue 479)

Scrapy 0.20.2 (released 2013-12-09)

Scrapy 0.20.1 (released 2013-11-28)

  • include_package_data is required to build wheels from published sources (commit 5ba1ad5)
  • process_parallel was leaking the failures on its internal deferreds. closes #458 (commit 419a780)

Scrapy 0.20.0 (released 2013-11-08)

Enhancements
  • New Selector’s API including CSS selectors (issue 395 and issue 426),
  • Request/Response url/body attributes are now immutable (modifying them had been deprecated for a long time)
  • ITEM_PIPELINES is now defined as a dict (instead of a list)
  • Sitemap spider can fetch alternate URLs (issue 360)
  • Selector.remove_namespaces() now remove namespaces from element’s attributes. (issue 416)
  • Paved the road for Python 3.3+ (issue 435, issue 436, issue 431, issue 452)
  • New item exporter using native python types with nesting support (issue 366)
  • Tune HTTP1.1 pool size so it matches concurrency defined by settings (commit b43b5f575)
  • scrapy.mail.MailSender now can connect over TLS or upgrade using STARTTLS (issue 327)
  • New FilesPipeline with functionality factored out from ImagesPipeline (issue 370, issue 409)
  • Recommend Pillow instead of PIL for image handling (issue 317)
  • Added debian packages for Ubuntu quantal and raring (commit 86230c0)
  • Mock server (used for tests) can listen for HTTPS requests (issue 410)
  • Remove multi spider support from multiple core components (issue 422, issue 421, issue 420, issue 419, issue 423, issue 418)
  • Travis-CI now tests Scrapy changes against development versions of w3lib and queuelib python packages.
  • Add pypy 2.1 to continuous integration tests (commit ecfa7431)
  • Pylinted, pep8 and removed old-style exceptions from source (issue 430, issue 432)
  • Use importlib for parametric imports (issue 445)
  • Handle a regression introduced in Python 2.7.5 that affects XmlItemExporter (issue 372)
  • Bugfix crawling shutdown on SIGINT (issue 450)
  • Do not submit reset type inputs in FormRequest.from_response (commit b326b87)
  • Do not silence download errors when request errback raises an exception (commit 684cfc0)
Bugfixes
Other
  • Dropped Python 2.6 support (issue 448)
  • Add cssselect python package as install dependency
  • Drop libxml2 and multi selector’s backend support, lxml is required from now on.
  • Minimum Twisted version increased to 10.0.0, dropped Twisted 8.0 support.
  • Running test suite now requires mock python library (issue 390)
Thanks

Thanks to everyone who contribute to this release!

List of contributors sorted by number of commits:

69 Daniel Graña <dangra@...>
37 Pablo Hoffman <pablo@...>
13 Mikhail Korobov <kmike84@...>
 9 Alex Cepoi <alex.cepoi@...>
 9 alexanderlukanin13 <alexander.lukanin.13@...>
 8 Rolando Espinoza La fuente <darkrho@...>
 8 Lukasz Biedrycki <lukasz.biedrycki@...>
 6 Nicolas Ramirez <nramirez.uy@...>
 3 Paul Tremberth <paul.tremberth@...>
 2 Martin Olveyra <molveyra@...>
 2 Stefan <misc@...>
 2 Rolando Espinoza <darkrho@...>
 2 Loren Davie <loren@...>
 2 irgmedeiros <irgmedeiros@...>
 1 Stefan Koch <taikano@...>
 1 Stefan <cct@...>
 1 scraperdragon <dragon@...>
 1 Kumara Tharmalingam <ktharmal@...>
 1 Francesco Piccinno <stack.box@...>
 1 Marcos Campal <duendex@...>
 1 Dragon Dave <dragon@...>
 1 Capi Etheriel <barraponto@...>
 1 cacovsky <amarquesferraz@...>
 1 Berend Iwema <berend@...>

Scrapy 0.18.4 (released 2013-10-10)

  • IPython refuses to update the namespace. fix #396 (commit 3d32c4f)
  • Fix AlreadyCalledError replacing a request in shell command. closes #407 (commit b1d8919)
  • Fix start_requests laziness and early hangs (commit 89faf52)

Scrapy 0.18.3 (released 2013-10-03)

Scrapy 0.18.2 (released 2013-09-03)

  • Backport scrapy check command fixes and backward compatible multi crawler process(issue 339)

Scrapy 0.18.1 (released 2013-08-27)

  • remove extra import added by cherry picked changes (commit d20304e)
  • fix crawling tests under twisted pre 11.0.0 (commit 1994f38)
  • py26 can not format zero length fields {} (commit abf756f)
  • test PotentiaDataLoss errors on unbound responses (commit b15470d)
  • Treat responses without content-length or Transfer-Encoding as good responses (commit c4bf324)
  • do no include ResponseFailed if http11 handler is not enabled (commit 6cbe684)
  • New HTTP client wraps connection losts in ResponseFailed exception. fix #373 (commit 1a20bba)
  • limit travis-ci build matrix (commit 3b01bb8)
  • Merge pull request #375 from peterarenot/patch-1 (commit fa766d7)
  • Fixed so it refers to the correct folder (commit 3283809)
  • added quantal & raring to support ubuntu releases (commit 1411923)
  • fix retry middleware which didn’t retry certain connection errors after the upgrade to http1 client, closes GH-373 (commit bb35ed0)
  • fix XmlItemExporter in Python 2.7.4 and 2.7.5 (commit de3e451)
  • minor updates to 0.18 release notes (commit c45e5f1)
  • fix contributters list format (commit 0b60031)

Scrapy 0.18.0 (released 2013-08-09)

  • Lot of improvements to testsuite run using Tox, including a way to test on pypi
  • Handle GET parameters for AJAX crawleable urls (commit 3fe2a32)
  • Use lxml recover option to parse sitemaps (issue 347)
  • Bugfix cookie merging by hostname and not by netloc (issue 352)
  • Support disabling HttpCompressionMiddleware using a flag setting (issue 359)
  • Support xml namespaces using iternodes parser in XMLFeedSpider (issue 12)
  • Support dont_cache request meta flag (issue 19)
  • Bugfix scrapy.utils.gz.gunzip broken by changes in python 2.7.4 (commit 4dc76e)
  • Bugfix url encoding on SgmlLinkExtractor (issue 24)
  • Bugfix TakeFirst processor shouldn’t discard zero (0) value (issue 59)
  • Support nested items in xml exporter (issue 66)
  • Improve cookies handling performance (issue 77)
  • Log dupe filtered requests once (issue 105)
  • Split redirection middleware into status and meta based middlewares (issue 78)
  • Use HTTP1.1 as default downloader handler (issue 109 and issue 318)
  • Support xpath form selection on FormRequest.from_response (issue 185)
  • Bugfix unicode decoding error on SgmlLinkExtractor (issue 199)
  • Bugfix signal dispatching on pypi interpreter (issue 205)
  • Improve request delay and concurrency handling (issue 206)
  • Add RFC2616 cache policy to HttpCacheMiddleware (issue 212)
  • Allow customization of messages logged by engine (issue 214)
  • Multiples improvements to DjangoItem (issue 217, issue 218, issue 221)
  • Extend Scrapy commands using setuptools entry points (issue 260)
  • Allow spider allowed_domains value to be set/tuple (issue 261)
  • Support settings.getdict (issue 269)
  • Simplify internal scrapy.core.scraper slot handling (issue 271)
  • Added Item.copy (issue 290)
  • Collect idle downloader slots (issue 297)
  • Add ftp:// scheme downloader handler (issue 329)
  • Added downloader benchmark webserver and spider tools ベンチマーク
  • Moved persistent (on disk) queues to a separate project (queuelib) which scrapy now depends on
  • Add scrapy commands using external libraries (issue 260)
  • Added --pdb option to scrapy command line tool
  • Added XPathSelector.remove_namespaces() which allows to remove all namespaces from XML documents for convenience (to work with namespace-less XPaths). Documented in セレクタ.
  • Several improvements to spider contracts
  • New default middleware named MetaRefreshMiddldeware that handles meta-refresh html tag redirections,
  • MetaRefreshMiddldeware and RedirectMiddleware have different priorities to address #62
  • added from_crawler method to spiders
  • added system tests with mock server
  • more improvements to Mac OS compatibility (thanks Alex Cepoi)
  • several more cleanups to singletons and multi-spider support (thanks Nicolas Ramirez)
  • support custom download slots
  • added –spider option to “shell” command.
  • log overridden settings when scrapy starts

Thanks to everyone who contribute to this release. Here is a list of contributors sorted by number of commits:

130 Pablo Hoffman <pablo@...>
 97 Daniel Graña <dangra@...>
 20 Nicolás Ramírez <nramirez.uy@...>
 13 Mikhail Korobov <kmike84@...>
 12 Pedro Faustino <pedrobandim@...>
 11 Steven Almeroth <sroth77@...>
  5 Rolando Espinoza La fuente <darkrho@...>
  4 Michal Danilak <mimino.coder@...>
  4 Alex Cepoi <alex.cepoi@...>
  4 Alexandr N Zamaraev (aka tonal) <tonal@...>
  3 paul <paul.tremberth@...>
  3 Martin Olveyra <molveyra@...>
  3 Jordi Llonch <llonchj@...>
  3 arijitchakraborty <myself.arijit@...>
  2 Shane Evans <shane.evans@...>
  2 joehillen <joehillen@...>
  2 Hart <HartSimha@...>
  2 Dan <ellisd23@...>
  1 Zuhao Wan <wanzuhao@...>
  1 whodatninja <blake@...>
  1 vkrest <v.krestiannykov@...>
  1 tpeng <pengtaoo@...>
  1 Tom Mortimer-Jones <tom@...>
  1 Rocio Aramberri <roschegel@...>
  1 Pedro <pedro@...>
  1 notsobad <wangxiaohugg@...>
  1 Natan L <kuyanatan.nlao@...>
  1 Mark Grey <mark.grey@...>
  1 Luan <luanpab@...>
  1 Libor Nenadál <libor.nenadal@...>
  1 Juan M Uys <opyate@...>
  1 Jonas Brunsgaard <jonas.brunsgaard@...>
  1 Ilya Baryshev <baryshev@...>
  1 Hasnain Lakhani <m.hasnain.lakhani@...>
  1 Emanuel Schorsch <emschorsch@...>
  1 Chris Tilden <chris.tilden@...>
  1 Capi Etheriel <barraponto@...>
  1 cacovsky <amarquesferraz@...>
  1 Berend Iwema <berend@...>

Scrapy 0.16.5 (released 2013-05-30)

  • obey request method when scrapy deploy is redirected to a new endpoint (commit 8c4fcee)
  • fix inaccurate downloader middleware documentation. refs #280 (commit 40667cb)
  • doc: remove links to diveintopython.org, which is no longer available. closes #246 (commit bd58bfa)
  • Find form nodes in invalid html5 documents (commit e3d6945)
  • Fix typo labeling attrs type bool instead of list (commit a274276)

Scrapy 0.16.4 (released 2013-01-23)

  • fixes spelling errors in documentation (commit 6d2b3aa)
  • add doc about disabling an extension. refs #132 (commit c90de33)
  • Fixed error message formatting. log.err() doesn’t support cool formatting and when error occurred, the message was: “ERROR: Error processing %(item)s” (commit c16150c)
  • lint and improve images pipeline error logging (commit 56b45fc)
  • fixed doc typos (commit 243be84)
  • add documentation topics: Broad Crawls & Common Practies (commit 1fbb715)
  • fix bug in scrapy parse command when spider is not specified explicitly. closes #209 (commit c72e682)
  • Update docs/topics/commands.rst (commit 28eac7a)

Scrapy 0.16.3 (released 2012-12-07)

Scrapy 0.16.2 (released 2012-11-09)

Scrapy 0.16.1 (released 2012-10-26)

  • fixed LogStats extension, which got broken after a wrong merge before the 0.16 release (commit 8c780fd)
  • better backwards compatibility for scrapy.conf.settings (commit 3403089)
  • extended documentation on how to access crawler stats from extensions (commit c4da0b5)
  • removed .hgtags (no longer needed now that scrapy uses git) (commit d52c188)
  • fix dashes under rst headers (commit fa4f7f9)
  • set release date for 0.16.0 in news (commit e292246)

Scrapy 0.16.0 (released 2012-10-18)

Scrapy changes:

  • added スパイダーコントラクト, a mechanism for testing spiders in a formal/reproducible way
  • added options -o and -t to the runspider command
  • documented AutoThrottle 拡張機能 and added to extensions installed by default. You still need to enable it with AUTOTHROTTLE_ENABLED
  • major Stats Collection refactoring: removed separation of global/per-spider stats, removed stats-related signals (stats_spider_opened, etc). Stats are much simpler now, backwards compatibility is kept on the Stats Collector API and signals.
  • added process_start_requests() method to spider middlewares
  • dropped Signals singleton. Signals should now be accesed through the Crawler.signals attribute. See the signals documentation for more info.
  • dropped Signals singleton. Signals should now be accesed through the Crawler.signals attribute. See the signals documentation for more info.
  • dropped Stats Collector singleton. Stats can now be accessed through the Crawler.stats attribute. See the stats collection documentation for more info.
  • documented コア API
  • lxml is now the default selectors backend instead of libxml2
  • ported FormRequest.from_response() to use lxml instead of ClientForm
  • removed modules: scrapy.xlib.BeautifulSoup and scrapy.xlib.ClientForm
  • SitemapSpider: added support for sitemap urls ending in .xml and .xml.gz, even if they advertise a wrong content type (commit 10ed28b)
  • StackTraceDump extension: also dump trackref live references (commit fe2ce93)
  • nested items now fully supported in JSON and JSONLines exporters
  • added cookiejar Request meta key to support multiple cookie sessions per spider
  • decoupled encoding detection code to w3lib.encoding, and ported Scrapy code to use that module
  • dropped support for Python 2.5. See https://blog.scrapinghub.com/2012/02/27/scrapy-0-15-dropping-support-for-python-2-5/
  • dropped support for Twisted 2.5
  • added REFERER_ENABLED setting, to control referer middleware
  • changed default user agent to: Scrapy/VERSION (+http://scrapy.org)
  • removed (undocumented) HTMLImageLinkExtractor class from scrapy.contrib.linkextractors.image
  • removed per-spider settings (to be replaced by instantiating multiple crawler objects)
  • USER_AGENT spider attribute will no longer work, use user_agent attribute instead
  • DOWNLOAD_TIMEOUT spider attribute will no longer work, use download_timeout attribute instead
  • removed ENCODING_ALIASES setting, as encoding auto-detection has been moved to the w3lib library
  • promoted DjangoItem to main contrib
  • LogFormatter method now return dicts(instead of strings) to support lazy formatting (issue 164, commit dcef7b0)
  • downloader handlers (DOWNLOAD_HANDLERS setting) now receive settings as the first argument of the constructor
  • replaced memory usage acounting with (more portable) resource module, removed scrapy.utils.memory module
  • removed signal: scrapy.mail.mail_sent
  • removed TRACK_REFS setting, now trackrefs is always enabled
  • DBM is now the default storage backend for HTTP cache middleware
  • number of log messages (per level) are now tracked through Scrapy stats (stat name: log_count/LEVEL)
  • number received responses are now tracked through Scrapy stats (stat name: response_received_count)
  • removed scrapy.log.started attribute

Scrapy 0.14.4

Scrapy 0.14.3

  • forgot to include pydispatch license. #118 (commit fd85f9c)
  • include egg files used by testsuite in source distribution. #118 (commit c897793)
  • update docstring in project template to avoid confusion with genspider command, which may be considered as an advanced feature. refs #107 (commit 2548dcc)
  • added note to docs/topics/firebug.rst about google directory being shut down (commit 668e352)
  • dont discard slot when empty, just save in another dict in order to recycle if needed again. (commit 8e9f607)
  • do not fail handling unicode xpaths in libxml2 backed selectors (commit b830e95)
  • fixed minor mistake in Request objects documentation (commit bf3c9ee)
  • fixed minor defect in link extractors documentation (commit ba14f38)
  • removed some obsolete remaining code related to sqlite support in scrapy (commit 0665175)

Scrapy 0.14.2

  • move buffer pointing to start of file before computing checksum. refs #92 (commit 6a5bef2)
  • Compute image checksum before persisting images. closes #92 (commit 9817df1)
  • remove leaking references in cached failures (commit 673a120)
  • fixed bug in MemoryUsage extension: get_engine_status() takes exactly 1 argument (0 given) (commit 11133e9)
  • fixed struct.error on http compression middleware. closes #87 (commit 1423140)
  • ajax crawling wasn’t expanding for unicode urls (commit 0de3fb4)
  • Catch start_requests iterator errors. refs #83 (commit 454a21d)
  • Speed-up libxml2 XPathSelector (commit 2fbd662)
  • updated versioning doc according to recent changes (commit 0a070f5)
  • scrapyd: fixed documentation link (commit 2b4e4c3)
  • extras/makedeb.py: no longer obtaining version from git (commit caffe0e)

Scrapy 0.14.1

  • extras/makedeb.py: no longer obtaining version from git (commit caffe0e)
  • bumped version to 0.14.1 (commit 6cb9e1c)
  • fixed reference to tutorial directory (commit 4b86bd6)
  • doc: removed duplicated callback argument from Request.replace() (commit 1aeccdd)
  • fixed formatting of scrapyd doc (commit 8bf19e6)
  • Dump stacks for all running threads and fix engine status dumped by StackTraceDump extension (commit 14a8e6e)
  • added comment about why we disable ssl on boto images upload (commit 5223575)
  • SSL handshaking hangs when doing too many parallel connections to S3 (commit 63d583d)
  • change tutorial to follow changes on dmoz site (commit bcb3198)
  • Avoid _disconnectedDeferred AttributeError exception in Twisted>=11.1.0 (commit 98f3f87)
  • allow spider to set autothrottle max concurrency (commit 175a4b5)

Scrapy 0.14

New features and settings
  • Support for AJAX crawleable urls
  • New persistent scheduler that stores requests on disk, allowing to suspend and resume crawls (r2737)
  • added -o option to scrapy crawl, a shortcut for dumping scraped items into a file (or standard output using -)
  • Added support for passing custom settings to Scrapyd schedule.json api (r2779, r2783)
  • New ChunkedTransferMiddleware (enabled by default) to support chunked transfer encoding (r2769)
  • Add boto 2.0 support for S3 downloader handler (r2763)
  • Added marshal to formats supported by feed exports (r2744)
  • In request errbacks, offending requests are now received in failure.request attribute (r2738)
  • Big downloader refactoring to support per domain/ip concurrency limits (r2732)
  • Added builtin caching DNS resolver (r2728)
  • Moved Amazon AWS-related components/extensions (SQS spider queue, SimpleDB stats collector) to a separate project: [scaws](https://github.com/scrapinghub/scaws) (r2706, r2714)
  • Moved spider queues to scrapyd: scrapy.spiderqueue -> scrapyd.spiderqueue (r2708)
  • Moved sqlite utils to scrapyd: scrapy.utils.sqlite -> scrapyd.sqlite (r2781)
  • Real support for returning iterators on start_requests() method. The iterator is now consumed during the crawl when the spider is getting idle (r2704)
  • Added REDIRECT_ENABLED setting to quickly enable/disable the redirect middleware (r2697)
  • Added RETRY_ENABLED setting to quickly enable/disable the retry middleware (r2694)
  • Added CloseSpider exception to manually close spiders (r2691)
  • Improved encoding detection by adding support for HTML5 meta charset declaration (r2690)
  • Refactored close spider behavior to wait for all downloads to finish and be processed by spiders, before closing the spider (r2688)
  • Added SitemapSpider (see documentation in Spiders page) (r2658)
  • Added LogStats extension for periodically logging basic stats (like crawled pages and scraped items) (r2657)
  • Make handling of gzipped responses more robust (#319, r2643). Now Scrapy will try and decompress as much as possible from a gzipped response, instead of failing with an IOError.
  • Simplified !MemoryDebugger extension to use stats for dumping memory debugging info (r2639)
  • Added new command to edit spiders: scrapy edit (r2636) and -e flag to genspider command that uses it (r2653)
  • Changed default representation of items to pretty-printed dicts. (r2631). This improves default logging by making log more readable in the default case, for both Scraped and Dropped lines.
  • Added spider_error signal (r2628)
  • Added COOKIES_ENABLED setting (r2625)
  • Stats are now dumped to Scrapy log (default value of STATS_DUMP setting has been changed to True). This is to make Scrapy users more aware of Scrapy stats and the data that is collected there.
  • Added support for dynamically adjusting download delay and maximum concurrent requests (r2599)
  • Added new DBM HTTP cache storage backend (r2576)
  • Added listjobs.json API to Scrapyd (r2571)
  • CsvItemExporter: added join_multivalued parameter (r2578)
  • Added namespace support to xmliter_lxml (r2552)
  • Improved cookies middleware by making COOKIES_DEBUG nicer and documenting it (r2579)
  • Several improvements to Scrapyd and Link extractors
Code rearranged and removed
  • Merged item passed and item scraped concepts, as they have often proved confusing in the past. This means: (r2630)
    • original item_scraped signal was removed
    • original item_passed signal was renamed to item_scraped
    • old log lines Scraped Item... were removed
    • old log lines Passed Item... were renamed to Scraped Item... lines and downgraded to DEBUG level
  • Reduced Scrapy codebase by striping part of Scrapy code into two new libraries:
    • w3lib (several functions from scrapy.utils.{http,markup,multipart,response,url}, done in r2584)
    • scrapely (was scrapy.contrib.ibl, done in r2586)
  • Removed unused function: scrapy.utils.request.request_info() (r2577)
  • Removed googledir project from examples/googledir. There’s now a new example project called dirbot available on github: https://github.com/scrapy/dirbot
  • Removed support for default field values in Scrapy items (r2616)
  • Removed experimental crawlspider v2 (r2632)
  • Removed scheduler middleware to simplify architecture. Duplicates filter is now done in the scheduler itself, using the same dupe fltering class as before (DUPEFILTER_CLASS setting) (r2640)
  • Removed support for passing urls to scrapy crawl command (use scrapy parse instead) (r2704)
  • Removed deprecated Execution Queue (r2704)
  • Removed (undocumented) spider context extension (from scrapy.contrib.spidercontext) (r2780)
  • removed CONCURRENT_SPIDERS setting (use scrapyd maxproc instead) (r2789)
  • Renamed attributes of core components: downloader.sites -> downloader.slots, scraper.sites -> scraper.slots (r2717, r2718)
  • Renamed setting CLOSESPIDER_ITEMPASSED to CLOSESPIDER_ITEMCOUNT (r2655). Backwards compatibility kept.

Scrapy 0.12

The numbers like #NNN reference tickets in the old issue tracker (Trac) which is no longer available.

New features and improvements
  • Passed item is now sent in the item argument of the item_passed (#273)
  • Added verbose option to scrapy version command, useful for bug reports (#298)
  • HTTP cache now stored by default in the project data dir (#279)
  • Added project data storage directory (#276, #277)
  • Documented file structure of Scrapy projects (see command-line tool doc)
  • New lxml backend for XPath selectors (#147)
  • Per-spider settings (#245)
  • Support exit codes to signal errors in Scrapy commands (#248)
  • Added -c argument to scrapy shell command
  • Made libxml2 optional (#260)
  • New deploy command (#261)
  • Added CLOSESPIDER_PAGECOUNT setting (#253)
  • Added CLOSESPIDER_ERRORCOUNT setting (#254)
Scrapyd changes
  • Scrapyd now uses one process per spider
  • It stores one log file per spider run, and rotate them keeping the lastest 5 logs per spider (by default)
  • A minimal web ui was added, available at http://localhost:6800 by default
  • There is now a scrapy server command to start a Scrapyd server of the current project
Changes to settings
  • added HTTPCACHE_ENABLED setting (False by default) to enable HTTP cache middleware
  • changed HTTPCACHE_EXPIRATION_SECS semantics: now zero means “never expire”.
Deprecated/obsoleted functionality
  • Deprecated runserver command in favor of server command which starts a Scrapyd server. See also: Scrapyd changes
  • Deprecated queue command in favor of using Scrapyd schedule.json API. See also: Scrapyd changes
  • Removed the !LxmlItemLoader (experimental contrib which never graduated to main contrib)

Scrapy 0.10

The numbers like #NNN reference tickets in the old issue tracker (Trac) which is no longer available.

New features and improvements
  • New Scrapy service called scrapyd for deploying Scrapy crawlers in production (#218) (documentation available)
  • Simplified Images pipeline usage which doesn’t require subclassing your own images pipeline now (#217)
  • Scrapy shell now shows the Scrapy log by default (#206)
  • Refactored execution queue in a common base code and pluggable backends called “spider queues” (#220)
  • New persistent spider queue (based on SQLite) (#198), available by default, which allows to start Scrapy in server mode and then schedule spiders to run.
  • Added documentation for Scrapy command-line tool and all its available sub-commands. (documentation available)
  • Feed exporters with pluggable backends (#197) (documentation available)
  • Deferred signals (#193)
  • Added two new methods to item pipeline open_spider(), close_spider() with deferred support (#195)
  • Support for overriding default request headers per spider (#181)
  • Replaced default Spider Manager with one with similar functionality but not depending on Twisted Plugins (#186)
  • Splitted Debian package into two packages - the library and the service (#187)
  • Scrapy log refactoring (#188)
  • New extension for keeping persistent spider contexts among different runs (#203)
  • Added dont_redirect request.meta key for avoiding redirects (#233)
  • Added dont_retry request.meta key for avoiding retries (#234)
Command-line tool changes
  • New scrapy command which replaces the old scrapy-ctl.py (#199) - there is only one global scrapy command now, instead of one scrapy-ctl.py per project - Added scrapy.bat script for running more conveniently from Windows
  • Added bash completion to command-line tool (#210)
  • Renamed command start to runserver (#209)
API changes
  • url and body attributes of Request objects are now read-only (#230)
  • Request.copy() and Request.replace() now also copies their callback and errback attributes (#231)
  • Removed UrlFilterMiddleware from scrapy.contrib (already disabled by default)
  • Offsite middelware doesn’t filter out any request coming from a spider that doesn’t have a allowed_domains attribute (#225)
  • Removed Spider Manager load() method. Now spiders are loaded in the constructor itself.
  • Changes to Scrapy Manager (now called “Crawler”):
    • scrapy.core.manager.ScrapyManager class renamed to scrapy.crawler.Crawler
    • scrapy.core.manager.scrapymanager singleton moved to scrapy.project.crawler
  • Moved module: scrapy.contrib.spidermanager to scrapy.spidermanager
  • Spider Manager singleton moved from scrapy.spider.spiders to the spiders` attribute of ``scrapy.project.crawler singleton.
  • moved Stats Collector classes: (#204)
    • scrapy.stats.collector.StatsCollector to scrapy.statscol.StatsCollector
    • scrapy.stats.collector.SimpledbStatsCollector to scrapy.contrib.statscol.SimpledbStatsCollector
  • default per-command settings are now specified in the default_settings attribute of command object class (#201)
  • changed arguments of Item pipeline process_item() method from (spider, item) to (item, spider)
    • backwards compatibility kept (with deprecation warning)
  • moved scrapy.core.signals module to scrapy.signals
    • backwards compatibility kept (with deprecation warning)
  • moved scrapy.core.exceptions module to scrapy.exceptions
    • backwards compatibility kept (with deprecation warning)
  • added handles_request() class method to BaseSpider
  • dropped scrapy.log.exc() function (use scrapy.log.err() instead)
  • dropped component argument of scrapy.log.msg() function
  • dropped scrapy.log.log_level attribute
  • Added from_settings() class methods to Spider Manager, and Item Pipeline Manager
Changes to settings
  • Added HTTPCACHE_IGNORE_SCHEMES setting to ignore certain schemes on !HttpCacheMiddleware (#225)
  • Added SPIDER_QUEUE_CLASS setting which defines the spider queue to use (#220)
  • Added KEEP_ALIVE setting (#220)
  • Removed SERVICE_QUEUE setting (#220)
  • Removed COMMANDS_SETTINGS_MODULE setting (#201)
  • Renamed REQUEST_HANDLERS to DOWNLOAD_HANDLERS and make download handlers classes (instead of functions)

Scrapy 0.9

The numbers like #NNN reference tickets in the old issue tracker (Trac) which is no longer available.

New features and improvements
  • Added SMTP-AUTH support to scrapy.mail
  • New settings added: MAIL_USER, MAIL_PASS (r2065 | #149)
  • Added new scrapy-ctl view command - To view URL in the browser, as seen by Scrapy (r2039)
  • Added web service for controlling Scrapy process (this also deprecates the web console. (r2053 | #167)
  • Support for running Scrapy as a service, for production systems (r1988, r2054, r2055, r2056, r2057 | #168)
  • Added wrapper induction library (documentation only available in source code for now). (r2011)
  • Simplified and improved response encoding support (r1961, r1969)
  • Added LOG_ENCODING setting (r1956, documentation available)
  • Added RANDOMIZE_DOWNLOAD_DELAY setting (enabled by default) (r1923, doc available)
  • MailSender is no longer IO-blocking (r1955 | #146)
  • Linkextractors and new Crawlspider now handle relative base tag urls (r1960 | #148)
  • Several improvements to Item Loaders and processors (r2022, r2023, r2024, r2025, r2026, r2027, r2028, r2029, r2030)
  • Added support for adding variables to telnet console (r2047 | #165)
  • Support for requests without callbacks (r2050 | #166)
API changes
  • Change Spider.domain_name to Spider.name (SEP-012, r1975)
  • Response.encoding is now the detected encoding (r1961)
  • HttpErrorMiddleware now returns None or raises an exception (r2006 | #157)
  • scrapy.command modules relocation (r2035, r2036, r2037)
  • Added ExecutionQueue for feeding spiders to scrape (r2034)
  • Removed ExecutionEngine singleton (r2039)
  • Ported S3ImagesStore (images pipeline) to use boto and threads (r2033)
  • Moved module: scrapy.management.telnet to scrapy.telnet (r2047)
Changes to default settings
  • Changed default SCHEDULER_ORDER to DFO (r1939)

Scrapy 0.8

The numbers like #NNN reference tickets in the old issue tracker (Trac) which is no longer available.

New features
  • Added DEFAULT_RESPONSE_ENCODING setting (r1809)
  • Added dont_click argument to FormRequest.from_response() method (r1813, r1816)
  • Added clickdata argument to FormRequest.from_response() method (r1802, r1803)
  • Added support for HTTP proxies (HttpProxyMiddleware) (r1781, r1785)
  • Offsite spider middleware now logs messages when filtering out requests (r1841)
Backwards-incompatible changes
  • Changed scrapy.utils.response.get_meta_refresh() signature (r1804)
  • Removed deprecated scrapy.item.ScrapedItem class - use scrapy.item.Item instead (r1838)
  • Removed deprecated scrapy.xpath module - use scrapy.selector instead. (r1836)
  • Removed deprecated core.signals.domain_open signal - use core.signals.domain_opened instead (r1822)
  • log.msg() now receives a spider argument (r1822)
    • Old domain argument has been deprecated and will be removed in 0.9. For spiders, you should always use the spider argument and pass spider references. If you really want to pass a string, use the component argument instead.
  • Changed core signals domain_opened, domain_closed, domain_idle
  • Changed Item pipeline to use spiders instead of domains
    • The domain argument of process_item() item pipeline method was changed to spider, the new signature is: process_item(spider, item) (r1827 | #105)
    • To quickly port your code (to work with Scrapy 0.8) just use spider.domain_name where you previously used domain.
  • Changed Stats API to use spiders instead of domains (r1849 | #113)
    • StatsCollector was changed to receive spider references (instead of domains) in its methods (set_value, inc_value, etc).
    • added StatsCollector.iter_spider_stats() method
    • removed StatsCollector.list_domains() method
    • Also, Stats signals were renamed and now pass around spider references (instead of domains). Here’s a summary of the changes:
    • To quickly port your code (to work with Scrapy 0.8) just use spider.domain_name where you previously used domain. spider_stats contains exactly the same data as domain_stats.
  • CloseDomain extension moved to scrapy.contrib.closespider.CloseSpider (r1833)
    • Its settings were also renamed:
      • CLOSEDOMAIN_TIMEOUT to CLOSESPIDER_TIMEOUT
      • CLOSEDOMAIN_ITEMCOUNT to CLOSESPIDER_ITEMCOUNT
  • Removed deprecated SCRAPYSETTINGS_MODULE environment variable - use SCRAPY_SETTINGS_MODULE instead (r1840)
  • Renamed setting: REQUESTS_PER_DOMAIN to CONCURRENT_REQUESTS_PER_SPIDER (r1830, r1844)
  • Renamed setting: CONCURRENT_DOMAINS to CONCURRENT_SPIDERS (r1830)
  • Refactored HTTP Cache middleware
  • HTTP Cache middleware has been heavilty refactored, retaining the same functionality except for the domain sectorization which was removed. (r1843 )
  • Renamed exception: DontCloseDomain to DontCloseSpider (r1859 | #120)
  • Renamed extension: DelayedCloseDomain to SpiderCloseDelay (r1861 | #121)
  • Removed obsolete scrapy.utils.markup.remove_escape_chars function - use scrapy.utils.markup.replace_escape_chars instead (r1865)

Scrapy 0.7

First release of Scrapy.

Scrapy に貢献する

重要

この文書の最新版を確認してください. http://doc.scrapy.org/en/master/contributing.html

Scrapy に貢献する方法はたくさんあります. ここに幾つか示します:

  • Scrapyについてのブログ. あなたがScrapyをどのように使っているかを世界に伝えてください. これは, より多くの新しい仲間と, Scrapyプロジェクトの可視性を高めるのに役立ちます.
  • issue tracker にバグと、機能の要望を報告する. 下記の `Reporting bugs`_ に記載されているガイドラインに従ってください.
  • 新しい機能やバグ修正のためのパッチを提出する. パッチを作成して提出する方法の詳細は `Writing patches`_`Submitting patches`_ を読んで下さい.
  • あなたのアイデアを共有するために scrapy-users メーリングリストに参加する. 私たちは常に提案を受け入れています.

バグの報告

注釈

Please report security issues only to scrapy-security@googlegroups.com. This is a private list only open to trusted Scrapy developers, and its archives are not public.

Well-written bug reports are very helpful, so keep in mind the following guidelines when reporting a new bug.

  • check the FAQ first to see if your issue is addressed in a well-known question
  • check the open issues to see if it has already been reported. If it has, don’t dismiss the report but check the ticket history and comments, you may find additional useful information to contribute.
  • search the scrapy-users list to see if it has been discussed there, or if you’re not sure if what you’re seeing is a bug. You can also ask in the #scrapy IRC channel.
  • write complete, reproducible, specific bug reports. The smaller the test case, the better. Remember that other developers won’t have your project to reproduce the bug, so please include all relevant files required to reproduce it. See for example StackOverflow’s guide on creating a Minimal, Complete, and Verifiable example exhibiting the issue.
  • include the output of scrapy version -v so developers working on your bug know exactly which version and platform it occurred on, which is often very helpful for reproducing it, or knowing if it was already fixed.

パッチを書く

The better written a patch is, the higher chance that it’ll get accepted and the sooner that will be merged.

Well-written patches should:

  • contain the minimum amount of code required for the specific change. Small patches are easier to review and merge. So, if you’re doing more than one change (or bug fix), please consider submitting one patch per change. Do not collapse multiple changes into a single patch. For big changes consider using a patch queue.
  • pass all unit-tests. See `Running tests`_ below.
  • include one (or more) test cases that check the bug fixed or the new functionality added. See `Writing tests`_ below.
  • if you’re adding or changing a public (documented) API, please include the documentation changes in the same patch. See `Documentation policies`_ below.

パッチを提出する

The best way to submit a patch is to issue a pull request on GitHub, optionally creating a new issue first.

Remember to explain what was fixed or the new functionality (what it is, why it’s needed, etc). The more info you include, the easier will be for core developers to understand and accept your patch.

You can also discuss the new functionality (or bug fix) before creating the patch, but it’s always good to have a patch ready to illustrate your arguments and show that you have put some additional thought into the subject. A good starting point is to send a pull request on GitHub. It can be simple enough to illustrate your idea, and leave documentation/tests for later, after the idea has been validated and proven useful. Alternatively, you can send an email to scrapy-users to discuss your idea first. When writing GitHub pull requests, try to keep titles short but descriptive. E.g. For bug #411: “Scrapy hangs if an exception raises in start_requests” prefer “Fix hanging when exception occurs in start_requests (#411)” instead of “Fix for #411”. Complete titles make it easy to skim through the issue tracker.

Finally, try to keep aesthetic changes (PEP 8 compliance, unused imports removal, etc) in separate commits than functional changes. This will make pull requests easier to review and more likely to get merged.

コーディングスタイル

Please follow these coding conventions when writing code for inclusion in Scrapy:

  • Unless otherwise specified, follow PEP 8.
  • It’s OK to use lines longer than 80 chars if it improves the code readability.
  • Don’t put your name in the code you contribute. Our policy is to keep the contributor’s name in the AUTHORS file distributed with Scrapy.

Scrapy 寄稿者

Scrapy contrib shares a similar rationale as Django contrib, which is explained in this post. If you are working on a new functionality, please follow that rationale to decide whether it should be a Scrapy contrib. If unsure, you can ask in scrapy-users.

ドキュメントポリシー

  • Don’t use docstrings for documenting classes, or methods which are already documented in the official (sphinx) documentation. For example, the ItemLoader.add_value() method should be documented in the sphinx documentation, not its docstring.
  • Do use docstrings for documenting functions not present in the official (sphinx) documentation, such as functions from scrapy.utils package and its sub-modules.

テスト

Tests are implemented using the Twisted unit-testing framework, running tests requires tox.

テストの実行

Make sure you have a recent enough tox installation:

tox --version

If your version is older than 1.7.0, please update it first:

pip install -U tox

To run all tests go to the root directory of Scrapy source code and run:

tox

To run a specific test (say tests/test_loader.py) use:

tox -- tests/test_loader.py

To see coverage report install coverage (pip install coverage) and run:

coverage report

see output of coverage --help for more options like html or xml report.

テストを書く

All functionality (including new features and bug fixes) must include a test case to check that it works as expected, so please include tests for your patches if you want them to get accepted sooner.

Scrapy uses unit-tests, which are located in the tests/ directory. Their module name typically resembles the full path of the module they’re testing. For example, the item loaders code is in:

scrapy.loader

And their unit-tests are in:

tests/test_loader.py

バージョニングとAPIの安定性

バージョニング

Scrapyバージョンには3つの数字があります: A . B . C

  • A はメジャーバージョンです. これはめったに変わらず, 非常に大きな変更を意味します.
  • B はリリース番号です. これには, 後方互換性を損なう可能性のある機能やものを含む多くの変更が含まれますが, これらのケースを最小限に抑えるよう努めています.
  • C はバグ修正リリース番号です.

下位互換性は明示的に リリースノート に示されていますが, アップグレードする前に特別な注意が必要な場合があります.

開発リリースは3桁のバージョンに従わず, 一般的に末尾に dev をつけています. 例: 1.3dev.

注釈

Scrapy 0.* シリーズでは, Scrapyは開発版に奇数バージョンを使用しました. Scrapy 1.0 以降のケースではありません. Scrapy 1.0からは、すべてのリリースがプロダクションの準備ができていると見なされるべきです.

例:

  • 1.1.11.1 シリーズの最初のバグフィックスバージョンです. (製品版で安全に使用するための.)

API 安定性

API 安定性は 1.0 のリリースで一つのゴールを迎えました.

単一のダッシュ( _ )で始まるメソッドまたは関数はprivateであり, 決して安定したものであるべきではありません.

安定したAPIは新しいメソッドや機能を拡張する可能性がありますが、既存のメソッドは同じ方法で機能し続ける必要があります。

リリースノート
最近のScrapyのバージョンで何が変わったのか見る.
Scrapy に貢献する
Scrapyプロジェクトに貢献する方法を学ぶ.
バージョニングとAPIの安定性
ScrapyのバージョニングとAPIの安定性を理解する.