satewn-memo

だいたい技術っぽい雑記

Python3でWebスクレイピング・クローリング入門:Scrapy実践編

前回の入門編の続き。 今回はScrapyの公式ドキュメントに沿って、簡単なチュートリアルを解説する。

Scrapy導入

MacLinuxでpipが使える環境にあるなら、いつも通り

pip install scrapy

で問題ない。もしエラーが出たら

pip install twisted

でtwistedを導入してからやり直そう。

Windowsでまともにインストールしようとすると幾つもライブラリを追加しなければならなかったり、幾つもパスを通さなければいけなかったりと面倒くさいが、Anacondaに一任すれば問題なかった。Anaconda導入については他記事に任せる。

activate py35con

コマンドプロンプトで上記を入力し、Anacondaのcondaで作ったPython3.5環境に入る。 あとは以下のコマンドであっさりと導入できる。

conda install -c conda-forge scrapy

公式ドキュメントにも、Windowsを使用している場合はcondaで導入するのが一番良い方法だと書かれている。

Scrapyによるスクレイピングのテスト

適当な作業用ディレクトリに移動し、以下のコマンドを入力するとScrapyのプロジェクトが作成される(tutorialはプロジェクト名なのでなんでもよい)。

scrapy startproject tutorial

次に、Spiderを作成する。Spiderというのはどのページの情報をどのように抽出するかを定義するもの。 以下のようなサンプルファイルを用意し、quotes_spider.pyという名前で/tutorial/tutorial/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)

各自で作成するSpiderクラスはscrapy.Spiderを継承している必要がある。 このサンプルではquotesというサイトのページを二つ取得するが、抽出処理は行っておらず、ただ指定されたURLのHTMLコピーをローカルに作成するだけのものである。 Spiderを用意したら以下のコマンドで起動できる(上記コードのnameで指定した名前を使う)。

scrapy crawl quotes

コマンドラインにログが出力され、正しく終了していればtutorialディレクトリにquotes-1.htmlとquotes-2.htmlが作成されている。 これらのファイルは元のサイトのHTMLそのままだが、cssが無いためブラウザで開いても見た目が悪い。

先ほどのサンプルで、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::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

このコードの動作を簡単に説明すると、

  • quoteというクラス名を持つすべてのdiv要素の子に対して
  • textというクラス名を持つspan要素のテキストを抽出し
  • span要素の子のsmall要素のテキストを抽出し
  • tagsというクラス名を持つdiv要素の子でかつ、tagというクラス名を持つa要素のテキストを抽出し
  • 辞書に格納する

ちなみにexctract_firstメソッドを用いると、条件にマッチした要素が存在しなくてもNoneを返してくれるためエラーにならない。 このようにSpiderを変更し、コマンドプロンプト

scrapy crawl quotes -o quotes.json

と入力してやると、所望の辞書がjsonとして出力される。 この他にも条件に合うリンクを辿り、その先でデータの抽出を行ったり、辿る深さなども設定できる。 これらのオプションは初めからScrapyに用意されており、複雑な記述をする必要がない。

Scrapy 実行の流れ

Scrapyのアーキテクチャ図を以下に示す(こちらの記事から拝借)。

Scrapy Architecture

Spiderが実行された時の処理の流れは以下のようになる。

  1. まず、start_urlsに含まれるURLを指すRequestオブジェクトがSpiderからSchedulerに渡される
  2. SchedulerはRequestをキューに溜める
  3. キューに追加されたオブジェクトは順にDownloaderに渡される
  4. DownloaderはWebページを取得し、Responseオブジェクトに格納し、それをSpiderに渡す
  5. Responseを引数としてparse()が呼び出される
  6. SpiderからItemや辞書が出力された場合はFeed Exporterへ、Requestが出力された場合はSchedulerに渡される
  7. 以上を繰り返し、キューからRequestがなくなれば実行完了

図にある各種ミドルウェアは、DownloaderやResponse, Requestに対する処理を拡張する役割をもつ。

おわりに

かなり駆け足になってしまったが、公式のチュートリアルに沿って簡単なスクレイピングを解説した。この短いコードだけではScrapyの強力さを説明しきれないので、是非いろいろとオプションなり拡張なり試してもらえると、楽しくスクレイピングが出来るはずだ。