Scrapy入門① とりあえずクローラーを作って実行し、Webサイトからデータを取得してみるとこまで

スポンサーリンク

クローリング・スクレイピングフレームワークのScrapyを使ってみる

Scrapyとは、クローリング・スクレイピングのPythonのフレームワークです。

ぶっちゃけPythonそんな好みじゃないので、はじめはRubyでやろうと思ったんですが、Rubyのクローリング・スクレイピングフレームワークが数年前に開発が止まっちゃってたり、なんだかんだScrapyが強力かつ使いやすいっぽいとの各所での評判により使ってみることにしました。

ちなみに、Scrapyはしばらくの間Python2系でしか動かなかったみたいですが、2016年にリリースされたScrapy ver1.1からPython3に対応しました。

この記事、というかこのブログでは基本的にはPython3系を前提にコードを書いていく予定です。

クローラー、クローリングって何?

そもそもクローリングとは、ざっくり言うと自動でwebを巡回してwebサイトなどからデータを収集する行為のことです。

そのクローリングをしてくれるプログラムのことをクローラー、クローラbot、もしくは単にbotなんかとも呼ばれます。

また、Web上のリンクを次々とたどっていく様子からか、スパイダーと呼ばれることもあり、今回使用するScrapyでは、クローリングの処理に用いる部分は”Spider”と名付けられています。

スクレイピングってなに?

スクレイピングとは、前述のクローリングで収集したデータから、必要な情報を抜き出す行為のことです。

クローリングとスクレイピングは同じ意味合いで使われることも多く、混同されやすいのですが、当ブログ内では

  • クローリング → 巡回・データ収集
  • スクレイピング → 元データからの情報抽出・整形処理

という意味合いで呼び分けていきます。

Scrapyのインストール

Scrapyのインストールにはpipを使うとラクチンです。

$ pip install scrapy

インストールに成功していれば、Scrapyコマンドが使えるようになります。
正しくインストールできているか、次のコマンドでバージョンを確認してみましょう。

$ scrapy version
Scrapy 1.4.0

実際にクローラーを作ってみる

今回は、Scrapyのアーキテクチャとか実行順序、データの流れなどの詳しい部分は一旦省略します。

まずはとりあえず動くものを作り、ざっくりどんなものか感じをつかんでからの方がとっつきやすいと思うので。

Spiderの作成

Scrapyでクローリング・スクレイピングをする際には、今回作成するSpiderというクラスを主に利用します。

Spiderには対象となるWebページをクローリングする際の設定やスクレイピングの処理を書いていきます。

今回のサンプルでは、Scrapy公式サイトにあるサンプルをもとに、何やってるのか適宜日本語でコメントをつけていきます。

このサンプルでは、ScrapyをメンテナンスしているScrapinghubのブログから、すべての記事タイトルを抜き出して一覧にするSpiderを作成します。

サンプルコード(samplespider.py)

genspiderコマンドでSpiderを一発生成する

Scrapyのgenspiderコマンドを使えば、Spiderのひな形を生成することができます。
このひな形を使えば、効率よくSpiderを作っていくことができます。

$ scrapy genspider samplespider blog.scrapinghub.com

genspiderコマンドでは、第1引数にSpiderの名前、第2引数にクロール対象のドメインを指定して実行してやると、下記のようにそれぞれの値がnameとallowed_domainsに自動で設定されます。

ちなみに、クラス名には”Spider”が自動でつきますので、この例のようにSpiderの名前を”samplespider”にしてしまうと、生成されるSpiderのクラス名が”SamplespiderSpider”と変な感じになってしまうのでご注意ください。

runspiderコマンドでSpiderを実行

では、作成したSpiderを以下のコマンドで実行してみましょう。
以下のように、scrapy runspider コマンドでSpiderを実行できます。

$ scrapy runspider samplespider.py -o sampleitems.jl

-o FILENAMEオプションで、抽出したデータ(ScrapyではItemと呼ぶことが多いので、今後はItemで)の保存先を指定できます。

拡張子の”.jl”はJSON Lines形式のことで、各行にJSONオブジェクトを追記していくイメージです。

古いバージョンのScparyを使っていると、AttributeError: ‘HtmlResponse’ object has no attribute ‘follow’ in scrapy.みたいなエラーが出ることがあります。

その場合、最新のScrapyにアップデートしてやりましょう。

ちなみに、アップデートのやり方は以下のコマンド。

$ pip install -upgrade scrapy

実行結果の確認

$ cat sampleitems.jl
{“title”: “Do Androids Dream of Electric Sheep?”}
{“title”: “Deploy your Scrapy Spiders from GitHub”}
{“title”: “Looking Back at 2016”}
{“title”: “How to Increase Sales with Online Reputation Management”}
{“title”: “How to Build your own Price Monitoring Tool”}
…(中略)…
{“title”: “Scrapy 0.14 released”}
{“title”: “Dirbot \u2013 a new example Scrapy project”}
{“title”: “Introducing w3lib and scrapely”}
{“title”: “Scrapy 0.12 released”}
{“title”: “Spoofing your Scrapy bot IP using tsocks”}
{“title”: “Hello, world”}

こんな感じで、記事タイトルが最後のページまで全て抜き出せていたら成功です。

サンプルの解説

それでは、サンプル(samplespider.py)の挙動を詳しく見ていきます。

start_urlに指定したページを取得

まず、scrapy runspiderでsamplespider.pyが実行されると、start_urlに指定したページを取得します。

ページが取得できたら、その結果をscrapy.Responseオブジェクトに格納し、parse(self,response)メソッドを呼び出します。

parseメソッドの実行

ページ内にある記事タイトルの取得

parseメソッド内では、まず記事タイトルを抜き出すための処理を行います。

response.css(‘h2.entry-title’)メソッドで、指定したcssセレクタにマッチする要素を取得し、SelectorListオブジェクトとして返します。

そしてfor文内でSelectorListのそれぞれの要素(title)に対しtitle.css(‘a ::text’).extract_first()を実行し、記事タイトルの文字列を取得しyieldで都度結果を返しています。

html要素のテキストを取得するには、疑似クラス”::text”を使ってテキストノードを取得してから、”extract()”メソッドを実行します。

extract()とextract_first()
複数のノードからテキストを取得するにはextract()を使用する。結果は文字列のリスト
1つのノードからテキストを取得するにはextract_first()を使用する。結果は文字列

次のページへのリンクを取得

現在のページにある記事タイトルをすべて取得したら、次のページに移動するためにリンクをたどります。

次ページへのリンクは、response.css(‘div.prev-post > a’)で取得し、next_pageに格納します。

そして、取得したリンクを自身のparseメソッドに渡し、次ページへのリンクがなくなる=最後のページになるまで再帰的にparseを実行し続けることで、最後までページをたどってすべての記事タイトルを取得することができます。

感想とか

始めにちょっとしたお作法とかを覚える必要はありますが、Scrapyを使うと短いコードで簡単にスクレイピングができるので便利です。

今回のサンプルみたいに、ブログの記事タイトルを抜き出すくらいじゃたいした役には立ちませんが、例えばGoogle検索結果の上位ページからテキストを抜き出し、形態素解析にかけたりなんやかやしてSEOの参考にしたりとか、特定のページを定点観測してゴニョったりとか、AIの学習用データをWebから自動で集めたりとか、いろいろ応用が利く分野だと思います。

ただ何も考えずにクローリングしまくると、相手先に迷惑をかけてしまうことになるので注意が必要です。

クローリングの実行には最低でも1秒以上の間隔をあけるなど、最低限のマナーを守るようにしましょう。

Scrapyにはクロール間隔の調整とか、robots.txtの指示に従うようにするとか、クロール先に迷惑をかけないための設定も用意されているので、また次回以降でまとめたいと思います。

【PR】

参考にしたもの

Scrapy
https://scrapy.org/

How do I update Scrapy from the Terminal? – StackOverFlow
https://stackoverflow.com/questions/21258961/how-do-i-update-scrapy-from-the-terminal

Pythonロゴ
https://www.python.org/community/logos/

ABOUTこの記事をかいた人

職業:遊び人。1日の半分は睡眠時間の超ロングスリーパー。元大手IT企業のサラリーマンだったが、ブラックな労働環境で体を壊した挙句クビになり、やむをえず独立。それ以来定職にもつかず、半分遊びのようなヌルい仕事をしながら適当に暮らしている。良く言えばノマドワーカー。 詳しいプロフィールはこちら