Dubus本記事は、原著者の許諾のもとに翻訳・掲載しております。ウェブスクレイピングについては、pythonのディスカッションボードなどでもよく話題になっていますよね。いろいろなやり方があるのですが、これが最善という方法がないように思います。本格的なscrapyのようなフレームワークもあるし、mechanizeのように軽いライブラリもあります。自作もポピュラーですね。requestsやbeautifulsoup、またpyqueryなどを使えばうまくできるでしょう。してこんなに様々な方法があるかというと、そもそも「スクレイピング」が複数の問題解決をカバーしている総合技術だからなのです。数百ものページからデータを抽出するという行為と、ウェブのワークフローの自動化(フォームに入力してデータを引き出すといったもの)に、同じツールを使う必要はないわけですから。私は自作派で、それは融通が利くからですが、大量のデータを抽出する時に自作はふさわしくありません。requestsは同期でリクエストを行うので、大量のリクエストが行われると待ち時間が長くなってしまうからです。このブログ記事ではrequestsの代わりに、最新のasyncioライブラリをベースにした案を紹介しましょう。これで小さなスクレイパーを書いてみましたが、とても高速なものができました。やったかお見せしましょう。asyncioの基本asyncioは、python3.4で導入された非同期I/Oライブラリです。Python3.3のpypiからも入手できます。複雑なので詳細までは触れませんが、このライブラリを使って非同期コードを書くために必要な部分についてのみ説明します。詳しく知りたい人は、ドキュメントを読んでくださいね。簡単に言えば、知っておくべきことは2つ。コルーチンとイベントループです。コルーチンは関数に似ていますが、任意の箇所で一旦処理を中断したあと、処理を再開することができます。I/Oを待っている間(HTTPリクエストなど)コルーチンは一旦中断し、他の作業を実行させます。イベントループはコルーチンの実行を制御するために用います。Asyncioについて学ぶことはたくさんありますが、当面はこれで十分でしょう。読んだだけでは分かりづらいかもしれませんから、実際にコードを見てみましょう。aiohttpaiohttpは、asyncioと連携するために設計されたライブラリです。requestsのAPIに似ています。現状では、あまりいいドキュメントがないのですが、役に立つ事例がいくつかあります。基本的な使い方について説明しましょう。最初にコルーチンを定義してページを取得し、出力します。asyncio.coroutineを用いて、関数をデコレートしてコルーチンとします。aiohttp.requestはコルーチンの一種で、readメソッドでもあります。fromを使う必要がありますが、そういう注意点を除けば、コードはとても分かりやすいものです。fromを用いれば、1つのコルーチンから新たな別のコルーチンを発生させることもできます。同期コードからコルーチンを発生させるには、イベントループが必要です。asyncio.get_event_loop()から基準となるコルーチンを取得して、run_until_complete()メソッドを用いてそのコルーチンを実行させればよいのです。元のコルーチンを実行させるには、ただ次のように記述します。asyncio.get_event_loop()loop.run_until_complete(print_page('http://example.com'))asyncio.waitという便利な関数があります。いくつかのコルーチンをリストとして取り出し、リスト内すべてのコルーチンを含有するひとつのコルーチンとして返してくれます。print_page('http://example.com/bar')]))もうひとつ別の便利な関数としてはasyncio.as_completedがあります。こちらはコルーチンのリストを取り出し、処理が完了した順にコルーチンを再開するイテレータを返します。このイテレータを実行すると、それぞれの結果が出次第すぐに順次入手できるということです。スクレイピングさて、非同期HTTPリクエストのやり方が分かったところで、スクレイパーを書いてみましょうか。残っているのは、htmlを読み込む部分です。今回はbeautifulsoupを使ってみました。他の選択肢としてはpyqueryやlxmlなどがあります。例題として、パイレート·ベイで配布されているlinuxのソフトウエア群の中からトレントリンクを取得する小さいスクレイパーを書いてみましょう。requestsを処理するヘルパーコルーチンです。response.read_and_close(decode=True))解析部分。ついて掘り下げることではありませんから、シンプルにダンプ出力に留めておきます。ページの最初のmagnetリストを取得します。a['href']そしてコルーチンです。下記のurlについて、結果はシーダーの数でソートされます。リストの一番目が最もシードされているということになります。magnet))最後に、これらすべてをコールするコードはこのようになります。distros])loop.run_until_complete(f)まとめこれで非同期の小規模スクレイパーができあがりました。様々なページが同時にダウンロードできます。requestsを使った同じコードよりも3倍も速く処理することができました。これで読者のみなさんも、自分独自のスクレイパーを書くことができますね。このgistに、「おまけ」の分も含めた最終的なコードが掲載されています。慣れてきたら、asyncioについてのドキュメントや、aiohttpのexamplesなども見てみるといいですよ。asyncioでどんなことができるか、いろいろな例が記載されています。このアプローチの制約は(実際のところ、自作の場合すべてに当てはまるのですが)フォームを処理するためのスタンドアロンライブラリが見当たらない、という点です。いいヘルパー関数があって、簡単にフォームを送信できますが、その2つを使わない場合は自分で何とかしなくてはなりません。これは結構面倒なので、いつか自作でライブラリを書いてしまうかもしれません...(期待はしないでくださいね)。おまけ:サーバをいじめないでリクエストを一度に3つこなせるのはクールですが、5000となると話は別です。一度にあまりにも多いリクエストをしようとすると、やがて接続が切れてしまったり、そのウェブサイトにアクセスできなくなってしまったりするかもしれないからです。このような事態を避けるためにsemaphoreを使います。これは同期ツールで、ある時点で使われるコルーチンの数を制限するのに使います。ループの前にsemaphoreをクリエイトして、同時に最大いくつまでリクエストを処理するかを引数で渡してやればいいのです。asyncio.Semaphore(5)ここを入れ替えます。compress=True)機能は同じですが、semaphoreによって保護されています。compress=True)これで、最大でも同時に5つまでのリクエストしか処理されなくなりました。おまけ:プログレスバーもうひとつおまけです。tqdmはプログレスバーを生成してくれるステキなライブラリです。このコルーチンはasyncio.waitと同様の動きをしますが、コルーチンの処理完了を示すプログレスバーを表示してくれます。fツイート翻訳に対するフィードバックがございましたら、メールまたはGitHubのIssueよりお寄せください。Ltd.リクルートグループサイトへ。