Scrapy入門（１）

# Scrapy入門（１）

## はじめに

PythonでWebスクレイピングを行う場合には、様々なアプローチが存在します。この記事ではスクレイピング用のフレームワークであるScrapyを題材に取り上げ実際に簡単なサンプルを作成しながら、Scrapyについて学んでいきます。

## Scrapyとは

Scarpyとは速くて、ハイレベルなスクレイピングのフレームワークです。Webサイトのクロールと、スクレイピングに関する様々な機能を持っています。主要な機能はコンポーネントに分かれており、ユーザーは各コンポーネントに関連するクラスなどを作成して、プログラムを作っていきます。

![コンポーネント図](http://doc.scrapy.org/en/1.0/_images/scrapy_architecture.png "コンポーネント図")
http://doc.scrapy.org/en/1.0/topics/architecture.html より

主要なコンポーネントは次の通りです。

### Scrapy Engine

* コンポーネント間のデータフローの制御を担当
* 特定のアクションが発生したら、イベントを発生

### Scheduler
* Engineからリクエストを受け取り、キューイングとスケジューリングを担当

### Downloader

* 実際のダウンロード処理を担当
* Download middlewareで処理を差し込む事が可能

### Spiders

* ユーザーが作成するカスタムクラス
* 取得したいURL、抽出したい部分を記述する
* ダウンロードしたコンテンツをスクレイピングして、Itemを作成

### Item Pipeline

* スパイダーによって抽出されたアイテムの出力処理
* データのクレンジング、検証
* 永続化（JSON、File、DB、Mail）

このようにScrapyには様々な機能があります。今回はまずScrapyの基本概念であるSpiderを作成して、
Qiita上にあるAdventCalendarに投稿されているURLを取得するプログラムを書いてみます。

## インストール

まずはpipにてインストールします。

```
pip install scrapy
```

## Spiderの作成

次にコンポーネントの一つであるSpiderを作成します。
Spiderにはクロールの処理を開始するためのURLのエンドポイントと、
URLの抽出するための処理を記述します。

```python
# -*- coding: utf-8 -*-

import scrapy


class QiitaSpider(scrapy.Spider):
    name = 'qiita_spider'

    # エンドポイント（クローリングを開始するURLを記載する）
    start_urls = ['http://qiita.com/advent-calendar/2015/categories/programming_languages']

    custom_settings = {
        "DOWNLOAD_DELAY": 1,
    }

    # URLの抽出処理を記載
    def parse(self, response):
        for href in response.css('.adventCalendarList .adventCalendarList_calendarTitle > a::attr(href)'):
            full_url = response.urljoin(href.extract())

            # 抽出したURLを元にRequestを作成し、ダウンロードする
            yield scrapy.Request(full_url, callback=self.parse_item)

    # ダウンロードしたページを元に、内容を抽出し保存するItemを作成
    def parse_item(self, response):

        urls = []
        for href in response.css('.adventCalendarItem_entry > a::attr(href)'):
            full_url = response.urljoin(href.extract())
            urls.append(full_url)

        yield {
            'title': response.css('h1::text').extract(),
            'urls': urls,
        }

```

## 実行

Scrapyにはたくさんのコマンドが付属しています。今回はSpiderを実行するための
runspiderコマンドを使用して、Spiderを実行します。
-oオプションを使用するとparse_itemで作成した結果を、JSON形式でファイルに保存する事が出来ます。

```shell
scrapy runspider qiita_spider.py -o advent_calendar.json
```

## 結果

実行結果は次の通りです。
各アドベントカレンダーのタイトルと投稿されたURLの一覧を取得することができました！

```json
{
    "urls": [
      "http://loboskobayashi.github.io/pythonsf/2015/12-01/recomending_PythonSf_one-liners_for_general_python_codes/",
      "http://qiita.com/csakatoku/items/444db2d0e421265ec106",
      "http://qiita.com/csakatoku/items/86904adaa0922e80069c",
      "http://qiita.com/Hironsan/items/fb6ee6b8e0291a7e5a1d",
      "http://qiita.com/csakatoku/items/77a36888d0851f6d4ff0",
      "http://qiita.com/csakatoku/items/a5b6c3604c0d411154fa",
      "http://qiita.com/sharow/items/21e421a2a40275ee9bb8",
      "http://qiita.com/Lspeciosum/items/d248b64d1bdcb8e39d32",
      "http://qiita.com/CS_Toku/items/353fd4b0fd9ed17dc152",
      "http://hideharaaws.hatenablog.com/entry/django-upgrade-16to18",
      "http://qiita.com/FGtatsuro/items/0efebb9b58374d16c5f0",
      "http://shinyorke.hatenablog.com/entry/2015/12/12/143121",
      "http://qiita.com/wrist/items/5759f894303e4364ebfd",
      "http://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8",
      "http://qiita.com/satoshi03/items/ae616dc080d085604b06",
      "http://qiita.com/CS_Toku/items/32028e65a8bfa97266d6",
      "http://fx-kirin.com/python/nfp-linear-reggression-model/"
    ],
    "title": [
      "Python \u305d\u306e2 Advent Calendar 2015"
    ]
  },
  {
    "urls": [
      "http://qiita.com/nasa9084/items/40f223b5b44f13ef2925",
      "http://studio3104.hatenablog.com/entry/2015/12/02/120957",
      "http://qiita.com/Tsutomu-KKE@github/items/29414e2d4f30b2bc94ae",
      "http://qiita.com/icoxfog417/items/913bb815d8d419148c33",
      "http://qiita.com/sakamotomsh/items/ab6c15b971587905ef43",
      "http://cocu.hatenablog.com/entry/2015/12/06/022100",
      "http://qiita.com/masashi127/items/ca092c13e1300f4f6ade",
      "http://qiita.com/kaneshin/items/269bc5f156d86f8a91c4",
      "http://qiita.com/wh11e7rue/items/15603e8970c36ab9733d",
      "http://qiita.com/ohkawa/items/368e6bcadc56a118adaf",
      "http://qiita.com/teitei_tk/items/5c5c9e653b3a13108d12",
      "http://sinhrks.hatenablog.com/entry/2015/12/13/215858"
    ],
    "title": [
      "Python Advent Calendar 2015"
    ]
  },
```

## 終わりに

今回はScrapyの基本的なコンポーネントである、Spiderを作成してスクレインピングを行いました。
Scrapyを使用すると、クローリングに関する定型的な処理はフレームワーク側で処理を担当してくれます。そのため開発者はURLの抽出処理や、データの保存処理などサービスやアプリケーションに本当に必要な部分のみを記述して開発することができます。次回以降はキャッシュや保存処理などについて取り上げます。お楽しみに！

[Scrapy入門（２）](http://qiita.com/checkpoint/items/65bb757d305f4a1288c0)