前回の準備編に続いて、今回はコーディングに移りますが、まず最初に完成形となるコードを掲載します。
ここからはこのコードを目指して、どういった手順で書いていくかを説明します。
Contents
Python でプログラムを書いてみる
エディタを使って、適当にプログラムを書きます。以下は、“Survival from the Colorado University.” という文字列を出力するだけのプログラムです。
1 | print "Survival from the Colorado University." |
これを “colorado.py” などと名前を付けて保存しましょう。“py” は Python プログラムの拡張子です。
ターミナルで “colorado.py” を保存したフォルダまで cd
で移動し、以下のように python
コマンドを用いてプログラムを実行します。
$ python colorado.py
Survival from the Colorado University.
実行すると文字列が出力されました。
日本語を使うときは注意が必要です。プログラムの1行目か2行目に文字コードを指定する必要があります。またファイル自体も同じ文字コードで保存してある必要があります。ここでは、便利がよい “UTF-8” を使います。
$ pydoc -w mechanize wrote mechanize.html $ open mechanize.html
これで HTML ファイルのドキュメントが生成されます。
ちょっと見にくいので、
$ pip install epydoc ... $ epydoc --html mechanize ... $ open html/index.html
という画期的な方法もあります。
mechanize で Web サイトにアクセスする
“colorado.py” を以下のように書き換えて、実行します。
1 2 3 4 5 6 7 8 | # coding: UTF-8 import mechanize browser = mechanize.Browser() # Create an instance of Browser class. browser.open("http://www.google.com") # Open Google with open() method. print browser.title() # Print title of web page with title() method. |
$ python colorado.py
Google
となります。3行目で mechanize
モジュールをインポートして、5行目で、mechanize
モジュールから Browser
クラスのインスタンスを生成しています。クラスとかインスタンスというのは、オブジェクト指向プログラミングの考え方からくる概念で、これを説明すると本が数冊書けるので、ここではごく手短に、いろいろなものをオブジェクトとして扱っていて、クラスオブジェクトっていうオブジェクトの設計図から、インスタンスオブジェクトっていう実際に使われる製品を生産する感じです。ということで、いま browser
変数に、Browser
クラスのインスタンスが入っていて、これを使って色々やります!
6行目では、browser
の open()
メソッド(クラスに内包された関数のこと)に Google の URL 文字列を引数として渡しつつ呼び出し、要するに Google 開け、ってことです。
8行目では、browser
の title()
メソッドを呼び出し、これは Web ページのタイトルを返すので、それを print
しています。
ここまでで、とにかく Web ページを開けるようになりました。
自動化したい流れの確認
では、どういった処理を自動化すれば、生き残ることができるのでしょう。“Global Near-Real-Time SSH Anomaly/Ocean Color Data Viewer” のページを開いて、衛星画像を得るところまで、人力で進めてみます。
まずブラウザでページを開きます。いろいろとオプションがありますが、ターゲットとなる日付や緯度経度、“Image Source” には7日間の合成、コンターは両方をオンに、これらのフォームを埋めて “Submit values” をクリックすると、ページが遷移して、表示された画像がリンクになっているからそれをクリックすると、GIF 形式の画像が開ける。これを保存する。
これを自動化すれば、生き残れる。
Web ページのフォームを埋める
colorado.py を次のように書き換えて実行してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # coding: UTF-8 import mechanize browser = mechanize.Browser() browser.set_handle_robots(False) response = browser.open("http://argo.colorado.edu/~realtime/modis/") print "\n=== HTML Source ===" print response.read() print "\n=== Fomrs ===" for form in browser.forms(): print form |
6行目で、browser
に set_handle_robots()
メソッドで、“robots.txt” に従わないように設定しています。これは、コンピュータによる自動収集プログラムのことをロボットということがあり、この自動収集に対するルールを Web サイト側が設定できます。これを True
にするとこの Web サイトを開けませんし、今回は継続的に自動収集するわけではなく、一時的に必要なものを得るだけですから、False
に設定します。このように、mechanize
には実際のブラウザに偽装するための仕組みがいくつかあり、これを設定しないとアクセスできないケースがあります。
8行目で今回利用するサービスの URL (http://argo.colorado.edu/~realtime/modis/) を開き、またこのメソッドは response_seek_wrapper
クラスのインスタンスを返すので、それを response
変数に入れています。
11行目で response
の read()
メソッドを呼び出し、HTML のソースを出力しています。
14から15行目では for
ループの構文で、browser.forms()
は、browser
が現在開いているページにある、HTML の form
要素を抜き出し、HTMLForm
オブジェクトのイテレータのかたちで返します。イテレータというのは繰り返し処理の手がかりとなるもので、Python ではこのイテレータを手がかりに、for ... in ...
で簡単に走査できます。この場合では、個々のループ処理の中で form
変数に実際の HTMLForm
クラスのインスタンスが入っています。16行目では実際に form
を出力しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | === HTML Source === ... === Fomrs === <POST http://argo.colorado.edu/cgi-realtime/modis/real-timeVr1.pl application/x-www-form-urlencoded <SelectControl(month=[01, 02, 03, *04, 05, 06, 07, 08, 09, 10, 11, 12])> <SelectControl(day1=[0, *1, 2, 3])> <SelectControl(day2=[0, 1, 2, 3, *4, 5, 6, 7, 8, 9])> <SelectControl(year=[93, 94, 95, 96, 97, 98, 99, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, *11])> <TextControl(alon0=262)> <TextControl(alon1=280)> <TextControl(alat0=18)> <TextControl(alat1=31)> <RadioControl(day_opt=[8, 7, 5, 3, *1])> <RadioControl(cont_opt=[T, *F])> <RadioControl(cont_anot=[T, *F])> <RadioControl(degree_format=[0, 1, 2, *3])> <SelectControl(palette=[*0, 1])> <TextControl(sshcont=5)> <TextControl(color_min=1)> <TextControl(color_max=5)> <TextControl(color_scale=0.01)> <RadioControl(page_orientation=[*P, L])> <RadioControl(plot_format=[*G, P])> <SubmitControl(<None>=Submit Values) (readonly)> <IgnoreControl(<None>=<None>)>> |
出力結果を確認すると、HTML の form
要素が一つあり、それが print form
で整形されて出力されていることが分かります。この出力結果を頼りにコードを書いていきます。
画像をダウンロードする
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # coding: UTF-8 import mechanize import urllib browser = mechanize.Browser() browser.set_handle_robots(False) # Open web page. response = browser.open("http://argo.colorado.edu/~realtime/modis/") # print "=== HTML Source ===" # print response.read() # Check forms. print "\n=== Fomrs ===" for form in browser.forms(): print form # Select first form. browser.select_form(nr=0) # Set day. browser["month"] = ["01"] browser["day1"] = ["0"] browser["day2"] = ["1"] browser["year"] = ["05"] # Set location. browser["alon0"] = "135" browser["alon1"] = "160" browser["alat0"] = "30" browser["alat1"] = "45" # Set options. browser["day_opt"] = ["7"] browser["cont_opt"] = ["T"] browser["cont_anot"] = ["T"] # Check forms. print "\n=== Forms ===" for form in browser.forms(): print form # Submit. response = browser.submit() # print "=== HTML Source ===" # print response.read() # Check links. print "\n=== Links ===" for link in browser.links(): print link if link.url[-4:] == ".gif": print link.url urllib.urlretrieve(link.url, "image.gif") |
画像をダウンロードする部分までのコードを載せました。
4行目で、あとで画像をダウンロードする際に用いる、Python に内蔵の urllib
モジュールを読み込んでいます。
21行目では、browser
の select_forms()
メソッドを呼び出し、第0番目の form
にフォーカスするようにしています。
24行目から36行目では、実際に値を設定しています。先ほどの出力結果と見比べてみてください。出力結果では、<コントロール名(名前=値)>
というかたちで、それぞれの項目が表されています。また SelectControl
や RadioControl
で *
がついているものは、その時点で選択されているものです。browser["名前"]
で一つの項目にアクセスでき、SelectControl
や RadioControl
には ["値"]
のかたちで、TextControl
には "値"
のかたちで、値を代入していきます。
39行目から41行目で、正しい値が設定されているか確認しています。
44行目の部分で、browser
の submit()
メソッドを呼んで、“submit” ボタンを押すのと同じことを行っています。自動的に次のページへ移行します。
51行目で、browser
の links()
から、そのページに存在するリンクのイテレータを得て、for ... in ...
文で全てチェックするようにしています。
53行目で、link
インスタンスの url
というインスタンス変数に link.url
でアクセスし、これはそのリンクの URL を文字列で保持しているので、Python のスライス機能で最後の4文字を取得し、それが “.gif” であるときそれが GIF 画像であると判断し、if
文で条件分岐する、というコードです。スライス機能というのは、文字列や配列などの一部を切り出す機能です。[始めのインデックス:終わりのインデックス]
のかたちで、切り出す最初の部分と最後の部分を指定します。またこの値を空にすると、自動的に一番最初と一番最後を指定され、また負の値を渡すと、最後から数えてのインデックスになります。従って [-4:]
は、最後から数えて4番目から、一番最後までの4文字を切り出すことになります。==
は比較演算子です。
55行目は、urllib
モジュールの urlretrieve()
関数を使って、実際に画像をダウンロードしています。1つ目の引数 link.url
は、ダウンロード元の URL で、2つめの引数 "image.gif"
はダウンロード先のファイル名です。2つめの引数は、絶対パスまたは、実行する Python ファイルからの相対パスになります。
実行して、Python ファイルのあるフォルダを覗くと、“image.gif” という画像がダウンロードされているはずです。
日付を変えていく
ここまでで、1回分の画像を取得するところまでは自動化できました。しかしこれだけでは意味がありませんから、日付を次々変更していくように書き換えます。
1 |
import os
import datetime
import mechanize
import urllib
# Set directory name.
directory_name = “images”
if not os.path.isdir(directory_name):
os.mkdir(directory_name)
# Set dates.
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2010, 12, 31)
current_date = start_date
while current_date <= end_date:
print current_date
browser = mechanize.Browser()
browser.set_handle_robots(False)
# Open web page.
response = browser.open("http://argo.colorado.edu/~realtime/modis/")
# print "=== HTML Source ==="
# print response.read()
# Check forms.
# print "\n=== Fomrs ==="
# for form in browser.forms():
# print form
# Select first form.
browser.select_form(nr=0)
# Set day.
browser["month"] = [current_date.strftime("%m")]
browser["day1"] = [current_date.strftime("%d")[0]]
browser["day2"] = [current_date.strftime("%d")[1]]
browser["year"] = [current_date.strftime("%y")]
# Set location.
browser["alon0"] = "135"
browser["alon1"] = "160"
browser["alat0"] = "30"
browser["alat1"] = "45"
# Set options.
browser["day_opt"] = ["7"]
browser["cont_opt"] = ["T"]
browser["cont_anot"] = ["T"]
# Check forms.
# print "\n=== Forms ==="
# for form in browser.forms():
# print form
# Submit.
response = browser.submit()
# print "=== HTML Source ==="
# print response.read()
# Check links.
# print "\n=== Links ==="
for link in browser.links():
# print link
if link.url[-4:] == ".gif":
print link.url
urllib.urlretrieve(link.url, directory_name + "/" + current_date.isoformat() + ".gif")
current_date += datetime.timedelta(7)
print "Ended."[/pyg]
3,4行目では、あとでフォルダを作るための os
モジュールと、日付を扱うための datetime
モジュールを読み込んでいます。どちらも Python 組み込みのモジュールです。
9から12行目では、画像を保存していくフォルダの名前を設定して、11行目でその存在を確認し、なければ12行目で作るといった感じです。os
モジュールのサブモジュール path
から、isdir()
関数を呼び出し、directory_name
変数に代入された “images” という名前のフォルダがあれば True
、なければ False
を返します。if
文を使いますが、先ほどの真偽値を逆にしたいので、not
を付けています。12行目の mkdir()
関数は、フォルダを作るだけです。
15,16行目では、最初と最後の日付をそれぞれ変数に代入しています。このとき日付をプログラムで扱うために、date
クラスのインスタンスを作ります。datetime
モジュールの date(year, month, day)
で簡単に作成できます。
18行目で今後実際に使う current_date
変数に最初の日付を代入しています。
20行目の while
文では、続く条件式が真の値を返す間、ループしてずっと処理を続けます。この場合では、current_date
が end_date
以前の間、処理を続けることになります。
22行目では実行中に進捗状況を確認するため、current_date
を出力しています。
42行目から45行目では、current_date
を元にして、フォームに日付を設定しています。date
クラスの strftime()
メソッドでは、その日付を元に引数で指定された形式の文字列を返します。形式を指定する際のパラメータはドキュメントに掲載されています。43行目と44行目では、スライスと同じように文字列のインデックスを指定して1文字だけ取り出すようにしています。
73行目では保存する先のパスとして、+
演算子による文字列の連結を使って、directory_name
のフォルダ下に、current_date
の isoformat()
メソッドで得られる 2005-01-01
といった文字列をファイル名として、拡張子を付けています。
75行目では current_date
に deltatime()
で作成できる、日付の差分オブジェクトを足すことで、7日進んだ日付の新しい date
クラスのインスタンスを代入しています。これで1サイクルごとに7日進めることができます。
これを実行すると、指定したフォルダに衛星画像がどんどん保存されていきます。
これで完成です。
注意するべきこと
サーバの負荷を考えましょう。スクリプトを動かして過度な負担がかかっているようなら、1サイクル毎に10秒間を空けるようにするなど、工夫する必要があります。
ステップアップのために
Python をもっと学びましょう。とりあえずオンラインドキュメントを一通り読むと良いです。
オブジェクト指向を学びましょう。オンラインにも資料がありますから、最低限のことを知っていると便利です。
サバイブ!
私たちは、いま高度情報化社会に生きています。誰もが情報機器を持っています。しかしながら、パーソナルコンピュータを始めこういった情報機器たちは、お世辞にもユーザーフレンドリーとは言い難く、使いこなすのは至難の業です。そんな中、ここではプログラミングでコンピュータに自動処理させるという、ただ使うよりずっと難しいことを紹介しました。
昔、誰もがプログラミングをするような未来がくると、ビル・ゲイツは考えていました。しかし、実際に訪れた未来では、もちろんそんなことにはなっていません。それでも筆者は考えます。これくらいのこと、数学よりずっと簡単じゃないか。高等教育を受けている読者の皆さんが、ちょっとしたコードを書けないはずはありません。
生き残らなければ。コンピュータに振り回されるのではなく、コンピュータを働かせなければ。サバイバルコンピューティングで明日を勝ち取りましょう。
@cockscomb プログラマ的には正しくても会社員的には失格!
— もっさりさんさん (@TeamMOSA2) 4月 22, 2011
サバイブ!
ピンバック: コロラド大学からのサバイブ — 準備編 | cockscomb.info