Pythonでスクレイピングの勉強メモ
2018年06月24日
様々なサイトから特定の情報を収集してまとめたサイトを作成しようと思い、Pythonでスクレイピングの勉強をしています。
メモ代わりにやったことをまとめておきます。
やりたいこと
Pythonで複数のウェブサイトから特定の情報を取り出してきてまとめたい。
何故Pythonかというと、PHPでもできるのだろうけど、最近Pythonが人気なのと、勉強も兼ねて。
スクレイピングして取り出したデータをデータベースに保存するまではPythonでやって、表示するサイトはPHP+WordPressでもいいかなと。
ローカル環境でのテスト
まずサイトを立ち上げる前にローカル環境でテストを行っています。
環境
MacOS High Sierra
Pythonのインストール
まずは最新のPython3をインストール
確認したところ、本記事執筆時点でバージョンは3.6.1
仮想環境の作成
以下のコマンドで、仮想環境を作成します。
python3 -m venv scraping
仮想環境を作成してそれ以下でやることで、開発環境でライブラリの依存関係などをプロジェクトごとに制御しやすい。
上の例では「scraping」という仮想環境を作成。
上記で作成した仮想環境に入るには
. scraping/bin/activate
では入れます。
仮想環境を抜けるには
deactivate
で抜けられる。
ライブラリ「Beautiful Soup」のインストール
スクレイピングをするのに便利なライブラリ「Beautiful Soup」をインストールする。
必須ではないが、あると便利
pip install beautifulsoup4
上記のコマンドでインストールを行う。
エラーが出たが、pipのアップグレートを行うことで解決した
pip install –upgrade pip
簡単なスクレイピングのテスト
簡単なスクレイピングのテストを行った。
Beautiful Soupは、それ自体ではローカルにあるファイルしか読み込めないため、urllibというモジュールをインポートして使用し、組み合わせることで外部URLを、URL指定で読み込むことができる。
import urllib.request from bs4 import BeautifulSoup html = urllib.request.urlopen("抜き出したいHTMLページのURL") soup = BeautifulSoup(html, 'html.parser') for a in soup.find_all('a'): print(a.get('href'), a.text)
上記のプログラムでは、抜き出したいHTMLページのURLからa要素を取り出して、URLとテキスト情報を出力している。
pythonのスクレイピングでやりたいこと
特定のウェブサイトのページの、特定テーブルのtd要素を全て取り出してリスト化したい。
そのために色々と試行錯誤をしてみた。
1・Beautiful Soupを使ってみる
import urllib.request from bs4 import BeautifulSoup html = urllib.request.urlopen("スクレイピングしたいURL") soup = BeautifulSoup(html, 'html.parser') tds = soup.find_all("td") for td_s in tds: print(td_s.get_text())
上記の例では、スクレイピングしたい対象のページからtd要素のテキストを抜き出して表示するというもの。
結果としては、途中のtd要素までは取得できたものの、取得するページの要素がJavascriptの広告の部分でストップしてしまい、その先の要素を取り出せなかった。
調べてみたところ、そもそも
soup = BeautifulSoup(html, ‘html.parser’)
でhtml要素を取り出して、何故かページの途中までしか正常に取得できていなかった。
原因は不明。
2.lxmlを使ってみる
Beautiful Soulを使ってみて駄目だったので、lxmlというライブラリを利用してスクレイピングをしてみました。
import lxml.html tree = lxml.html.parse('抽出したいページ') type(tree)
まずは必要なライブラリをインストールして、基本的なプログラムを作成。
上記の例は、URL指定をしてページを取得するというもの。
取得する部分でエラー。
failed to load external entity “ページURL”
よく分からないけどできなかった。
3. Scrapyを使ってみる
スクレイピング用のフレームワーク「Scrapy」を使ってみた。
Scrapyは
pip install scrapy
でインストールできる。
>>> scrapy shell File "<stdin>", line 1 scrapy shell ^ SyntaxError: invalid syntax
最初に対話モードで実行したところ、最初の時点でいきなりエラー。
一度は実行できたのに、もう一度実行するとエラーに。
意味不明・・・。
と思っていろいろと操作していたら、pythonコマンドから実行するのではなく、コマンドライン上ですぐに実行すると実行できた。
scrapy shell 取得したいURL >>response.xpath('//table[@class="クラス名"]/tr/td/text()').extract()
上記の例では、まずはscrapyで対象のURLのページ内容を取得し、
次で、特定のクラス名のテーブル要素の、tdのテキスト要素を取得できた。
ただし、上記の例での問題として、tdの中にspan要素があり、span要素に挟まれたテキスト情報が除外されてしまうという問題が発生した
response.xpath(‘//table[@class=”クラス名”]/tr/td/span[@class=”クラス名”]/text()’).extract()
上記に変えて実行することで、除外されていたspan要素に含まれるテキスト情報を逆に全て出力することができた。
また、特定のテーブルの見出し要素の次のtd要素を取得する記述は以下の通り
response.xpath(‘//table/tr/th[text()=”テキスト”]/following-sibling::td[1]’).extract()
だんだんコツがつかめてきた。
Scrapyがツールとしては一番使えそうなかんじがした(といっても、簡単なプログラムを作成した結果問題が起きなかった)ので、とりあえずはスクレイピングに使うツールとして決定。
以下は、変数「url」に入った相対パスを、現在クローラーがいるページのURLにつなげて絶対パスに変換する記述
response.urljoin(‘url’)
プロジェクトを作成する
scrapyを使用するにあたって、まずはプロジェクトを作成する
scrapy startproject プロジェクト名
これで、プロジェクト名のディレクトリが作成される。
プロジェクトを作成すると、プロジェクト名のディレクトリ以下に、フレームワークのファイルやディレクトリ一式が自動生成される。
プロジェクトを作成後、設定ファイルとして「setting.py」ファイルが作成されるが、
クロール先のサイトに迷惑をかけないように、
DOWNLOAD_DELAY = 1
を追記しておいがほうがよい。
デフォルトのダウンロード間隔は0秒のため、クロール先のサイトに思わぬ迷惑をかけてしまう場合があるので、この設定をしておくことで、ページのダウンロード間隔が1秒になるため、クロール先のサイトに迷惑を少なくできる。
Spiderの作成
Scrapyでは、Spiderと呼ばれるファイルを作成することで、クローラーを実行する。
Spiderは、コマンドラインから以下のコマンドで作成できる。
scrapy genspider Spider名 サイトのドメイン名
上記のコマンドで、プロジェクトのspidersディレクトリ以下に、「Spider名.py」と呼ばれるファイルが作成される。
ファイルを開くと、デフォルトで
def parse(self, response):
pass
という関数が定義されているが、このparseという関数をScrapyは実行するのでこの関数を変更していく。
parseで実行した処理は、yield文を使用することでスクレイピングした値を返していく。
返す形式は、辞書形式でも問題なし、itemクラスを使用しても問題ない。
itemクラスは、プロジェクトを作成時に、「items.py」というファイル内に定義されていて、ファイルにクラスを定義することで使用できる。
yieldで返した値は、Scrapyを実行する際に、外部ファイルに出力することができる他、後述するItem Pipelineを使用することで、データベースへ保存することも可能。
実行する場合は、
scrapy crawl スパイダー名
で実行できる。
Item Pipelineの活用
ScrapyにはItem Pipelineと呼ばれる機能があり、使用することでデータをチェックしたり、データベースにデータを保存したり、parseの処理で返したItemオブジェクトを利用した処理を行うことが可能。
Pipelineの記述は、プロジェクトフォルダ内の「pipelines.py」に記述する。
pipeline.pyにクラスを定義し、setting.pyに定義をすることで呼び出す。
ITEM_PIPELINES = {
‘プロジェクト名.pipelines.クラス名’ : 300,
‘プロジェクト名.pipelines.クラス名’ : 800,
}
上記の例の300や800といった記述は優先度で、数が小さいほど優先して処理される。
0〜1000までの間で定義され、優先度が高い順から定義されたクラスが処理される。
ここで、yieldで返されたitemオブジェクトのチェックを行ったり、データベースに保存する処理を行える。
pipeline.py内の関数 process_item内に、pipeline実行時に処理をする記述を行う。