14
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PythonからFlickr APIを使う

Last updated at Posted at 2014-12-08

はじめに

概要

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
  1. requestsでHTTPリクエスト投げる
  2. 返ってきたXMLをxml.dom.minidomでパースする

の二つの作業を行っているだけです。
flickr.photos.getSizesの中に、サイズごとの写真のURLが入っているのがちょっと意外だったかな。

同じようなことをやっている記事が既にいくつかありました。
結果をJSONで取得したり、flickr.photos.getSizesではなくURLを直接構成している点がこの記事と異なり参考になります。
URL構成規則が公開されているならAPI叩くより自分で構成したほうが賢いですね。

前提

前提条件は以下の通りです。

  • requestsがインストールされていること
  • FlickrのアカウントとAPIキーを持っていること

なお、requestsの導入及び使い方については次の記事が参考になります。

FlickrのAPIキー取得については次の記事が参考になります。

コード

最初Python2でやったら動かなかったのでちょっと応急処置中です。ひとまずはPython2でも動作しますが、後でちゃんと直すかもしれません。

flickr.py
#!/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)

今後

  • 大した内容はありませんが、個々の機能の解説を書くかもしれません。
  • コード自体は改善の余地が大ありなのでアドバイスを頂ければと思います。
14
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?