picasawebからの一括画像ダウンローダ

同じカテゴリーの画像検索、して一括ダウンロード、みたいなことは、いまどきはそんなに必要としないと思うわけだ。何も手許に置かなくても良いではないか、と。ただ、創造工学のような発想法の科学でいう「強制発想トリガー法」のひとつの「画像エクスカーション」のためには、「完全にランダムとはいえないがある程度バリエーションのある」画像が「収集済み」であると便利で。つまりは都度検索するのではなくて、「収集済み」とするにはダウンロードしておくのが手っ取り早い、と。

エクスカーションについては、

アイデア・スイッチ 次々と発想を生み出す装置

新品価格
¥1,404から
(2015/2/14 09:35時点)


などを参照してください。

また、画像・映像で憶える英語で書いた「Quotesで画像検索」してそれをスクリーンセーバにする、ための収集にも使えるだろう。

で、Python でこれをやろうと思った。何せ本気でこれやろうとすると、結構ダルい。ほんとは google の画像検索からの取り出しにトライしようかと思ったのだけれど、まったく近いことをしている人がいたので、真似てみた。まさしく他人のふんどしで相撲を取るとでもいうか。これ:

 1 # -*- coding: utf-8 -*-
 2 # 例えば創造工学の分野で「画像エキスカーション」なんて強制発想
 3 # トリガー法があって、そんなときに「動画検索 -> ダウンロード」を
 4 # 一気にやりたい場合がある。と思ったら Qiita でやってる人がいた。
 5 #
 6 #   http://qiita.com/kurehajime/items/8a700db4f462521f4d77
 7 #
 8 # 動かしてみると細かい問題もあるので、好みに合わせて書き換えて
 9 # みた。
10 import urllib
11 import os
12 import re
13 import logging
14 # ↓標準添付ライブラリではないのでインストールしてください
15 import feedparser
16 # ↓標準添付ライブラリではないのでインストールしてください
17 # (see http://diveintopython3-ja.rdy.jp/http-web-services.html)
18 import httplib2
19 
20 logger = logging.getLogger(__name__)
21 handler = logging.StreamHandler()
22 handler.setLevel(logging.DEBUG)
23 logger.setLevel(logging.DEBUG)
24 logger.addHandler(handler)
25 
26 def _retrieve(url, destdir):
27     # たぶん、.cache フォルダに画像を置き過ぎると何か問題起こす。
28     # (Windows では file not found になった。いるのに。)
29     http = httplib2.Http(".cache")
30     # 「%252520」って、これ、urlencode なの? お初である…
31     # じっくり観察すると、空白っぽいのが %252520 になっていて、
32     # これは普通なら %20。仕方ないので「%252520 -> %20」としてみるも、
33     # そうでないものもあるらしい。このエンコーディング、何者なんだ?
34     basename = re.sub(r'%2525([0-9A-F]{2})',
35                       lambda m: unichr(int(m.group(1), 16)),
36                       os.path.basename(url))
37     if not os.path.exists(destdir):
38         os.mkdir(destdir)
39     try:
40         # http.request(url) はキャッシュ済みならお外にはゆかぬ。
41         open(os.path.join(destdir, basename), "wb").write(
42             http.request(url)[1])
43         logger.info(u'%08d: download: %s, %s', os.getpid(), url, basename)
44     except Exception as e:
45         logger.error(e)
46 
47 def dl_from_picasaweb(query, count=10):
48     # feedparserはRSS解析のOSS。いい時代になったものだ。よく自力でパース
49     # したもんだわ。
50     baseurl = "https://picasaweb.google.com/data/feed/base/all"
51     feed = feedparser.parse(
52         "{}?q={}&max-results={}".format(
53             baseurl, urllib.quote(query.encode("utf-8")), count))
54     destdir = os.path.join(os.path.dirname(__file__), "_dest")
55     if not os.path.exists(destdir):
56         os.mkdir(destdir)
57     destdir = os.path.join(destdir, query)
58     for entry in feed['entries']:
59         url = entry.content[0].src
60         _retrieve(url, destdir)
61 
62 if __name__ == "__main__":
63     # 標準入力から検索文字列入れてください。日本語OK。
64     # (コマンドライン引数にしてないのは MSYS の bash が日本語
65     # 受け付けないから。)
66     import sys
67     import time
68     query = sys.stdin.readline().strip().decode(sys.stdin.encoding)
69     t = time.time()
70     dl_from_picasaweb(query, 10 if len(sys.argv) == 1 else int(sys.argv[1]))
71     logger.info("done. [%.f sec]", time.time() - t)

なお、httplib2 については、コードのコメントに書いた通りで、

Dive Into Python 3 – 14章 HTTPウェブサービス

を参照。というより必読でしょうね、これ。つられて調べてよかった、このサイト。