はじめに
Flickrに投稿する人のカメラの設定を分析できるように、Flickr APIを使って画像検索、EXIF取得できるPythonプログラムを作ってみた。
FlickrAPIの導入についてはいろいろなサイトで紹介があるので割愛するが、例えばこちらを参照。
pythonプラグラム
参考文献
以下の情報を参考にして、プログラムを作成した。
- https://qiita.com/Saayaman/items/a3066697a108a7e7fc39
- http://bty.sakura.ne.jp/wp/archives/1196
- https://stackoverflow.com/questions/43437364/converting-json-to-csv-using-python-pandas
上記に対してオリジナルなところはEXIFを取得することで、jsonデータの平滑化をすることでCSVファイルに所望の状態でEXIF情報を書きだすことができた。
作成ポイント
ライブラリ
以下のライブラリをimportして設計した。
Flickr APIへのアクセスはrequestsを使用する。
auth key情報
Flickr APIを使えるようにすると自分のkeyが発行されるので、それをauth_flickr.py
に記載する。
flickr_key = "1234567890abcdefghijklmnopqrstuv"
メインプログラム側では、上記をimportする。
from auth_flickr import(
flickr_key
)
Flickr画像の検索
画像検索用のペイロードをjson形式で作成し、Flickr APIのflickr.photos.searchを使うリクエストを投げて、画像検索する。
#画像検索用のjsonデータ
payload = {
'method': 'flickr.photos.search',
'api_key': flickr_key,
'text': 'mountain moon',
#'extras': 'date_upload',
#'has_geo': 1,
#'place_id' : '36i2ieFQU7uqDINu',
'per_page': '10',
'format': 'json',
'nojsoncallback': '1'
}
#画像検索
r = requests.get(url, params=payload)
resp = r.json()
#print (json.dumps(resp, sort_keys=True, indent=2))
'text'には、検索したいキーワードを記載する。AND条件は半角スペースで併記すればよいようだが、やや検索結果が所望とは外れてくるようだ。
'text'は日本語もOKだが、(ほぼ)日本人の撮影した写真に限定される。。。
'per_page'で指定した数だけ検索する。この例では10個検索している。
'place_id'を定義すると、写真の撮影された場所を限定できる。'place_id'は、こちらで検索できる。これがなかなか見つけられず苦労した。コメントのものはyokohamaの'place_id'。
画像検索結果の取得とEXIF情報の取得
表示用URLの雛型記述をPhoto Source URLsに従ってtpl_urlに準備する。
#表示用URLの雛型
tpl_url = 'https://farm%s.staticflickr.com/%s/%s_%s.jpg'
以下はfor文で一気に記載している。
検索結果はjson形式でrespに格納されているので、検索結果の情報が格納されているresp['photos']['photo']を順次呼び出して、表示用URLを生成、画像取得する。
次に、EXIF取得用のペイロードをjson形式で作成し、Flickr APIのflickr.photos.getExifを使うリクエストを投げて、結果をresp2に格納する。
EXIFが取得できない画像もあるので、リクエスト結果をチェックしてresp2['stat'] == 'fail'であれば、EXIFをCSV化する処理をスキップする。
EXIFが取得出来たらpandasでjsonデータを平たん化して、csvに書き出す。これをやらずにpd.DataFrame(resp2['photo']['exif'])としてしまうと、EXIF情報が1つのセル情報にまとまって書き出されてしまうので、注意。
#検索結果画像の情報処理
count = 1
for i in resp['photos']['photo']:
#表示用URLの生成
img_url = tpl_url % (i['farm'],i['server'],i['id'],i['secret'])
print ("#%04d" % count, img_url)
#画像取得
r = requests.get(img_url)
fname = "%04d.jpg" % count
f = open(fname, 'wb')
f.write(r.content)
f.close()
#EXIF取得用のjsonデータ
payload2 = {
'method': 'flickr.photos.getExif',
'api_key': flickr_key,
'photo_id' : i['id'],
'format': 'json',
'nojsoncallback': '1'
}
#EXIF取得
r2 = requests.get(url, params=payload2)
resp2 = r2.json()
#print (json.dumps(resp2, sort_keys=True, indent=2))
#EXIFがない場合はスキップする
if resp2['stat'] == 'fail' :
count += 1
continue
#pandasでjsonデータを平たん化してcsvに書き出す
df = pd.read_json(json.dumps(resp2, sort_keys=True, indent=2))
#exif = pd.DataFrame(resp2['photo']['exif'])
exif = pd.io.json.json_normalize(resp2['photo']['exif'])
exif.to_csv("%04d.csv" % count)
count += 1
作成結果
メインプログラム全体は以下。関数化すればよい部分もあるが、実験用の初版なのでこれで良しとした。
# -*- coding: utf-8 -*-
import json
import requests
import pandas as pd
url = 'https://api.flickr.com/services/rest/'
from auth_flickr import(
flickr_key
)
#画像検索用のjsonデータ
payload = {
'method': 'flickr.photos.search',
'api_key': flickr_key,
'text': 'mountain moon',
#'extras': 'date_upload',
#'has_geo': 1,
#'place_id' : '36i2ieFQU7uqDINu',
'per_page': '10',
'format': 'json',
'nojsoncallback': '1'
}
#画像検索
r = requests.get(url, params=payload)
resp = r.json()
#print (json.dumps(resp, sort_keys=True, indent=2))
#表示用URLの雛型
tpl_url = 'https://farm%s.staticflickr.com/%s/%s_%s.jpg'
#検索結果画像の情報処理
count = 1
for i in resp['photos']['photo']:
#表示用URLの生成
img_url = tpl_url % (i['farm'],i['server'],i['id'],i['secret'])
print ("#%04d" % count, img_url)
#画像取得
r = requests.get(img_url)
fname = "%04d.jpg" % count
f = open(fname, 'wb')
f.write(r.content)
f.close()
#EXIF取得用のjsonデータ
payload2 = {
'method': 'flickr.photos.getExif',
'api_key': flickr_key,
'photo_id' : i['id'],
'format': 'json',
'nojsoncallback': '1'
}
#EXIF取得
r2 = requests.get(url, params=payload2)
resp2 = r2.json()
#print (json.dumps(resp2, sort_keys=True, indent=2))
#EXIFがない場合はスキップする
if resp2['stat'] == 'fail' :
count += 1
continue
#pandasでjsonデータを平たん化してcsvに書き出す
df = pd.read_json(json.dumps(resp2, sort_keys=True, indent=2))
#exif = pd.DataFrame(resp2['photo']['exif'])
exif = pd.io.json.json_normalize(resp2['photo']['exif'])
exif.to_csv("%04d.csv" % count)
count += 1
デバッグで、EXIF情報の一部を標準出力に出した時の記述は以下。json形式にまだ慣れていなくて、記述方法はけっこう試行錯誤した。
#標準出力にEXIFの情報を選択して出力する場合
for j in resp2['photo']['exif']:
if j['label'] == 'Model' :
print(j['raw']['_content'])
elif j['label'] == 'Exposure' :
print(j['raw']['_content'])
elif j['label'] == 'Aperture' :
print(j['raw']['_content'])
elif j['label'] == 'ISO Speed' :
print(j['raw']['_content'])
やってみる
以下を実行し、FlickrAPIで所望の画像を検索し、EXIF情報を取得してCSVに書き出すことができた。
$ python3 GetFlickrImagesExif.py
さいごに
これまで作成してきたpythonのrequestsで、FlickrAPIも比較的早く使いこなせるようになった。
今回のプログラムは、EXIFのCSVファイルが画像ファイルごとにできる仕様だが、分析用にはひとつのCSVファイルにまとまっていると使いやすい。
pandasは初めて使ったが、もっと多くの機能があるようなので、使いこなせるようになるとよいと感じた。