Heroku で bash を使う&Python の urllib.urlretrieve() を使う (ファイルのダウンロード)

Heroku で Python を使ってファイルのダウンロードを実現する際の動作や注意点をまとめた。また動作を確認するにあたって、heroku の実行環境がどのような構成となっているか、bash を使って調べてみた。

なお Python でファイルをダウンロードする際は urllib モジュールの urlretrieve() 関数を使用することを想定する。また、手元の環境は heroku コマンドが利用可能な状態であることを前提とする (環境準備はこちらを参照)。

環境情報

  • Heroku (Ubuntu 14.04.1 LTS)
  • Python 2.7.9

Heroku 上 の bash を起動

手元の環境にてターミナルを起動し、以下のコマンドを実行する。bash のプロンプト「~ $ (heroku 実行ユーザのホームディレクトリにいる状態)」が表示されたら OK。

$ heroku run bash
Running `bash` attached to terminal... up, run.<PID>
~ $

この bash 実行環境がどのような設定/構成なのか確認してみる。

~ $ bash --version
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.1 LTS
Release: 14.04
Codename: trusty

~ $ pwd
/app

~ $ whoami
u<ランダムな数字5桁>

Heroku のアプリケーション実行環境が Ubuntu 14.04 で動作していることが分かる。

Heroku 上で Python インタプリタ (Interactive console) を起動する

引き続き、Heroku 上の bash で以下のコマンドを実行する。Python インタプリタのプロンプトである「>>>」が出力されたら OK。

~ $ python
Python 2.7.9 (default, Dec 11 2014, 17:18:51) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

引き続き、このインタプリタのカレントディレクトリを確認する。Python の os モジュールを import し、os.system() 関数の引数にコマンドを指定すると、Heroku の実行環境 (ここでは Ubuntu) にコマンドを渡すことができる。

>>> import os
>>> os.system('pwd')
/app
0
>>>

Heroku 上の Python でファイルのダウンロードを実行する

Heroku 上の Python インタプリタにて、python.org のトップページをダウンロードしてみる。

>>> import urllib
>>> url = 'http://python.org/'
>>> name, headers = urllib.urlretrieve(url)
>>> 
>>> print name
/tmp/tmp<ランダムな文字列>
>>> 
>>> print headers
Date: <ダウンロードした日時>
Server: nginx
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 46952
Accept-Ranges: bytes
Via: 1.1 varnish
Age: 3001
X-Served-By: cache-atl6235-ATL
X-Cache: HIT
X-Cache-Hits: 5
Vary: Cookie
Public-Key-Pins: (省略)
Strict-Transport-Security: max-age=63072000; includeSubDomains
Connection: close

>>> os.system('ls -alrt /tmp/')
total 56
drwxr-xr-x 15 root root 4096 Jan 14 09:04 ..
-rw------- 1 u<ランダムな数字5桁> <ランダムな数字5桁> 46952 Feb 21 14:24 tmp<ランダムな文字列>
drwx------ 2 u<ランダムな数字5桁> <ランダムな数字5桁> 4096 Feb 21 14:24 .
0
>>>

上記の通り、/tmp 配下に「tmp」から始まるランダムな文字列のファイル名でダウンロードできた。が、インタプリタを終了してみると・・・

>>> exit()
~ $ ls -alrt /tmp/
total 8
drwxr-xr-x 15 root root 4096 Jan 14 09:04 ..
drwx------ 2 u<ランダムな数字5桁> <ランダムな数字5桁> 4096 Feb 21 14:32 .
~ $

先ほど見えていたファイルが存在しない。Python のインタプリタを終了する際に、事後処理で消えた (消された) 可能性が考えられる。

対策

以下の通り、urlretrieve() 関数の第2引数にダウンロード時のファイル名を指定することでファイルが残ることを確認出来た。

~ $ python
Python 2.7.9 (default, Dec 11 2014, 17:18:51) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> import os
>>> import urllib
>>> url = 'http://python.org/'
>>> file = '/tmp/index.html'
>>> name, headers = urllib.urlretrieve(url, file)
>>> print name
/tmp/index.html
>>> 
>>> os.system('ls -alrt /tmp/')
total 56
drwxr-xr-x 15 root root 4096 Jan 14 09:04 ..
-rw------- 1 u<ランダムな数字5桁> <ランダムな数字5桁> 46952 Feb 21 16:25 index.html
drwx------ 2 u<ランダムな数字5桁> <ランダムな数字5桁> 4096 Feb 21 16:25 .
0
>>> exit()
~ $ 
~ $ ls -alrt /tmp/
total 56
drwxr-xr-x 15 root root 4096 Jan 14 09:04 ..
-rw------- 1 u<ランダムな数字5桁> <ランダムな数字5桁> 46952 Feb 21 16:25 index.html
drwx------ 2 u<ランダムな数字5桁> <ランダムな数字5桁> 4096 Feb 21 16:25 .
~ $

ただし、上記でダウンロードしたファイルも、いったん heroku 上の bash から exit するとファイルが削除されてしまうため、あくまで一時的な保存先として利用することとし、永続化する場合は DB やストレージサービスへ配置するといった恒久対策が必要と考える。

yktmnb について

IT土方兼社畜を生業としています。 入社以来 Java を用いた社内向け Web アプリの開発に携わっていました。 最近は IaaS 関連の仕事をしています。 個人的に Android アプリ開発をしたり、ゆるべんという勉強会 (http://wooven.org/) をのんびりとやってます。
カテゴリー: Heroku, Python パーマリンク