Rubyでbingから画像をスクレイピングしたよ
唐突ですが、乃木坂46の3期生の顔を機械学習で学習させて未知の画像データから名前を推測するWebアプリケーションを作っています。Webから画像データを取得するスクレイピングの作業の際に、色々とハマったので備忘録的に記させていただきます。
進捗
顔認識させるために大量の画像データが必要となるので集めることになりました。
ということでbing画像検索から自動でローカルに画像を保存するスクリプトを書きました。
Googleを使わなかったのは、同じことをやろうとしていた人がGoogleよりbingの方がいいよって言っていたためです。
はじめPythonのBeautifulSoupとかScrapyとかのスクレイピング用ライブラリorフレームワークを使おうと思ったのですが、結構難しかったのでRubyで書くことにしました。似たようなスクリプトがネットにあったので参考にさせていただきました。
taremimi.hatenablog.jp
できたコードと解説
ほぼパクリになりました。
require 'uri' require 'open-uri' require 'nokogiri' require 'selenium-webdriver' class Scraper def initialize(prefix, query) @prefix = prefix @query = URI.escape(query.encode("utf-8")) @search_url = "https://www.bing.com/images/search?q=" + @query end def scrape_img driver = Selenium::WebDriver.for :chrome driver.navigate.to(@search_url) 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") @array = [] elements.each do |element| @array << element.attribute("m").scan(/","murl\":"(.+)","turl":/) end @array = @array.flatten! @url_array = [] @array.each do |img| if /\.(jpg|png)$/ =~ img.to_s @url_array << URI.escape(img.to_s.force_encoding("utf-8")) end end @url_array.each_with_index do |url, i| begin if /\.(jpg)$/ =~ url filename = "#{@prefix}_#{i}.jpg" else filename = "#{@prefix}_#{i}.png" end p filename + " << " + url dirname = "#{@prefix}_img" FileUtils.mkdir_p(dirname) unless FileTest.exist?(dirname) filepath = dirname + "/" + filename open(filepath, "wb") do |f| open(url.encode("utf-8", invalid: :replace, undef: :replace)) do |data| sleep(2) f.write(data.read) end end p "できたよ" rescue p "無理でした" end end driver.quit end end if __FILE__ == $0 keywords = {"ririan" => "伊藤理々杏", "rentan" => "岩本蓮加", "minamin" => "梅澤美波", "momochan" => "大園桃子", "kubochan" => "久保史緒里", "tamachan" => "阪口珠美", "denchan" => "佐藤楓", "renochan" => "中村麗乃", "hazukichan" => "向井葉月", "miichan" => "山下美月", "ayaty" => "吉田綾乃クリスティー", "yodachan" => "与田祐希" } keywords.each do |prefix, query| p prefix p query scraper = Scraper.new(prefix, query) scraper.scrape_img end end
はじめ'Nokogiri'というスクレイピング用のgem(ライブラリ)を使用していたのですが、bing画像検索ではおそらくJavascriptによって動的にページを生成しているので'Nokogiri'では対処できなそうだ、ということで'Selenium'というgemを使うことにしました。この'Selenium'というgemは、プログラムからWebプラウザを操作し、Webサイトが正しく動作するか検証するためのツールだそうです。JavaやRubyやPythonなどいろんな言語でのサポートがあり、とても便利なのでオススメです。
Scraperクラスのインスタンスを生成するときに引数として'prefix'と'query'を指定します。
'query'では検索したいワードを指定します。'prefix'で指定した文字列がフォルダ名、ファイル名となって出力されます。
スクレイピングするとき、相手のサーバに負荷をかけないために所々でsleep関数を使っています。
sleepを使わないと短時間で無数のリクエストが相手サーバに送られてしまうので、マナーとしてsleepで1,2秒の間隔を置くらしいです。
そもそも公式のAPIを使っていない時点でマナーとしてどうなのっていう感じはある。
Selenium関連のエラーは、ドライバのバージョンによるエラーがほとんどだそうです。
僕が入れた最新バージョンのchromedriverは2.32でした。
Google Chrome使っている人は「selenium chromedriver」で検索すれば色々情報が出てきます。
qiita.com
zip形式のドライバを解凍して、$which rubyしたときに出てくるフォルダと同じ階層に入れればOKです。
僕の場合はrbenvを使用しているので、'~/.rbenv/shims'としました。
Githubにもあげてみました!pushできました!
github.com