はじめに
この記事は クローラー/Webスクレイピング Advent Calendar 2015 の 19日目です。
今回はブラウザを使っていろいろページを巡回しながら、特定の正規表現にマッチする URL の場合にだけダウンロードを行うようなスクリプトの書き方を紹介します。
使うのは mitmproxy
です。
mitmproxy
とは
mitmproxy
とは Man In The Middle Proxy を表しています。
その名の通りブラウザとサーバの間にプロキシとして動作します。
ただし、単なるプロキシではなく、リクエストやレスポンスを検知して自動的な処理を実施したり、あるルールでリクエストやレスポンスの文字列を書きかえることも可能です。
mitmproxy
は Python
で書かれているので、その上での処理も Python
で記述する必要があります。
今回の記事でやりたいこと
この記事では下記の前提で自動的にコンテンツをダウンロードすることが目的です。
- ダウンロードしたいコンテンツはブラウザを直接操作して選択、アクセスする
- 逆にいうと、特定のリンクすべてとかではない。そうしちゃうと対象として多すぎる。
- もしくはダウンロードしたいリンクの一覧を作成するのがカンタンではない。
- たとえばログインが必要なサイトで、ログイン処理の記述などすべて自動化するのは面倒
- 一回一回、ブラウザの機能でファイルとして保存するには多すぎる。
- ファイル名の命名など、特定のルールで採番して、保存したい。
- 手でやるには面倒
- ダウンロード自体には wget を使う
作成したスクリプト
上記の目的で作成したのが次のスクリプトです。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib
import urlparse
import re
import sys
import datetime
import subprocess
def request(context, flow):
request = flow.request
if ".pdf" in request.path:
cmdline = "wget"
for k, v in request.headers.items():
if k == "User-Agent":
cmdline = cmdline + ( " --user-agent=\"%s\"" % ( v ) )
elif k == "Referer":
cmdline = cmdline + ( " --referer=\"%s\"" % ( v ) )
elif k == "Host":
pass
elif k == "Proxy-Connection":
pass
elif k == "Accept-Encoding":
pass
else:
cmdline = cmdline + ( " --header \"%s: %s\"" % ( k, v.replace(":", " ")))
url = request.url
cmdline = cmdline + " \"" + url + "\""
now = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d%H%M%S')
filename = now + "-" + os.path.basename(request.path)
cmdline = cmdline + " -O " + filename
cmdline = "( cd ~/pdf; " + cmdline + ")"
subprocess.call(cmdline)
上記のスクリプトを mitmproxy を使って下記のように実行します。
$ mitmproxy -s download.py
すると、mitmproxy
が 8080番ポートで HTTPプロキシとしての動作を開始します。
その後、手元のブラウザのプロキシの設定を localhost:8080 に変更すると、その後は自動的に mitmproxy
経由でアクセスが行われるようになります。
上記のスクリプトを動作させているので、.pdf
を含むパスにアクセスしたときは wget
を使ってダウンロードが行われます。
解説
上記のスクリプトでは、下記のことが実現できています。
- 実際のブラウザのユーザエージェントやリファラなどの HTTP リクエストヘッダを使って、ダウンロード処理を実施。
- ブラウザ側でログイン済みのクッキー情報がそのまま利用できている
- スクリプト上でログイン処理などを手で実装する必要がない
- 本当にやりたいダウンロード処理に関する記述だけで OK
- ファイル名で並び替えたときもダウンロードした順に並べたかったので、ダウンロードタイミングをファイル名の先頭につける
補足
wget
のコマンドラインオプションや HTTP リクエストヘッダに関する処理についてはほかの文献で調べてほしいですが、一点だけ Accept-Encoding
についてだけ解説しておきます。
この Accept-Encoding
をスキップさせる処理を記述しない場合、せっかくダウンロードしたコンテンツが gzip で圧縮された状態で、もうひと手間加えないと使える状態にならず、軽くハマってしまいました。このヘッダは通信量を減らすためにどのような圧縮を行うかを指定するものだからです。
Accept-Encoding
ヘッダはスキップさせることによって、非圧縮のファイルが保存されるようになるのでその手間を減らすことができます。