Python
wget
mitmproxy
クローラー
crawler

ブラウザで巡回しながら、特定の条件にマッチする URL の場合にダウンロード

More than 1 year has passed since last update.

はじめに

この記事は クローラー/Webスクレイピング Advent Calendar 2015 の 19日目です。

今回はブラウザを使っていろいろページを巡回しながら、特定の正規表現にマッチする URL の場合にだけダウンロードを行うようなスクリプトの書き方を紹介します。

使うのは mitmproxy です。

mitmproxy とは

mitmproxy とは Man In The Middle Proxy を表しています。
その名の通りブラウザとサーバの間にプロキシとして動作します。
ただし、単なるプロキシではなく、リクエストやレスポンスを検知して自動的な処理を実施したり、あるルールでリクエストやレスポンスの文字列を書きかえることも可能です。
mitmproxyPython で書かれているので、その上での処理も Python で記述する必要があります。

今回の記事でやりたいこと

この記事では下記の前提で自動的にコンテンツをダウンロードすることが目的です。

  • ダウンロードしたいコンテンツはブラウザを直接操作して選択、アクセスする
    • 逆にいうと、特定のリンクすべてとかではない。そうしちゃうと対象として多すぎる。
    • もしくはダウンロードしたいリンクの一覧を作成するのがカンタンではない。
    • たとえばログインが必要なサイトで、ログイン処理の記述などすべて自動化するのは面倒
  • 一回一回、ブラウザの機能でファイルとして保存するには多すぎる。
  • ファイル名の命名など、特定のルールで採番して、保存したい。
    • 手でやるには面倒
  • ダウンロード自体には wget を使う

作成したスクリプト

上記の目的で作成したのが次のスクリプトです。

download.py
#!/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 ヘッダはスキップさせることによって、非圧縮のファイルが保存されるようになるのでその手間を減らすことができます。