Python勉強したのでBeautifulSoupでWEBスクレイピングやってみた
こんにちは、くりあです。
いつもは大学の講義だったりUnityだったりでCやC#ばかり書いているのですが、最近Pythonを勉強し始めました。
そういうわけでせっかく勉強したので前から興味のあった「WEBスクレイピング」やってみました。
クローラー/スクレイピング Advent Calendar 2014
クローラー/スクレイピング Advent Calendar 2014 - Qiita
この記事は「クローラー/スクレイピング Advent Calendar 2014」の16日目の記事です。
やってみたこと(概要)
ある月のゲーム発売日を知りたくなったときにshellを開きPython2.7を使ってこんな風に調べることが出来ます。python publish_calender.py 2014/12
ちなみに出力はこんな感じ。
2014年12月発売のゲームカレンダー 1日 シムシティ:コンプリートエディション [オンラインコード](エレクトロニック・アーツ)[PC] 2日 Terraria(505 Games)[PS4] テイルズ オブ ジ アビス [オンラインコード](バンダイナムコゲームス)[3DS] (略) 30日 WiiU/Wii/PS4/PS3用マイク『USBシンプルマイク』(ゲームテック)[PS4] アストラエアの白き永遠 オリジナルサウンドトラック プラス(FAVORITE)[ゲームCD] 31日 Kingdom Hearts III(Ps4)[PS4] The Lego Movie Videogame(Whv Games)[3DS] Witcher 3: The Wild Hunt C.E.(Warner Bros Games)[Xbox One] Kingdom Hearts III(Xbox One)[Xbox One] Tomb Raider Platinum Hits(Square Enix LLC)[Xbox360]
こちらのカレンダーから最低限のところだけスクレイピングしてコンソールやファイルに出力するようにしました。
ゲーム 発売日
BeautifulSoup
今回スクレイピングするのにBeautifulSoup4を使用しました。いわゆるHTMLやXMLのパーサーで簡単にWEBから要素を取得出来ます。jQueryっぽい印象ですね。
これがとっても便利なライブラリで最近いろんなところをターゲットに遊んでます。
スクリプト完成までの流れ
簡単にちゃんとした出力が得られるまでの流れを挙げてみます。苦労した点もいくつか。- BeautifulSoup4の導入
- ターゲットとなるhtml(のソース)を決定
- ターゲットのソースをぼんやり眺める
- コードを書いてみる
- HTMLエンティティとエンコーディングに悩まされる
- 解決、機能拡張
- 完成
ざっくりとこんな感じでした。
WEBスクレイピングやってみようと思ってすぐにBeautifulSoup使おうと決めていました。
ちょっと試してなんとなく使い方を掴みました。
次にどのWEBページを狙うのかを決めました。これは僕が便利だな、と思うことから適当に。
さてターゲットも決まってまず最初にしたのは、ソースコードを眺めることです。
そのWEBページがどういう構成で成り立っているのかをなんとなく掴みます。
「こんな感じで書けそう」と思い付くまでぼんやり眺めましょう!
要素の親子関係を意識するとわかりやすいかもしれません。
思いついたら後はコードを書いてみました。
ここで要素の取得なんかは思うように出来たのですが出力にはめちゃくちゃ気を遣う必要がありました。
例えばこれ。
龍が如く 1&2 HD EDITION PlayStation®3 the Best
この文字列はWEBから取得するとHTML特殊文字を含む文字列
龍が如く 1&2 HD EDITION PlayStation®3 the Best
となります(今回の場合)。これを元に戻すためにワントラップ、さらに登録商標マーク「®」をそのままWindowsのコンソールなんかでprintしようとすると下の通り怒られます。
UnicodeEncodeError: 'cp932' codec can't encode character u'\xae' in position 0: illegal multibyte sequence
Windowsのコンソールはデフォルトエンコーディングが'cp932'(拡張SJIS)なのでprintのときには当然変換されるのでしょう、変換できなくても。
1つ1つ例外チェックするのも面倒だったので調べたところ、便利な一行を知りました。
sys.stdout = codecs.getwriter(sys.stdout.encoding)(sys.stdout, errors='replace')
現在設定されている標準出力のエンコーディングを検索→StreamWriterの引数を指定してインスタンス生成→標準出力に設定ということをしているようです。
コンソール出力の際にはこれを使って例外処理をするようにしました。全部を適切に置換なんて出来ないので!
参考:
cp932で表現できない文字がたまに混ざるユニコード文字列をWindowsのコンソールにprintしたい場合 - 西尾泰和のはてなダイアリー
7.8. codecs — codec レジストリと基底クラス — Python 2.7ja1 documentation
ここまでやってようやくちゃんとした出力が出来たので実行時の引数から年月と出力先の指定を出来るようにしてひとまず落ち着きました。