Hatena::ブログ(Diary)

カストリブログ

2009-12-07

Google Web履歴からWeb活動履歴をダウンロード(Cookieによる認証版)

| 02:25

はじめに

 以前公開した、Web活動履歴ダウンロードプログラムに不具合が発生しました。その原因は、Cookie認証を利用していないことでした。そこで、プログラムをCookie認証するように変更しました。


ソースコード


・注意事項

  1. 詳細な使い方は、「python getwebhistory.py -h」の出力を参照してください。
  2. htmlパーサーのlxmlというpythonのライブラリを導入する必要があります。

・12月7日以降の修正

  • 12月9日:履歴中にvideo resultなどが含まれるときにエラーになる不具合の修正

発生した問題

 以前に記載したWeb活動履歴(以下、単に履歴)のダウンロードプログラムが、気づいたら502エラーを吐いて動作しなくなっていたことに気付いた。


 よくよく調べてみたら、Linuxのwgetコマンドを使用した場合でも、下記のように502エラーと401エラーを吐いて、履歴のRSSが取得できなくなっていた。

wgetコマンドによる履歴の取得
$ wget --http-user=username --http-passwd=passwd 'https://www.google.com/history/lookup?hl=ja&output=rss'

--2009-12-08 00:57:16--  https://www.google.com/history/lookup?hl=ja&output=rss
www.google.com をDNSに問いあわせています... 66.249.89.99, 66.249.89.103, 66.249.89.104, ...
www.google.com|66.249.89.99|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 401 Unauthorized
www.google.com:443 への接続を再利用します。
HTTP による接続要求を送信しました、応答を待っています... 502 Bad Gateway
2009-12-08 00:57:17 エラー 502: Bad Gateway。

原因考察

 以前のダウンロードプログラムとwgetコマンドともに、履歴のRSSを取得できなくなった。しかし、Webブラウザ上では何事もなく履歴のRSSを閲覧することはできる。この2つの違いは、おそらく認証方法です。前者はCookieを利用せず、Didgest認証をしています。後者はCookieを利用した認証をしています。


以上のことから考えて、今回の不具合の原因は、Googleが主な認証方式として、Cookie認証を利用するためと考えました。


実は当初から、10月30日のダウンロードプログラムは、402エラーを吐くときがしばしば起こっていました。この原因は、基本的にはGoogleは認証方式としてCookie認証を利用しているものの、ごく一部のGoogleのサーバでは、Didgest認証も許可していたためと考えられます。


Cookie認証の利用

 履歴をGoogleから取得する前に、Cookie認証を行うように、ダウンロードプログラムを変更しました。

ソースコード


変更の味噌である箇所を解説します。

冒頭部分
import lxml.html import fromstring
import cookielib

まずは、htmlやxmlのパーサーのlxmlと、cookieを利用するためにcookielibをインポートします。cookielibはpythonの標準ライブラリですが、lxmlについては、別途導入が必要です。


Cookie認証部
def certifyByCookie(user, passwd):
    cj = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
    #プロキスを使用する場合
    #proxySupport = urllib2.ProxyHandler({'プロキシサーバのスキーム(httpなど)': 'プロキシサーバのURL'})
    #opener = urllib2.build_opener(proxySupport, urllib2.HTTPCookieProcessor(cj))
    
    #
    #認証時にPOSTするデータとCookieを取得
    #
    url = "https://www.google.com/accounts/ServiceLogin?hl=ja&nui=1&service=hist"
    req = urllib2.Request(url)
    req.add_header("User-Agent", "Mozilla/5.0")
    pagehandle = opener.open(req)
    doc = fromstring(pagehandle.read())
    #認証ページ中のinputタグの値を全て取得
    data = dict([(input.name, input.value) for input in doc.xpath('//input')])
    #ユーザIDとパスワードを設定
    data["Email"] = user 
    data["Passwd"] = passwd

    #
    #実際に認証
    #
    url = "https://www.google.com/accounts/ServiceLoginAuth?service=hist"
    #OSがWindowsであり、ここでエラーが出る場合、コメントアウト
    #data["signIn"] = u"ログイン".encode("sjis")
    req = urllib2.Request(url, urllib.urlencode(data))
    req.add_header("User-Agent", "Mozilla/5.0")
    pagehandle = opener.open(req)

    return opener

ここでは、Webブラウザの行うGoogle Web履歴で認証と同じ処理を行っています。

  1. 認証時にPOSTするデータ(data)とCookie(openerが内部的に保持)を取得
    1. 認証ページにアクセス
    2. 認証ページのhtmlをパースして、inputタグの値を取得
    3. ユーザIDとパスワードを設定
  2. 実際に認証
    1. httpリクエストを発行
    2. 認証に必要なCookie情報がopenerに内部的に保持される

後は、このcertifyByCookieが返すopenerを利用して、以前と同じようにGoogle Web履歴にRSS取得するためのHttpリクエストを発行することにより、不具合は修正なくなりました。


追記

wgetコマンドの方ですが、ブラウザの生成したcookieなどを利用すれば、Web履歴は取得できます。

nano0061nano0061 2010/04/01 08:26 こんばんは.

ずっと,Googleウェブ履歴をダウンロードできないかなーと思っていたので
非常に参考にさせていただいています.

質問させていただきたいのですが,私の環境では上手くプログラムが動作しません.具体的にはwgetコマンドを試すと「・・・失敗しました: Connection timed out.」と表示されてしまいます.

原因としては,私のネットワーク環境がProxy経由しているからだと思うのですが,Proxy経由で本プログラムを利用することは可能でしょうか?

もし可能であれば,どの辺りを修正すればよいかアドバイスいただけませんか?
よろしくお願い致します.

hippuhippu 2010/04/07 22:15 nano0061さん、こんにちは。

一週間ほどネットが使用できない環境にいたため、返信が遅れました。

ソースコードの298〜300行目にプロキシ環境下で使用する場合についてのコメントを書いておきました。
新しいソースコードは、この記事の上部のリンクからダウンロードできます。

ただし、現在、私はプロキシ環境下にいないので、動作確認はしていません。
なので、正直いって自信はありません。

nano0061nano0061 2010/04/08 00:19 hippuさん、こんばんは.

今、アップしていただいたソースコードをダウンロードして中身を眺めているところです.
成功/不成功に関しては、追って報告させていただきたいと思います.
また、分からない点があったら質問させてください.
対応していただき本当にありがとうございました!!

匿名希望匿名希望 2012/02/27 21:49 こんooは。これを見つけて実際に試してみましたが、うまく取れませんでした。

ソースそのまま
→urllib2.HTTPError: HTTP Error 401: Unauthorized

url = "https://accounts.google.com/ServiceLogin?hl=ja&continue=https://www.google.com/history/%3Fhl%3Dja&nui=1&authuser=0"
と2か所ソースのURLを変更してみる
→AttributeError: 'module' object has no attribute 'dumps'

url = "https://accounts.google.com/ServiceLoginAuth"
さらに…と2番目だけを変更する
→AttributeError: 'module' object has no attribute 'dumps'