Scrapy をはじめる

# オフィシャルページなど
公式ページ https://scrapy.org/
公式ドキュメント https://docs.scrapy.org/en/latest/index.html
Github https://github.com/scrapy/scrapy

# Scrapyとは?
WebサイトなどをスクレイピングするためのPythonのフレームワークです。
[スクレイピング](https://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0) とは HTML や Javascript, CSS などから情報を収集することです。Python で usllib や re (正規表現) を組み合わせれば同じことができますが **Scrapyを使うメリット**として

- 少ない記述量
- 複数のWebページを遷移する複雑なスクレイピングができる
- 自動的に並列化された高速なクローリング
- Web UI など Scrapy 向けに作れた便利ライブラリが使える

一方でデメリットとしては

- いちいちプロジェクトを作る必要があり面倒

つまり正規表現で事足りる小規模なスクリプトでは不要と思います。

# インストール
```
pip install scrapy
```
Windowsで**ビルドに失敗する場合**、[Python公式の案内](https://wiki.python.org/moin/WindowsCompilers)を参考に[Build Tools for Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15) をインストールします。

# 最初のスクリプト
最初にプロジェクトを作成する必要があり、コマンドラインでプロジェクトを作る場所に移動したら次のようにします。
**myfirstproject** が今回の**プロジェクト名**です。

```
scrapy startproject myfirstproject
```
プロジェクトのファイル一式が自動生成されます。
次に Spider (Scarpyのクローラーのクラス) を継承したクラスの定義ファイルを作ります。

```
cd myfirstproject
scrapy genspider myfirstspider quotes.toscrape.com
```
- **myfirstspider** が今回の**クラス名**です。
- **quotes.toscrape.com** がスクレイプを行う対象のサイトの **ドメイン** です。

myfirstproject/spiders/myfirstspider.py が生成されます。
まずは **myfirstspider.py** の **parse 関数**に次のように書き換えます。

```python:myfirstspider.py
    def parse(self, response):
        text = response.xpath("//div[@class='quote']/span[1]/text()").extract()
        print(text)
```
Scrapyは spider クラスの **start_urls のページをダウンロードしたあと parse 関数を呼び出します**。
parse 関数の **response 引数にはダウンロードされたページが入っています**。
myfirstspider.py を編集できたらさっそく実行します。

```
scrapy crawl myfirstspider --loglevel WARNING
```
loglevelを指定したのはデフォルトではデバッグログが大量出力されるためです。
問題がなければ **スクレイプ されたテキストが表示されます。

```
['“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”', '“It is our choices, Harry, that show what we truly are, far more than our abilities.”', '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”', '“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”', "“Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”", '“Try not to become a man of success. Rather become a man of value.”', '“It is better to be hated for what you are than to be loved for what you are not.”', "“I have not failed. I've just found 10,000 ways that won't work.”", "“A woman is like a tea bag; you never know how strong it is until it's in hot water.”", '“A day without sunshine is like, you know, night.”']
```

parse 関数内の reponse.xpath(...).extract() にてresponse に入っているページから必要な情報を抽出しています。
xpathとは**XML(HTMLなど) から特定のノード(タグ)を指定するための記述方法**です。
xpathの[詳細](https://webbibouroku.com/Blog/Article/xpath)。忘れやすい人には**[チートシート](http://blog.bangboo.com/sub/xpath.html)**がオススメです。
ここで使っている xpath について簡単に説明すると

- //div[@class='quote'] : divタグ 内の class属性 が 'quote'であるもののみを指定
- /span[1] : そのなかのspanタグの1番目のものを指定
- /text() : そのなかのテキスト属性 を指定

xpathで指定したもの**全て**を extract() 関数 で抽出します。
これで text変数には抽出されたもののリストが入ります。

# デバッグの方法
デバッグをしながらスクリプトを完成させるまでの手順を示します。
前のセクションのスクリプトを修正して Author の情報("by Albert Einstein (about)" のテキスト) を抽出したいとします。

```
scrapy shell http://quotes.toscrape.com/
```
とするとPythonシェルが起動して、指定したアドレスをScrapyが読み込んだ後に対話モードに移行します。
ちなみに起動するPythonシェルは myfirstproject/scrapy.cfg 内の [settings] セクションにて ```shell = ipython```のように指定できます。ipythonが入っていると指定しなくても自動で使用されるようです。

対話シェルにて

```python
In [1]: response.url
Out[1]: 'http://quotes.toscrape.com'
```
responseにはダウンロードされたページが入っています。
このページのHTMLソースを確認する(chromeならCtrl+u) と ```<small class="author" itemprop="author">Albert Einstein</small>``` の記述が見つかります。**smallタグの中でclass属性がauthorであるもの**を抽出すればよさそうです。
次のようにためしてみます。

```python
In [2]: response.xpath("//small[@class='author']")
Out[2]:
[<Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>,
 <Selector xpath="//small[@class='author']" data='<small class="author" itemprop="author">'>]
```
うまくいったようですが、欲しいの このsmallタグの中のテキストです。
なので次のようにしてみます。

```python
In [3]: response.xpath("//small[@class='author']/text()").extract()
Out[3]:
['Albert Einstein',
 'J.K. Rowling',
 'Albert Einstein',
 'Jane Austen',
 'Marilyn Monroe',
 'Albert Einstein',
 'André Gide',
 'Thomas A. Edison',
 'Eleanor Roosevelt',
 'Steve Martin']
```
これで必要なテキストのリストを取得できました。
```response.xpath("//small[@class='author']/text()").extract()```をスクリプトに追加して完成させます。

# 結果の保存
スクレイピングした結果をファイルに保存する方法はいくつかあります。

- parse 関数内で直接ファイルに書き込んでしまう
- Scarpy の item, pipelines を使う
- Scapy の Feed exports を使う

今回は Feed exports を使ってみます。
まず、parse関数にて **必要な情報を辞書にして yield で返す**ようにします。

```python
 def parse(self, response):
    author_list = response.xpath("//small[@class='author']/text()").extract()
    yield {'author': author_list}
```

あとはコマンドラインから

```
scrapy crawl myfirstspider -o myfirstspider.json
```
とすると、**json 形式で"myfirstspider.json"に保存**されます。
保存するファイルの**拡張子**をかえれば csv, xmlなどでも保存できます。