はじめに
概要
Photos | PyCon JP 2014 in TOKYOにあるFlickrの写真をごにょごにょするPythonスクリプトを作ったときのメモ。
以下の表に従い、
したいこと | Flickr APIのURI | Flickr APIの引数 |
---|---|---|
アルバムの一覧を取得 | flickr.photosets.getList | user_id |
アルバム内の写真を取得 | flickr.photosets.getPhotos | photoset_id |
写真のURLを取得 | flickr.photos.getSizes | photo_id |
- requestsでHTTPリクエスト投げる
- 返ってきたXMLをxml.dom.minidomでパースする
の二つの作業を行っているだけです。
flickr.photos.getSizesの中に、サイズごとの写真のURLが入っているのがちょっと意外だったかな。
同じようなことをやっている記事が既にいくつかありました。
結果をJSONで取得したり、flickr.photos.getSizesではなくURLを直接構成している点がこの記事と異なり参考になります。
URL構成規則が公開されているならAPI叩くより自分で構成したほうが賢いですね。
前提
前提条件は以下の通りです。
なお、requestsの導入及び使い方については次の記事が参考になります。
FlickrのAPIキー取得については次の記事が参考になります。
コード
最初Python2でやったら動かなかったのでちょっと応急処置中です。ひとまずはPython2でも動作しますが、後でちゃんと直すかもしれません。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import requests
import xml.dom.minidom as md
class Flickr(object):
u"""
基本的な使い方
# 適宜修正してください
api_key = 'ENTER_YOUR_API_KEY'
f = Flickr(api_key)
# pyconjpのuser_id
print(f.get_photoset_ids_from_user_id('102063383@N02'))
# PyCon JP 2014 day4 Sprintフォトセットのphotoset_id
print(f.get_photos_from_photoset_id('72157647739640505'))
# 上記フォトセットの中の写真のひとつ
print(f.get_url_from_photo_id('15274792845'))
"""
def __init__(self, api_key):
self.api_url = 'https://api.flickr.com/services/rest/'
self.api_key = api_key
def get_photoset_ids_from_user_id(self, user_id):
u"""
ユーザー(user_id)が所有するアルバムidの一覧(photset_idのリスト)を返す
今回の目的には必要ない
"""
# requestの送信
r = requests.post(self.api_url, {'api_key': self.api_key,
'method': 'flickr.photosets.getList',
'user_id': user_id
})
# xmlをパースしてdomオブジェクトにする
dom = md.parseString(r.text.encode('utf-8'))
# domオブジェクトからphotoset_idを探し出す
result = []
for elem in dom.getElementsByTagName('photoset'):
result.append(elem.getAttribute('id'))
return result
def get_photos_from_photoset_id(self, photoset_id):
u"""
アルバムid(photset_id)内の写真一覧(photo_idのリスト)を返す
"""
# requestの送信
r = requests.post(self.api_url, {'api_key': self.api_key,
'method': 'flickr.photosets.getPhotos',
'photoset_id': photoset_id
})
# xmlをパースしてdomオブジェクトにする
dom = md.parseString(r.text.encode('utf-8'))
# domオブジェクトからphoto_idを探し出す
result = []
for elem in dom.getElementsByTagName('photo'):
result.append(elem.getAttribute('id'))
return result
def get_url_from_photo_id(self, photo_id):
u"""
写真(photo_id)が実際に格納されているURLを返す
"""
# requestの送信
r = requests.post(self.api_url, {'api_key': self.api_key,
'method': 'flickr.photos.getSizes',
'photo_id': photo_id
})
# xmlをパースしてdomオブジェクトにする
dom = md.parseString(r.text.encode('utf-8'))
# domオブジェクトからURLを探し出す
result = None
for elem in dom.getElementsByTagName('size'):
# オリジナルのサイズのもののみにする
if elem.getAttribute('label') == 'Original':
result = elem.getAttribute('source')
# オリジナルは1個だと考えて他はスキップ
break
else:
# 何もない場合はNone
pass
return result
if __name__ == '__main__':
# 動作確認
# 適宜修正してください
api_key = 'ENTER_YOUR_API_KEY'
f = Flickr(api_key)
# pyconjpのuser_id
print(f.get_photoset_ids_from_user_id('102063383@N02'))
# PyCon JP 2014 day4 Sprintフォトセットのphotoset_id
print(f.get_photos_from_photoset_id('72157647739640505'))
# 上記フォトセットの中の写真のひとつ
print(f.get_url_from_photo_id('15274792845'))
実行結果
コードの実行結果
mainの中の'ENTER_YOUR_API_KEY'を自分のAPIキーに書き換えて
python flickr.py
を実行すると、以下の写真のURLが入手できます。
使用例
URLをまとめて取得するには、flickr.pyをインポートするなりコピペするなりして、以下のようにすればよいでしょう。
なお、ここでもENTER_YOUR_API_KEYの部分は自分のAPIキーに変更してください。
また、テスト実行時に時間がかからないようにbreakがついているので、実際の実行時にはbreakを取ってください。
targetsとかを指定するのが面倒なら、get_photoset_ids_from_user_idを使えば特定のユーザーの全アルバムが取得でき、そちらを使うののありかもしれません。これだとかなりクローリングぽくなりますね。
>>> from collections import defaultdict
>>> api_key = 'ENTER_YOUR_API_KEY' # 適宜修正してください
>>> f = Flickr(api_key)
>>> targets = ['72157641092641983', # PyCon JP 2014 下見 - an album on Flickr
... '72157647111767068', # PyCon JP 2014 day1 Tutorial - an album on Flickr
... '72157647184237569', # PyCon JP 2014 day2 Conference - an album on Flickr
... '72157647216509890', # PyCon JP 2014 day3 Conference - an album on Flickr
... '72157647739640505' # PyCon JP 2014 day4 Sprint - an album on Flickr
... ]
>>> d = {}
>>> for elem in targets: # photosetの中のphotoの一覧を作成
... d[elem] = f.get_photos_from_photoset_id(elem)
... break # 取る
>>> d2 = defaultdict(list)
>>> for k,v in d.items(): # データのurl一覧を作成
... for elem in v:
... d2[k].append(f.get_url_from_photo_id(elem))
... break # 取る
... break # 取る
>>> for k,v in d2.items(): # urlの一覧を用いてファイルを取得
... if not os.path.exists(k):
... os.mkdir(k)
... for elem in v:
... r = requests.get(elem) # urlからデータを取得
... # photoset_id/ファイル名.jpg で保存
... with open("{0}/{1}".format(k, elem.split("/")[-1]), 'wb') as g:
... g.write(r.content)
今後
- 大した内容はありませんが、個々の機能の解説を書くかもしれません。
- コード自体は改善の余地が大ありなのでアドバイスを頂ければと思います。