Ruby
Mac
機械学習
画像
Slenium

Rubyで画像を無限収集する

はじめに

機械学習の教師データを集めるため大量の画像が必要だったので、Rubyでプログラムをかきました。
今回はsleniumを使いました。理由は画像検索結果の新規読み込みで動的なスクロールが必要になるからです。

また、今回初めて Google画像検索よりBingの方が、精度のいい画像がでてくることに気づきまして(個人の感覚かもしれません)、Bing画像検索でレッツゴーです。

(本来ならAPIを使うべきですが、Googleは制限が多い、BingAPIは使うまでのUIが変わってて使いづらかった、という経緯により、このような対応をとりました。サーバー負荷を忘れず必ずおとなしくやりましょう。自己責任でお願いします。)

手順

1.必要なライブラリを入れる
2.画像の保存先フォルダを作成
3.コードを書く
4.Run

1.必要なライブラリを入れる

ターミナルで

brew install chromedriver

gem install open_uri_redirections

....等々(以下のrequireを参照して下さい)

2.画像の保存先フォルダを作成

プログラムコードと同じ階層に任意フォルダを作る(ここではsakuramana_imgとしました)

3.コードを書く

infinite_crawl.rb
require 'selenium-webdriver'
require 'nokogiri'
require 'open-uri'
require 'addressable/uri'
require 'kconv'
require "uri"
require 'open_uri_redirections'

#文字対応増やすためStringクラスを拡張
class String  
 def sjisable  
  str = self  
  str = str.exchange("U+301C", "U+FF5E") # wave-dash  
  str = str.exchange("U+2212", "U+FF0D") # full-width minus  
  str = str.exchange("U+2013", "U+FF0D") # full-width minus  
  str = str.exchange("U+00A2", "U+FFE0") # cent as currency  
  str = str.exchange("U+00A3", "U+FFE1") # lb(pound) as currency  
  str = str.exchange("U+00AC", "U+FFE2") # not in boolean algebra  
  str = str.exchange("U+2014", "U+2015") # hyphen  
  str = str.exchange("U+2016", "U+2225") # double vertical lines  
 end  

 def exchange(before_str,after_str)  
  self.gsub( before_str.to_code.chr('UTF-8'),after_str.to_code.chr('UTF-8') )  
 end  

 def to_code  
  return $1.to_i(16) if self =~ /U\+(\w+)/  
  raise ArgumentError, "Invalid argument: #{self}"  
 end  
end  

def get_image
 driver = Selenium::WebDriver.for :chrome
 #q=の後に日本語でキーワード入れて下さい
 driver.navigate.to 'https://www.bing.com/images/search?q=さくらまな'

 10.times do
  driver.find_elements(:class,'iusc').last.location_once_scrolled_into_view
  current_count = driver.find_elements(:class, 'iusc').length
  until current_count < driver.find_elements(:class, 'iusc').length
   sleep(3)
  end
  sleep(5)
 end

 elements = driver.find_elements(:class, 'iusc')
 ary = []
 elements.each do |element| 
  ary << element.attribute('m').scan(/","murl\":"(.+)","turl":/)
 end

 @ary = []
 ary.flatten!
 ary.each do |img|
  if /\.(jpg|png)$/ =~ img.to_s 
   @ary << URI.escape(img.to_s.force_encoding('utf-8').sjisable.encode('UTF-8'))
  end
 end
 @ary.flatten!
 #ファイルの書き出しを行う
 @ary.each_with_index do |url,i|
  #ファイル名を決定
  begin
   if /\.(jpg)$/ =~ url 
    filename = "sakuramana#{i}.jpg"
   else 
    filename = "sakuramana#{i}.png"
   end

   #ファイルを保存するフォルダ名を指定
   p filename
   dirname = "./sakuramana_img/*" + filename
   open(dirname, 'wb') do |file|
    open(url.encode("utf-8", invalid: :replace, undef: :replace), :allow_redirections => :safe) do |data|
     sleep(1)
     file.write(data.read)
    end
   end
  rescue
   puts "とばしました"
  end
 end
end
get_image

4.Run

ターミナルで
ruby infinite_crawl.rb

(ファイル名は適宜合わせてください)

とすると、勝手にクロムが立ち上がり、先ほど指定したフォルダに画像が自動で保存されていくはずです。

尚、10.times do の数字をいじれば30件毎に画像の取得件数が増減します。

おわりに

ということで、以上になります。

エラーでたらそれはきっとsleniumドライバーのあれかBingの仕様変更です。
前者であればたくさんでてくるのでググってみて下さい。

だれかのためになれば幸いです。

それでは〜!