Yahoo!画像検索で自動収集してデータセットを作る

2017-04-05

機械学習に用いるデータセットの作成はしんどいとよく聞きます.画像認識であれば少なくとも数百,数千枚は必要なので,自動でダウンロードしてラベル付けすれば少しは楽になりそうですね.

自動収集している人はたくさんいるので今更目新しくもないですが,これも勉強の一環ということでやってみました.

なぜ Yahoo なのか

日本で最強の検索エンジンはもちろん Google です.Yahoo! Japan も独自の検索アルゴリズム開発を数年前にやめて Google のものを使っているはずなので,実質 99% のカバー率.Yahoo! よりも Google のほうが検索で表示される画像の枚数も多いです.

しかし,Google 検索エンジンを利用する例は少ないですよね.個人的には Google を使いたかったのですが,残念ながら無理でした.

理由はたぶん二つくらいあって,一つは Google の検索結果のページを読むとリンクがないこと.圧縮された画像のリンクはありますが,元画像のリンクが恐らくないので,画質が悪くてデータセットとしては微妙な気がします.

理由のもう一つは,Google Custom Search の導入.以前は Google Image Search API があったのですが,2011 年に非推奨になり,Custom Search に切り替わりました.(使えるかもしれませんが.) じゃあ Custom Search 使えばいいじゃんって話ですが,この API は課金制なので大量のデータを持ってくるとなると...って感じです.

自動収集の流れ

Google 画像検索の文句をだらだら書いてたので導入が長くなってしまいましたが,最初に大まかな流れを考えておきたいと思います.

  1. 検索キーワード,収集する枚数を指定
  2. Yahoo! の検索結果を HTML ごと取得
  3. 要素を解析して画像の URL を特定
  4. URL を読み込んで画像ファイルにする
  5. 一緒に画像ファイル名を書いたテキストファイルも作る

こんな感じにしたいと思います.複数の種類の画像を一度に集めてそれぞれラベル付けするのは書くのがめんどくさいので,一種類ずつ検索する形で妥協.

検索キーワード,枚数の入力

こんなこと項目にするまでもないですが一応.

keyword = input('keyword: ')
num = int(input('how many?: '))

HTML を取得

HTML ファイルの読み込みは簡単にできます.

from urllib.request import urlopen

html = urlopen('http://example.com').read()

Yahoo! 画像検索の URL パラメータ

  • ei: 文字コード
  • b: 画像の枚数のインデックスみたいなもの
  • p: キーワード

って感じです.

それを https://search.yahoo.co.jp/image/search の後につければ完成です.

code = '&ei=UTF-8'
phrase = '?p=' + keyword
start = '&b=' + str(num)
url = 'https://search.yahoo.co.jp/image/search' + phrase + code + start

画像の URL を抜き出す

画像のリンクは a タグのところを抜き出せばいいですね.img タグの方は恐らく圧縮画像のリンクなので無視.

なんとなく使いませんでしたが,パッケージ使うのがたぶん一番楽です.

html = '<div class="tb"><a href="http://example.com/img.jpg" target="...'

URL はこのような感じで埋め込まれています.この場合なら

url = html[html.find('href="')+6:html.find('" ')]

みたいな感じで抜き出せますね.頭悪いコードみたいで嫌なんですけど...

画像をダウンロード

この URL を使って画像をダウンロードできます.ファイルをバイナリデータ書き込みモードで開いて,URL で読み込んだ内容を保存します.

image = urlopen(url).open()

s = open(keyword + '.jpg', 'wb')
s.write(image)
s.close()

ラベルファイル作成

データを読み込むときに楽なので,同時にラベルのファイルも作っておきます.

f = open(keyword + '.txt', 'w')
f.write(keyword + '.jpg')
f.close()

注意すること

日本語対応

このままではキーワードに日本語が使えません.リクエストしたときに ASCII がなんちゃらってエラーが出ると思うので,エンコードして引数に渡す必要があります.

複数枚の収集

複数枚収集するためには for 文で繰り返せばいいでしょう.検索結果には 20 枚の画像が含まれているので,それをすべて読み込み終えたらまた検索するようにすればできます.

画像ファイルの形式

画像には様々なフォーマット形式があるので,すべて .jpg とは限りません.URL の末尾からフォーマットの種類を特定してつけるといいでしょう.

ラベルファイルの書き込みモード

'w' は上書きしていくので,一度閉じるとまた書いたときに前のラベル名が消されます.それを防ぐためには 'a' で開き,上書きではなく追加するようにするか,もしくは画像名をリストにしておいて後で一気に書く方法でもいいでしょう.

最後に

Yahoo! の HTML に合わせて作ったので汎用性皆無ですね.