Pythonのurllibでダウンロードしたファイルのタイムスタンプをサーバ上の最終更新日時に合わせる
wgetというCLIのダウンロードツールは、HTTPサーバの返す「Last-Modified:」ヘッダをもとに、ダウンロードしたファイルの最終更新日時を設定する。
しかし、大部分のダウンロードツールやWebブラウザなどでは、ダウンロードしたファイルのタイムスタンプは、ダウンロードした時点のものとなり、「urllibを使用してファイルをローカルに保存する」で書いたコードも同様となる。
Pythonのurllibを使用して、wgetのように「Last-Modified:」ヘッダによるタイムスタンプ設定を行うためには、
- HTTPヘッダから「Last-Modified:」ヘッダを探して解析
- 得られたタイムスタンプ情報をローカル時間としてファイルに適用
という操作が必要となり、「urllibを使用してファイルをローカルに保存する」のコードをもとにしてこれらの変更を行ったコードは下のようになる。
#! /usr/bin/python # -*- encoding: utf-8 -*- import urllib import time import sys import os class MyURLopener(urllib.FancyURLopener): version = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)" # User-Agent: def open_unknown(self, given_url, data): print u"URLではないため、処理できません: %s".encode("utf-8") % given_url sys.exit(1) url = "http://www.python.jp/pub/doc_jp/html-2.4.tar.bz2" #url = "hoge://www.python.jp/pub/doc_jp/html-2.4.tar.bz2" localfile = "/tmp/%s" % os.path.basename(url) opener = MyURLopener({}) try: (filename, headers) = opener.retrieve(url, localfile) except IOError: print u"取得に失敗しました".encode("utf-8") sys.exit(1) opener.cleanup() # キャッシュの削除 ## 最終更新日時をローカル上のファイルのタイムスタンプとしてセットする for l in str(headers).splitlines(): if "Last-Modified: " in l: try: ## Last-Modified: の値を解析してローカルのタイムスタンプを得る timestamp = time.mktime(time.strptime(l, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT")) - time.timezone except ValueError: print u"エラー: URL \"%s\" の最終更新日時の解析に失敗しました".encode("utf-8") % url sys.exit(1) try: os.utime(localfile, (timestamp, timestamp)) # 場所, (アクセス, 更新) except OSError: print u"エラー: ファイル \"%s\" のタイムスタンプ操作に失敗しました".encode("utf-8") % localfile sys.exit(1)
ダウンロードされるファイルは前回と同じくPythonのドキュメントの日本語訳(バージョン2.4のHTML版)で、/tmp/以下に保存される。
設定されるタイムスタンプ(最終アクセス日時と最終更新日時)は「2006/06/27 12:53」となり、wgetでダウンロードした場合と同じになる。
実際のコードにおける作業としては
- HTTPヘッダを文字列型にして行単位で処理し、「Last-Modified:」の部分をtime.strptime()関数で解析(記号の意味は下のリファレンスのURLを参照)
- ローカル時間のタイムスタンプを得る(time.mktime()関数に上の結果を渡したものからtime.timezoneを引く)
- os.utime()関数*1でタイムスタンプを変更
となる。
関連記事:
参考URL:
- Python ライブラリリファレンス: 6.11 time -- 時刻データへのアクセスと変換
- Python ライブラリリファレンス: 6.1.4 ファイルとディレクトリ - os.utime()を含む
*1:1番目の引数がファイルの場所、2番目の引数が(最終アクセス, 最終更新)のタプル