Pythonでwebスクレイピングをする時に必要だった知識
Pythonでスクレイピングする仕事が降ってきました
半月前ぐらいに、 Pythonでwebサイトをスクレイピングする仕事が降ってきました。
自分が始めるならHaskellやScala、他人が読み書き出来ることを要求されてもTypeScriptあたりで書くのですが、まあ既存のスクレイピングコードがPythonでそれに追加することを要求されたので仕方がありません。
と言うわけでちょっと書いていました。仕事があるたびに即座に(その日以内あたり)終わらせていた気がするので、あまり本格的にはやっていませんが。
必要だったライブラリを書いていきます。
ルールを守って楽しくスクレイピング!
requests
Requests: HTTP for Humans™ — Requests 2.24.0 documentation
これは自分で探してきたと言うより元々使われていたものですね。
色々調べましたが最終的にはallow_redirects=False
をget
メソッドに設定して不意のリダイレクトを防ぐぐらいしか使いませんでしたね。
mypyはtypeshedに登録してある型定義を自動的に認識すると見ましたが、全然読み込まれません。どうなってるんですか。
bs4
これも前から使われていたものですね。
CSSクエリでselect
出来るのは良いのですが、
lxmlだとパースできないHTMLが存在してhtml5lib
を使う必要があったり、
text
プロパティにHTMLソースコード中の無視されるべきスペースが入っていたりでちょっとつらいです。
get_text
にstrip
を付けると方法は改行含めて消してしまうのでダメです。
stripped_strings
もちゃんと消していないのでダメです。
とりあえず今回の要件では本当に行頭にスペース入れてる場合は考慮しなくても良かったのでサクッと正規表現で加工しましたが。
bs4の対応パーサはバギーなHTMLに対応してなかったりテキスト取得がつらいので厳しい。
やはりwebのスクレイピングを真面目にやるなら動的コンテンツを含んでいなくてもHeadless Chrome/Firefoxなのですかね、でもHeadlessブラウザ立ち上げるのは流石に重たいので、今後jsdomなどはちゃんとしているのか調査したいです。
retry
主に
があるみたいですがまあどちらでも良いのではないですか。私はretryを選びましたが。
サーバの問題なのかクライアントの問題なのか時折コネクション生成に失敗するのでその時は時間をかけて3回ぐらいは再試行したい所です。
最初は自前で再帰していましたが、
Pythonの変数スコープは型チェック時にあまりちゃんとチェックしてくれないので、
expect
部で例外を起こしてしまったりしてつらい。
スクレイピングで何か間違えた時はraise ValueError(url)
のようにURLを情報に含めると便利。
concurrent.futures.ProcessPoolExecutor
concurrent.futures -- 並列タスク実行 — Python 3.9.0 ドキュメント
データを読み込んでパースして結果を出しますが、パースしている時にデータを読み込んでいないのは無駄ですね。
並列処理で解決です。
やる前に相手がsitemapを公開しているかとかrobots.txtに拒否設定書いてないかとか確認しましょう。いや並列じゃなくても確認はするべきですが。
ThreadPoolExecutor
とか
asyncio
とかはDOMの解析は速くしなさそうなので微妙に思えます。
asyncioの記述めっちゃ面倒に見えますし…
実際はワーカープロセスへの変数の受け渡しのオーバーヘッドで遅くなる可能性もありそうですね。将来的にDOMの処理量が増えていった場合優位なのはProcessPoolExecutor
でしょうけど。
サイトマップを見て収集する対象のURLの配列を確保したら、
map
に突っ込んであげましょう。
tqdm
tqdm/tqdm: A Fast, Extensible Progress Bar for Python and CLI
量が多いと本当に進んでるのか不安になるのでプログレスバーは欲しいですね。
マルチプロセスに処理を投げてもexecutor.map
は普通にイテレータを返すようなので、
total=len(urls)
のように設定しておけば動きます。
random.sample
random --- 擬似乱数を生成する — Python 3.9.0 ドキュメント
たくさん収集した後処理コードが思いっきり間違ってるとかだったら悲惨なので、最初に100件ぐらいURLを限定して走らせた方が良いでしょう。
総論
やっぱりPythonはwebスクレイピングに不向きな言語だと思います。たくさん収集した後にランタイムエラーが出たら悲惨ですし、 GILがあるから並列処理も気軽に書けません。
可能なら他の言語を使いたいですね。