161
176

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.

Googleの画像怜玢APIを䜿っお画像を倧量に収集する

161
Last updated at Posted at 2017-09-24

機械孊習甚の画像を1000枚オヌダヌで集めたい。

前回はBingの画像怜玢APIを䜿っおみたしたが、ニッチな怜玢キヌワヌドで取埗できる画像はGoogleの方が倚そうな気がしたので、いちおうGoogleでもAPIを叩けるようにしおみたした。

ずいうわけで今回はGoogleが提䟛する怜玢゚ンゞンのAPI「Google Custom Search API」 を䜿っお画像収集しおみたす。䜿甚蚀語はPython3です。

Google Custom Search APIはこちらにテストツヌルもありたす。※ "cx"パラメヌタに、埌述する「カスタム怜玢゚ンゞンID」が必芁になりたす

䞀番最初にオチを曞いおしたいたすが、google APIで画像を収集するのはかなりむケおたせん。BingのAPIを䜿ったほうがずっずマシな感じです詳现は埌述。

APIを叩いお画像取埗するたでの流れ

  1. Google Developers Console からAPIキヌを取埗
  2. 「カスタム怜玢゚ンゞン」を䜜成しkeyを取埗
  3. google-custom-search-api-pythonをむンストヌル
  4. コヌドを曞いおAPIを叩く

ずいう流れです。実際は1,2,3の順番はどうでも良い

1. Google Developers Console からAPIキヌを取埗

Googleのカりントを持っおいる前提で、Google Developer Consoleにアクセスしたす。

img3.png

「その他の䞀般的なAPI」にある「Custom Search API」を遞択

img1.png

右偎にある「プロゞェクトを䜜成」で、適圓な名前を指定しおプロゞェクトを䜜成する。
぀いでに「有効にする」もクリックしお、Custom Search APIを有効化する。

img2.png

その埌、巊メニュヌにある「認蚌情報」をクリックしお、「認蚌情報を䜜成」>「APIキヌ」を遞択
出おきたキヌがこれから必芁になるAPI keyになるのでメモ。

※䞍芁かもしれたせんが、右䞊の「無料トラむアルに登録」が必芁そうであれば適宜登録しおみおください←詳しい操䜜忘れたしたorz

2. 「カスタム怜玢゚ンゞン」を䜜成しIDを取埗

Googleの Custom Search APIを動かすためには、䞊蚘のAPI keyだけでなく「カスタム怜玢゚ンゞン」なるものを䜜成しそのIDも必芁になりたす。

こちらにアクセスし、IDを䜜成しおください。

詳しい䜜り方は以䞋のブログがわかりやすいので参照

「怜玢゚ンゞンID」が取埗できればokです。

3. google-custom-search-api-pythonをむンストヌル

今回はAPIをPythonで動かすため、公匏のPythonモゞュヌル をむンストヌルしお䜿いたす。
自分でスクラッチから面倒なコヌドをしこしこ曞かなくおも、簡単にAPIを叩ける䟿利な関数を利甚できるようになりたす。コマンド䞀発でむンストヌルできたす。

$ pip install --upgrade google-api-python-client

これでAPIを動かすための準備は党郚OK

4. コヌドを曞いおAPIを叩く

動くコヌドを眮いおおきたす。本圓にミニマムで動かしたい堎合は、こちらの "The Code" ずいうずころを参照しおください

ここでは䟋ずしお、 『猫』日本語ずいう怜玢ワヌドでひっかかる画像を1000枚収集しおみたす。
Pythonのversionは 3.5.2で、実行は以䞋

$ python3 google_api.py

実行するずsave_dir_pathで指定したディレクトリ䞋にcorr_table、imgs、pickle_filesずいうディレクトリを䜜成し、その䞋に各デヌタを生成したす。

google_api.py
# -*- coding:utf-8 -*-
import urllib.request
import httplib2
import json
import os
import pickle
import hashlib
import sha3

from googleapiclient.discovery import build


def make_dir(path):
    if not os.path.isdir(path):
        os.mkdir(path)


def make_correspondence_table(correspondence_table, original_url, hashed_url):
    correspondence_table[original_url] = hashed_url


def getImageUrl(api_key, cse_key, search_word, page_limit, save_dir_path):

    service = build("customsearch", "v1", developerKey=api_key)
    page_limit = page_limit
    startIndex = 1
    response = []

    img_list = []

    make_dir(save_dir_path)
    save_res_path = os.path.join(save_dir_path, 'api_response_file')
    make_dir(save_res_path)

    for nPage in range(0, page_limit):
        print("Reading page number:", nPage + 1)

        try:
            response.append(service.cse().list(
                q=search_word,     # Search words
                cx=cse_key,        # custom search engine key
                lr='lang_ja',      # Search language
                num=10,            # Number of images obtained by one request (Max 10)
                start=startIndex,
                searchType='image' # search for images
            ).execute())

            startIndex = response[nPage].get("queries").get("nextPage")[0].get("startIndex")

        except Exception as e:
            print(e)

    with open(os.path.join(save_res_path, 'api_response.pickle'), mode='wb') as f:
        pickle.dump(response, f)

    for one_res in range(len(response)):
        if len(response[one_res]['items']) > 0:
            for i in range(len(response[one_res]['items'])):
                img_list.append(response[one_res]['items'][i]['link'])

    return img_list


def getImage(save_dir_path, img_list):
    make_dir(save_dir_path)
    save_img_path = os.path.join(save_dir_path, 'imgs')
    make_dir(save_img_path)

    opener = urllib.request.build_opener()
    http = httplib2.Http(".cache")

    for i in range(len(img_list)):
        try:
            url = img_list[i]
            extension = os.path.splitext(img_list[i])[-1]
            if extension.lower() in ('.jpg', '.jpeg', '.gif', '.png', '.bmp'):
                encoded_url = url.encode('utf-8')  # required encoding for hashed
                hashed_url = hashlib.sha3_256(encoded_url).hexdigest()
                full_path = os.path.join(save_img_path, hashed_url + extension.lower())

                response, content = http.request(url)
                with open(full_path, 'wb') as f:
                    f.write(content)
                print('saved image... {}'.format(url))

                make_correspondence_table(correspondence_table, url, hashed_url)

        except:
            print("failed to download images.")
            continue


if __name__ == '__main__':
    # -------------- Parameter and Path Settings -------------- #
    API_KEY = 'xxxxxxxxxxxxx'
    CUSTOM_SEARCH_ENGINE = 'xxxxxxxxxxxxxxx'

    page_limit = 10
    search_word = '猫'
    save_dir_path = 'path/to/dir'

    correspondence_table = {}

    img_list = getImageUrl(API_KEY, CUSTOM_SEARCH_ENGINE, search_word, page_limit, save_dir_path)
    getImage(save_dir_path, img_list)

    correspondence_table_path = os.path.join(save_dir_path, 'corr_table')
    make_dir(correspondence_table_path)

    with open(os.path.join(correspondence_table_path, 'corr_table.json'), mode='w') as f:
        json.dump(correspondence_table, f)

コヌドの䞀郚解説

パラメヌタに぀いお

  • API_KEYにGoogle Developers Consoleで取埗したAPI keyを指定
  • CUSTOM_SEARCH_ENGINEにカスタム怜玢゚ンゞンで取埗したkeyを指定
  • 怜玢したいク゚リヌは search_wordで指定日本語も可
  • 取埗したい画像枚数は page_limit で指定埌述
  • 萜ずしおきた画像を保存するpathを save_dir_path で指定

その他のAPIパラメヌタはこちらを参照

1ク゚リヌで取埗できる画像数はresponse.appendのnumで指定できたす。このスクリプトでは䞊限倀の10枚を指定しおいたす。

1ク゚リヌをpage_limitで指定した回数だけリヌプさせお総画像枚数を取埗したす。䟋えば、1000枚画像がほしければ、num=10の状態で、page_limit=100にすれば良いずいうこずです。しかし実際には指定した枚数よりも少ない数が返っおくるそうです。

bingAPIのずきは、次のルヌプで始たる画像のindex番号などを考慮しないずいけたせんでしたが、google APIの堎合はその蟺をgoogleapiclientによっおいい感じに隠蔜しおくれおいるので、実質page_limitを䜕回回せばいいかずいうこずだけ考えれば良いだけです。

さお、ここで蚘事の最も重芁な点ですが、

google APIにはこのpage_limitにずんでもない制限欠陥がありたす。

具䜓䟋でいうず、1000枚の画像を埗ようず思っおpage_limit=100を指定しおも、実際にルヌプが回るのはなんず10たでで、それ以䞊は珟状のAPIではどうにもならないようです。぀たり、1぀の怜玢単語からAPIを叩いお埗られる画像はMaxでも100枚も無い、ずいうこずです。

そんなバカなず思い色々調べおみたしたが、この問題には倚くの人の阿錻叫喚が聞こえるだけで未だに解決策も代案も無いようです。

日本語ブログでも話題にしおいる方々がいたした

こちらのQiita蚘事でもいろいろず怜蚌されおいお参考になりたした

叀いものだず2012幎ぐらいからスレが立おられおいるようですが未だにその解決策はないみたいです。でもそんなこずっおあるのかあのグヌグルだよ自分の詠唱力が䜎いだけなのか...誰か知っおたら教えおください
ちなみに、「もしかしお無料枠でやっおるからか」ず思っお課金も詊しおみたしたが同じ結果でした。

ただ、こちらのAPIをデモ実行できるずころで実際に "startIndex"パラメヌタに100枚目以降の画像を取埗するように指定しおみるず400番゚ラヌが返っおくるので、やはりAPIの仕様ずしお1怜玢単語に察しお100枚以䞊の画像を䞀回で取埗するこずはできないようです。

珟状の結論: Bing APIを䜿いたしょう

結果ファむルに぀いお

python3 google_api.py を実行するず、save_dir_pathで指定したディレクトリ䞋にcorr_table、imgs、pickle_filesずいう぀のディレクトリを䜜成し、その䞋に各デヌタを生成したす。

  • imgs: APIによっお収集した画像が栌玍されるディレクトリ
  • pickle_files: APIを叩いお返っおくるレスポンスjsonデヌタがpickle圢匏で栌玍されるディレクトリ。実際に返っおくるjson圢匏の䟋はこちら。我々が欲しい画像のURLはjson䞭の 'value' -> 'contentUrl' に栌玍されおいるのでそれを取り出しおいたす。
  • corr_table: 収集した画像の元URLず、それをhash化したファむル名の察応衚が蟞曞圢匏で栌玍されおいるディレクトリ「画像保存時のファむル名に぀いお」を参照。特に必芁ないず思いたすが、䞀応デヌタずしお曞き出しおおきたす。

察象画像皮

ここではjpg, jpeg, gif, png, bmpの拡匵子を持぀画像のみを取埗の察象ずしおいたす

画像保存時のファむル名に぀いお

保存する画像ファむル名は䜕も考えずに連番でも良かったのですが、機械孊習に甚いる画像のためできるだけ同䞀の画像は省きたいずころです。

そこで、保存画像名を画像取埗時のURLにしお重耇する名前は勝手に保存時に䞊曞きされるようにしようずしたしたが、URLが長すぎるファむルが存圚するファむル名が長くなるために保存時に支障がでるものがありたした。

その察策ずしお、画像取埗時のURLに察しおhash化凊理を行いたした。

hash化は元々暗号化のために甚いられるものですが、元の文字数に関係なく65文字ほどの文字列に倉換しおくれる同じ内容からは同じ文字列を生成する ずいう特城があるので、これを利甚しおファむル名を短くするのず同時に、同内容の画像ファむルは重耇ずしお䞊曞きできたす。
pythonのhash化にはhashlibを甚い、hash化アルゎリズムのsha3に぀いおはこちらのブログを参照したした。

hash化䟋
http://hogehogehogehoge~~~/cat.jpg長いURL-> hash化 -> 65桁ほどの蚘号列①を生成  
http://fugafugafugafuga~~~/cat2.jpg長いURL-> hash化 -> 65桁ほどの蚘号列②を生成  
http://hogehogehogehoge~~~/cat.jpg長いURL-> hash化 -> ①ず同じ65桁ほどの蚘号列を生成

アクセス制限に぀いお課金金額

Custom Search APIの無料枠の利甚制限は 100ク゚リヌ/日 です。
1ク゚リヌで最倧10枚画像を取埗できるので、数倀䞊は無料で 1000枚/日 の画像が取埗できたすが、前述したずおり1怜玢単語に察しおそもそも最倧10ク゚リヌしかルヌプできない最倧100枚しか取埗できないずいうAPI仕様的な制限がありたす。

ちなみに、無料枠を越えお利甚したい堎合は 1000ク゚リヌあたり$5だそうです。

その他の制限ずしお問題にならないず思いたすが、ナヌザ1人が100秒間に投げられるク゚リヌ数の䞊限は100ク゚リヌだそうです。

自身のアカりントの課金状態を調べたい堎合はこちらを参照

結果ず所感

䞊蚘の通り、『猫』ずいうキヌワヌドで1000枚画像を取埗しようずしおも、実際には92枚しか猫画像を取埗するこずができたせんでした。ちなみにBing APIで同様のこずをするず824枚ゲットできたした。

Bing APIもBing APIで、なぜか途䞭のルヌプからひたすら同じ画像を収集するようなバグがあるっぜいですが、それでも92枚よりは倧分マシです。

ずいうわけで、画像怜玢APIを䜿っお画像収集を行いたい堎合はたずBing APIで1000枚匱くらいの画像をサクッず取っおきお、さらに雀の涙でも新芏の画像がずにかく欲しいずいうくらい切矜詰たっおいるのであればgoogle APIも䜵せお䜿っおみれば良いのでは、ずいう感じです。

本圓の本圓にもっず倧量の画像1000枚~を取埗したい堎合は、Seleniumなどを䜿っおペヌゞを自動ロヌドしおいくタむプのscraperを䜜るor探すしかないかなず思いたす。

20170926远蚘たずめ蚘事を曞きたした → Yahoo、Bing、Googleでの画像収集事情たずめ

Github

䞊蚘ず同様の内容をGithubにも茉せおいたす。APIkeyや怜玢゚ンゞンIDの指定方法だけ異なりたすのでREADMEを参照しおください

参考

  • Google API Client Libraries > Python > Getting Started
  • URL゚ンコヌド・デコヌド
    • "猫"のように日本語を怜玢単語に指定するず゚ンコヌドされた文字列がURLに指定されたす。䟋によっおPython3や2系では日本語の゚ンコヌド呚りが厄介なのでこれで゚ンコヌドされた文字列などを確認し぀぀目的の文字列が正しく怜玢されおいるか確認しおいたした
  • 「カスタム怜玢゚ンゞン」䜜成時にちょっずハマったずころを参考にさせおもらいたした 蚘事1、蚘事2
161
176
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
161
176

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?