Python
Flickr

Flickr APIをPythonでたたいて画像検索・EXIF取得する

はじめに

Flickrに投稿する人のカメラの設定を分析できるように、Flickr APIを使って画像検索、EXIF取得できるPythonプログラムを作ってみた。
FlickrAPIの導入についてはいろいろなサイトで紹介があるので割愛するが、例えばこちらを参照。

pythonプラグラム

参考文献

以下の情報を参考にして、プログラムを作成した。

上記に対してオリジナルなところはEXIFを取得することで、jsonデータの平滑化をすることでCSVファイルに所望の状態でEXIF情報を書きだすことができた。

作成ポイント

ライブラリ

以下のライブラリをimportして設計した。

Flickr APIへのアクセスはrequestsを使用する。

auth key情報

Flickr APIを使えるようにすると自分のkeyが発行されるので、それをauth_flickr.pyに記載する。

auth_flickr.py
flickr_key = "1234567890abcdefghijklmnopqrstuv"

メインプログラム側では、上記をimportする。

from auth_flickr import(
    flickr_key
)

Flickr画像の検索

画像検索用のペイロードをjson形式で作成し、Flickr APIflickr.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 APIflickr.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

作成結果

メインプログラム全体は以下。関数化すればよい部分もあるが、実験用の初版なのでこれで良しとした。

GetFlickrImagesExif.py
# -*- 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は初めて使ったが、もっと多くの機能があるようなので、使いこなせるようになるとよいと感じた。