LoginSignup
6
3

Scrapy入門

Last updated at Posted at 2022-10-30

UnsplashKrzysztof Niewolnyが撮影した写真

Scrapyは非常に便利だが、何かをスクレイピングしたいというニーズは、そんなに頻繁に起こるわけではないので、そんなことがあるたびに、毎回、使い方をググっている。いい加減めんどうくさいので、簡単な使い方を、ここにまとめておくことにした。

ここでは、ざっくりとした手順だけを書いておき、各々のステップの詳細は別途ググるつもりで書いている。

B-Leagueの試合日程を公式ページからスクレイピングするのに使ったプロジェクトをサンプルとしておいておくlink
以下のコマンド実行例やコードのサンプルは、このサンプルを念頭に書かれている。

主な手順

Scrapyでスクレピングをするときには、以下のようなステップを踏むことになる。
Scrapyプロジェクトの構成や、Item・Spider・PipelineといったScrapy用語については、以下に述べるステップの詳細の中で説明する。

  1. プロジェクトの作成
  2. Spiderの作成
  3. Itemの定義
  4. Pipelineの定義
  5. 設定ファイルの調整

プロジェクトの作成

  1. プロジェクト用のディレクトリを作る
  2. poetryで仮想環境を作る
  3. scrapyをインストールする
  4. poetryの仮想環境に入る
$ mkdir scraping-bleague
$ cd scraping-bleague
$ poetry init
$ poetry add scrapy
$ poetry shell

するとscrapyコマンドが使えるようになる。これを使って、プロジェクトを初期化する。

$ scrapy startproject bleague

これによって、Scrapyプロジェクトの雛形が出来上がる。

Scrapyプロジェクトは、主に

  • 設定ファイル
  • Spiderの定義: どのURLを訪問するか・そのURLが指すリソースからどうやって情報を取得するか、などを書くところ
  • Itemの定義: 取得していようとしている情報の形を定義するところ
  • Pipelineの定義: 取得した情報を、どうやって処理する(DBに保存したらファイルに書いたり統計処理をしたり)かを書くところ

といった構成要素からなる。上のコマンドで、spider以外の雛形が作成される。
以下のような雛形が作成されるはず。

scraping-bleague
├── bleague
│   ├── bleague
│   │   ├── __init__.py
│   │   ├── items.py
│   │   ├── middlewares.py
│   │   ├── pipelines.py
│   │   ├── settings.py
│   │   └── spiders
│   │       └── __init__.py
│   └── scrapy.cfg
├── poetry.lock
└── pyproject.toml

追記: 上のコマンドだと、scraping-bleague/bleague/bleague以下に.pyファイルを配置していくことになる。深すぎる場合は、pyproject.toml, poetry.lockファイルと同じ階層にコードを移してしまっても良い。

以下のコマンドは、全てscrapy.cfgファイルが存在する場所で実行する・・ので、そこに移動しておく。

cd bleague

Spiderの作成

Scrapyでは、訪問するURLや、得られたリソースをどうやってパースするかといった情報はSpiderクラスの子クラスとして定義する。
以下のコマンドで、www.bleague.jpドメイン以下のURLからスクレピングするためのSpiderの雛形が作成される。

$ scrapy genspider bleague-match www.bleague.jp

これによって、以下のようなscraping-bleague/bleague/bleague/spiders/bleague_match.pyファイルができる。

bleague_match.py
import scrapy

class BleagueMatchSpider(scrapy.Spider):
    name = 'bleague-match'
    allowed_domains = ['www.bleague.jp']
    start_urls = ['http://www.bleague.jp/']

    def parse(self, response):
        pass

この段階で、とりあえずクローラを走らせることができる。

$ scrapy crawl bleague-match

spiderの書き方

spiderを書くにあたって必要な基礎知識を箇条書きしておく。

  • spiderは、最初にstart_urlsにあるurlをgetする
  • getの結果が、responseオブジェクトに入った状態でparseが呼ばれる
    • response.xpathなどで要素にアクセスできる
  • 必要な情報を取得したら、それを辞書またはItem(詳細は後述)クラスのオブジェクトにまとめてyieldする
  • 訪問したページからリンクを取得して別のページを訪問したい場合はresponse.follow()yieldする
  • scrapy crawl <spider name> -a <key>=<value>という形で、コマンド引数を利用できる。ここで与えた値は、spiderの__init__の引数として受け取ることができる。コマンドで与えるkey__init__の引数名になる。この値は、文字列として渡ってくるので、適宜パースが必要

B-leagueのトップページからaタグを2つ抜き出し、そのリンク先のタイトルを取得する例

class BleagueMatchSpider(scrapy.Spider):
    name = 'bleague-match'
    allowed_domains = ['www.bleague.jp']
    start_urls = ['http://www.bleague.jp/']

    def parse(self, response):
        links = response.xpath("//a")

        cnt = 0
        i = 0
        while cnt < 2:
            url = links[i].attrib["href"]
            if any(map(lambda d: d in url, self.allowed_domains)):
                cnt += 1
                yield response.follow(url, self.get_title)
            i += 1

    def get_title(self, response):
        title = response.xpath("//title/text()").get()
        yield dict(title=title)

開発用のrepl

from scrapy.shell import inspect_response
inspect_response(response, self)

というコードを書いておくと、クロール中に上記のコードが評価される時点で、対話環境に入ることができる。この中で、responseの中身などを見ることができるので便利。view(response)で取得したhtmlをブラウザで見ることができる。

Itemの定義

自動生成される雛形の中に、scraping-bleague/bleague/bleague/items.pyというファイルがある。
この中に、スクレイピングによって取得したデータの形式をItemクラスのサブクラスとして定義する。
ここにItemとして定義せずに、dictを利用して任意のデータ形式を使うこともできる。

Pipelineの定義

自動生成される雛形の中に、scraping-bleague/bleague/bleague/pipelines.pyというファイルがある。
この中に、取得したItem(またはdict)をどうやって処理するかを書く。

Pipelineの書き方

pipelineを書くにあたって必要な基礎知識を箇条書きしておく。

  • itemに、spiderがyieldしたオブジェクトが渡される
  • spiderには、それをyieldしたSpiderオブジェクトが渡される
  • pipelineは(名前から想像されるように)、複数定義して、それらを順番に適用するように想定されている。そのため、process_itemの最後に、渡されたitemをreturnして、次の処理にオブジェクトを渡す
  • 適用されるPipelineや、それらの順番は、scraping-bleague/bleague/bleague/settings.pyITEM_PIPELINESで指定する
    • なので、Pipelineを定義したら、この項目を編集しなければ処理に反映されない

Itemからtitleを抜き出して、/tmp/titles.txtに追記していく例

class BleaguePipeline:
    def process_item(self, item, spider):
        title = item["title"]
        with open("/tmp/titles.txt", "a") as fp:
            fp.write(title + "\n")
        return item

ここまでを実装して、scrapy crawl bleague-matchを実行すると/tmp/titles.txtに値が入るはず。

設定の調整

クローラの設定は、scraping-bleague/bleague/bleague/settings.pyで記述される。

  • robots.txtに従うか or Not
  • 同一ドメインに同時に投げるリクエストの数

などを、ここでコントロールできる。

トラブルシュート

Mac OS + pyenvで作業した際に、_lzmaというパッケージがないというエラーを吐いた。この方法で対処link

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3