Section6 Scrapy Advanced Spider

環境設定をする。

$ scrapy startproject test_quotes
$ scrapy genspider test_quotes http://quotes.toscrape.com/

shellで準備をする。

scrapy shellを使って動作確認をする。
response.xpath('//[@class="quote"]') でとれる確認する
次にquotes = response.xpath('//
[@class="quote"]')と式を入れる
そして、quote = quotes[0]として、quote.extract()1段目を抽出する。

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

In [1]: response.xpath('//*[@class="quote"]')
Out[1]: 
[<Selector xpath='//*[@class="quote"]' data='<div class="quote" itemscope itemtype="h'>,

In [2]: quotes = response.xpath('//*[@class="quote"]')

In [3]: quote = quotes[0]

In [4]: quote
Out[4]: <Selector xpath='//*[@class="quote"]' data='<div class="quote" itemscope itemtype="h'>

In [5]: quote.extract()
Out[5]: '<div class="quote" itemscope itemtype="http://schema.org/CreativeWork">\n        <span class="text" itemprop="text">“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”</span>\n        <span>by <small class="author" itemprop="author">Albert Einstein</small>\n        <a href="/author/Albert-Einstein">(about)</a>\n        </span>\n        <div class="tags">\n            Tags:\n            <meta class="keywords" itemprop="keywords" content="change,deep-thoughts,thinking,world"> \n            \n            <a class="tag" href="/tag/change/page/1/">change</a>\n            \n            <a class="tag" href="/tag/deep-thoughts/page/1/">deep-thoughts</a>\n            \n            <a class="tag" href="/tag/thinking/page/1/">thinking</a>\n            \n            <a class="tag" href="/tag/world/page/1/">world</a>\n            \n        </div>\n    </div>'

この部分が抜き取られることになる。 f:id:yukking3:20180512225017p:plain

①本文の抽出方法

('.//a')とドットを入れると少量で抽出する。
quotes をquotes[0]都とすることで、最初の1個だけに絞る。

In [1]: quotes = response.xpath('//*[@class="quote"]')

In [2]: quote = quotes[0]

In [6]: quote.xpath('.//a')
Out[6]: 
[<Selector xpath='.//a' data='<a href="/author/Albert-Einstein">(about'>,
 <Selector xpath='.//a' data='<a class="tag" href="/tag/change/page/1/'>,
 <Selector xpath='.//a' data='<a class="tag" href="/tag/deep-thoughts/'>,
 <Selector xpath='.//a' data='<a class="tag" href="/tag/thinking/page/'>,
 <Selector xpath='.//a' data='<a class="tag" href="/tag/world/page/1/"'>
In [7]: quote.xpath('.//*[@class="text"]').extract()
Out[7]: ['<span class="text" itemprop="text">“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”</span>']

_firstを入れるとより見やすくなる。

In [11]: quote.xpath('.//*[@class="text"]/text()').extract_first()
Out[11]: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'

ドットをつけた場合とつけなかった場合の違い。 f:id:yukking3:20180512230910p:plain

②Autherの抽出方法

実際のページを確認すると f:id:yukking3:20180512231345p:plain

以下のtext部分をauthorに変更するだけ。

quote.xpath('.//*[@itemprop="text"]/text()').extract_first()    

In [22]: quote.xpath('.//*[@itemprop="author"]/text()').extract_first()
Out[22]: 'Albert Einstein'

③タグの取得方法

最初にhtmlを確認する。 f:id:yukking3:20180512231655p:plain

[@itemprop="keywords"]/の部分だけ変えると何も出ない。 f:id:yukking3:20180512231823p:plain

/text()'の部分を/@content'に変える成功する。 f:id:yukking3:20180512232015p:plain

もう一つの方法としては、
quote.xpath('.//*[@class="tag"]/text()').extract() 
とするともっとシンプルになる。 f:id:yukking3:20180512232350p:plain

.// VS //

実際に検証して見た

In [1]: fetch('http://quotes.toscrape.com/')
In [2]: quotes = response.xpath('//*[@class="quote"]')
In [3]: quote = quotes[0]
In [4]: quote
Out[4]: <Selector xpath='//*[@class="quote"]' data='<div class="quote" itemscope itemtype="h'>

In [5]: quote.xpath('.//*[@class="text"]/text()').extract_first()
Out[5]: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'

In [6]: quote.xpath('//*[@class="text"]/text()').extract_first()
Out[6]: '“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”'

In [7]: quote.xpath('//*[@class="text"]/text()').extract()
Out[7]: 
['“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 


In [8]: quote.xpath('.//*[@class="text"]/text()').extract()
Out[8]: ['“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”']

つまり、the "//" expression with a "." tells the XML search engine to execute the search relative to the current node reference.
" . "は現在のノードの部分のみを取得する。

pyファイルに書き込んでSpiderを作って動かして見る

import scrapy


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

    def parse(self, response):
        quotes = response.xpath('//*[@class="quote"]')
        for quote in quotes:
            text = quote.xpath('.//*[@class="text"]/text()').extract_first()
            author = quote.xpath('.//*[@itemprop="author"]/text()').extract_first()
            tags = quote.xpath('.//*[@itemprop="keywords"]/@content').extract_first()

            yield{'Text': text,
                  'Author': author,
                  'Tags': tags}

quotes →10個分
quote →1個分

大量位取得出来ているのが確認できる。 f:id:yukking3:20180512232839p:plain

今回詰まった点

allowed_domains とstart_urlsをなおさなければならい。 f:id:yukking3:20180603122518p:plain

Nextボタン以降の情報の取得方法

最初にページを確認する。 f:id:yukking3:20180513111351p:plain

次にNextを取得してみる。
注意点としては、hrefの前には@がつく!
①//* 
②[@class="next"] 
③/a 
④/@href 
f:id:yukking3:20180513111714p:plain

次にURLを取得してつぎのページを読み込まなくてはいけないので
absolute_nextpate_urlを設定する。 f:id:yukking3:20180513112100p:plain

最後にyield scrapy.Request(absolute_next_page_url)でつぎのページでの処理が実行される。

When spider finishes crawling last page, it will try to get the content of class named 'next' (response.xpath('//*[@class="next"]/a/@href') that does not exists = null/none.

①もし、NEXTがなければnoneが返される。 ②また、scrapy.dupefiltersがあって、くり返されるものは無視される。

        next_page_url = response.xpath('//*[@class="next"]/a/@href').extract_first()
        absolute_next_page_url = response.urljoin(next_page_url)
        yield scrapy.Request(absolute_next_page_url)

CSVファイルに落とし込む

スクレイピングできるスパイダーがあれば残りは以下の通りにするだけ。

$ scrapy crawl quotes -o item.csv

フォルダーを確認すると、以下のファイルがある。 f:id:yukking3:20180513115029p:plain ファイルを開くとcsvに保存されているのが確認できる。 f:id:yukking3:20180513115032p:plain 同じ要領でjsonファイルにすることもできる。

quotes.pyファイルをprintからyieldに変更する。

以下のコードをyieldデータに変更する。 f:id:yukking3:20180513125746p:plain yieldに変更する理由は、辞書型にすることで別ファイルへエクスポートすることが可能となる。

アーキテクト

configファイルのおかげで順序よくスパイダーなどが動くようになる。 ここで順に説明する。

Item Loader
抽出したデータを Item に格納する際に使える便利メソッドを持つFactoryクラスです。それら便利メソッドを使って値を Field に格納し、最後に load_item() メソッドを呼び出すと Item インスタンスが一つ作成されます。
Item Pipeline
Item 一つに対し加工を行ったり、データチェックしたり、外部に情報を出力したりする仕組みです。

cfg.pyを確認する。

f:id:yukking3:20180513130706p:plain

中身はこのようになっている。 f:id:yukking3:20180513130531p:plain settingがある。 urlはlocalhost projectはquote_spiderとなっている。

実際にprojectフォルダーをのぞいてみると。 f:id:yukking3:20180513130749p:plain item.py pipeline.py setting.pyなどがある。

item.pyを設定する。

item.pyを設定することで、例えばエクセルのデータの一番目をAuthorにするなど、データの順を変更することができる。 item.pyの初期はこんな感じ。 f:id:yukking3:20180513131217p:plain 最初にするのは以下のように2つをimportする。

from scrapy.loader import ItemLoader
from quotes_spider.items import QuotesSpiderItem

次にdef parse(self, response)にitemLoaderを記載する。
(itemはQuotesSpiderItem()である。

        l = ItemLoader(item=QuotesSpiderItem(), response=response)

        h1_tag = response.xpath('//h1/a/text()').extract_first()
        tags = response.xpath('//*[@class="tag-item"]/a/text()').extract()

        l.add_value('h1_tag', h1_tag)
        l.add_value('tags', tags)

        return l.load_item()

pipeline.pyを設定する。

pipeline.pyを使う理由は、importしたデータを整形する役目がある。

初期ではこんな感じ。 f:id:yukking3:20180513141803p:plain 以下のように2行追加するだけで、

class TestQuotesPipeline(object):
    def process_item(self, item, spider):

        if item['h1_tag']:
            item['h1_tag'] = item['h1_tag'][0].upper()

        return item

pipeline.pyの65行目のコメントアウトを直す。 f:id:yukking3:20180513142213p:plain

初期ではsomepipelineになっているのでそれをQuotesSpiderItemに変更する。 f:id:yukking3:20180513142338p:plain

実際に実行してみると大文字で表示される。 f:id:yukking3:20180513142453p:plain

setting.pyの役割

重要なのはDOWNLOAD_DELAY これを設定することで3秒以上反応なければERROR処理される。 f:id:yukking3:20180513143006p:plain