Rubyとか使ってクローリングやスクレイピングするノウハウを公開してみる!
- 今まで何度もスクレイピングとかクローリングをしてきたので、マエショリストの端くれとしてコツを公開すべきかなあ、と思い、公開します。
今日の題材は、CNET Newsです。私はウェブ文書にタグ付けをするという研究をしているのですが、そのための教師データとしてクローリングをします。
要件定義
CNET Newsから全ニュースを保存し、その本文をデータベースに保存します。これは、次のようなフローに書き下すことができます。
- 全URLを取得し、データベースに保存
- データベースにある全URLをダウンロードする
- 保存した全ページを解析して、本文をデータベースに保存する
という流れです。これらは独立していますから、それぞれどの言語で解析しても構いません。しかし後述しますが、「あとから追いやすくする」「適材適所」といったあたりを気をつけて、言語選択をするべきだと思います
0. データベースを用意
まず、保存するにあたって、データベース・サーバーなどを用意しておくと良いと思います。大量のデータを、ソートしたり、フィルターしたりするのに非常に便利だからです。したがって、URLはすべてデータベースに保存することとします。
いくつか選択肢がありますが、「簡単」ということで言えば、SQLiteのようにポータブルなものや、MongoDBのようなNoSQLなものを使うと良いような気がします。
私はMongoDBに苦しめられた記憶があるので、SQLiteを使っていますが、スキーマ管理の面倒臭さからMongoDBなどをおすすめします。が、私は苦しめられたのでSQLiteを使って説明します。
SQLiteのデータベースの作成は、コマンドラインインターフェースを使うのもひとつの手ですが、「あとから追いやすくする」という目的のため、一つのファイルにちゃんと書いておくといいと思います。私の場合は、以下のようにRubyのスクリプトを書き、これを実行することでデータベースを作成しています
1. 全URLを取得し、データベースに保存
例えばブログのように、ある記事があって、一個前の記事をたどり、すべての記事にたどり着けるような場合は、このステップは不要です。しかしCNET Newsの場合はそのような構造になっていないので、全URLを先に取得することを考えます。
通常であればトップページ(インデックスページ)を使いますが、CNET Newsの場合はトップページがAjaxを使っていて面倒くさいこと、Sitemapがあることなどから、Sitemapを使います。
これを見てみると、たかだか1000ページ以下のHTMLにURLがまとまっているのがわかると思います。たかだか1000ページ以下なので、特に工夫せず、全件フェッチをします。
本来であれば(件数が増えれば)、2.のように「一旦ダウンロード」して、「解析」するという2つのステップに分割したほうが良いです。そうすれば再解析したいときも再度ダウンロードする必要がなく、サーバーの負荷を防いだり、時間的を節約できます。また、途中でこけたり(Exception)したときも、再ダウンロード不要です(3回くらいこけた)。
ということで、これは普通にバッドプラクティスですね、真似しないでください
また、今回はデータ解析のためにダウンロードしているのでrobots.txtを無視していますが、「インターネット情報検索サービス事業者」として収集をする場合にはrobots.txtの指定に従う必要があります。
3. 本文をデータベースに保存する
全URLを取得したので、今度は本文をダウンロードします。先ほど述べましたが、「一旦ダウンロード」してから「本文を取り出してDBに保存」という、2つのステップを踏みます。
また、多くのページをダウンロードするときには、Resqueというシステムを使うと良いと思います。Resqueは、「非同期バッチ処理をする」ソフトウェアだと言われますが、簡単に言えば、タスク管理システムです。「ページのダウンロード」「ダウンロードしたページの解析」をそれぞれタスクとして登録しておいて、それをワーカーに非同期に処理させることができます。例えば10000ページをダウンロードする際、たまたまどこかで503エラーになってしまったら、それ以降の処理がすべて止まってしまいます。しかし1つ1つをタスクとして処理すれば、1つのタスクに失敗しても、次のタスクをワーカーが非同期に処理してくれます。
まずは、「ページをダウンロードする」というタスクを登録するためのコードです。
で、そこで登録される FetchPage
というタスクのコードです。
このタスクは、ダウンロードして保存して、「ダウンロードしたページを解析して本文をDBに保存する」という ExtractPage
タスクを登録します。
以上で、タスクが登録されるので、以下のコマンドでワーカーを作動させます。
(追記)ダウンロードの方に、負荷がかからないためのインターバルを追加しました。
$ PIDFILE=./resque-extract.pid BACKGROUND=yes QUEUE=cnet_extract rake resque:work $ PIDFILE=./resque-fetch.pid INTERVAL=5 BACKGROUND=yes QUEUE=cnet_fetch rake resque:work
こうすると、しばらくサボっているとデータベースの方へ保存されます。
サボっているついでに、最近HuluでGALACTICA見てるんですが、結構面白いですね!
まとめ
以上のソースは、yamitzky/cnet-scrapeにて公開しています。
まとめですが、クローリングのコツは
- 一旦保存してから解析する
- 解析結果はDBに保存する
- Resque的なものを使う
の3つを意識すると良いかなあーと思います。ちなみにopen-uriは少し不便なので、「いろんなデータソースから取得する場合」にはあまりオススメできません、ということも共有しておきます。また、ResqueはPythonとかからも使えるそうです。
なぜこんな記事を書いたのか
おまけですが、、、
スクレイピング/クローリング的な話は、結構タブー視されてしまっています。しかし、著作権法第47条の7「情報解析のための複製等」にて認められておりますから、もっと積極的にスクレイピングしましょう、的なことをお伝えしたい、ということです。
著作権法に基づいたスクレイピングが広まり、解析が広まり、よりデーターでドリブンになっていってほしいですし、皆様の研究が捗ると嬉しいな、と思います。
次回は著作権法第47条の7に基づいて翻案等をしたいと思います。しかし翻案や、それを元にした公開の、許されている範囲がよくわからないので、この辺りについて何かご存知のかたは教えていただけますと幸いです。
著作権法47条の7は、データ解析のためのダウンロードを認めています。その過程にあるような複製(例えば特徴量作るとか)も認められています。そして、解析の結果発生したものが著作物でなければ、公開しても良いと言われています。では、特徴量を公開するのは、「著作物でないから良い」ということができるのでしょうか。。。元に戻せないようなBag of Wordsは著作物に該当しないような気もします。そして事業者の損害はとても少ないはずなので、立法趣旨にも反さないような気がしますが。。。
著作権法むずかしいです
追記
sinatra-activerecordってgem使うとデータベースの処理がもっと単純に書けますよ。スキーマ管理もできるし https://github.com/janko-m/sinatra-activerecord/blob/master/README.md
とのことです。確かにActiveRecordのようなO/Rマッパーを使ったほうが(私の経験上)よく、でもRailsぐぬぬ・・・てなっていたので、本当に便利ですね。ありがとうございます。
Resque などを使って非同期に平行してガシガシ SQLite に INSERT UPDATE しまくるとあっさりデッドロックするので PostgreSQLか MariaDB 使ったほうがいいと思います。
本当にそうなんですよね。したがって、今回のように並行処理しなくても現実的な時間で終わる程度のページ数であることとか、そういったところがSQLiteを使う条件になってくると思います。
逆にSQLiteの良い所は、その保存先が単一のファイルにまとまることです。上記の例だと、cnet.dbにまとまります。私の場合、自分のVPSでクローリングして、ローカルのPCで解析して、やっぱり研究室のPCで解析して、、、とかやることがよくあるので、別のPCにDBを移すときに「単一のファイルをやりとりすれば良い」というのは非常に楽でした。
もちろん、MySQL(やMariaDB)をダンプしてやりとりすることもできるので、ファイルのやりとりができないということはありません。ただ、ダンプしたファイルのリストアにとても時間がかかったりしたことなどがあります(Wikipediaのダンプファイルとか)。この辺りそれほど詳しくないのですが、さくっと転送できたりするのでしょうか。。。
スクレイピングの場合はホントにrobots.txtに従わなくて良いの?
「スクレイピングの場合は」ではなく、「情報解析を目的とした場合は従わなくて良い」です。しかし、もし検索事業者として行う場合には、「収集を禁止する措置(robots.txtのこと)がとられた情報の収集を行わない」ということが求めらています(政令)。おそらく、robots.txtは「検索に伴う公開を望まないものの指定」として扱われているのだと思います*1。
経産省の資料にて紹介されています。参考:(【情報大航海プロジェクト・事業者向け解説書】 平成 21 年著作権法改正のポイント)http://www.meti.go.jp/policy/it_policy/daikoukai/igvp/index/h22_report/sub/06.pdf
でも利用規約で止められてるけどね。
利用規約がどれくらい効力を発揮するのかは難しいところです。これは、著作権の該当条文が「強行規定なのかどうか」という問題らしいのですが、これに関しての明確な事例や条文ってあるんでしょうか。
以下の記事が話題になっていたので、乗っかってPythonの話を書いてみたいと思います。
PythonとかScrapyとか使ってクローリングやスクレイピングするノウハウを公開してみる! - orangain flavor
言及してくださいました! Pythonでのスクレイピングです。こちらはHTMLの保存などはしてくれないようで。
という風に一長一短だったりするので、いろいろと周辺技術を探してみてくださいっ!