Knowledge Stones

フリーランスエンジニアの技術ブログです。

RubyでWebスクレイピングして画像を一括ダウンロードする

 

まえがき

最近、Railsを使ってスプラトゥーン2のLineボットアプリを作ってみています。

作るにあたって、ゲームに登場するブキの画像をアプリ内で使いたかったんですが、公式がまとめて配布しているなんて都合の良いこともなく、地道にネットに転がっている画像を集めることになります。

何かを作るって時は大概こういう面倒な収集作業があるもので、時間もかかりますよね。

そこで、Webスクレイピングを使って、素材の収集作業を効率化しよう!となったわけです。

 

やりたいこと

対象はこのサイトです。

【スプラトゥーン2】全武器(ブキ)一覧とサブ・スペシャル|ゲームエイト

各ブキ情報のページにお目当の画像があるので、

  1. 一覧ページから詳細ページのURLを取得
  2. 各詳細ページから画像URLを取得
  3. 画像URLからダウンロード

こんな流れで処理できれば、一発で全部取れんじゃん?

 

 

 

実行環境

大した準備はいりません、NokogiriのGemだけInstallしてください。

  • Ruby 2.6.3
  • Nokogiri 1.10.3

 

ソースコード

# ------------------------------------------------
# 初期処理
# ------------------------------------------------
# URLにアクセスするためのライブラリの読み込み
require 'open-uri'
# Nokogiriライブラリの読み込み
require 'nokogiri'

index = 0
# 画像ファイルの保存先
download_key = './download_imgs/'

# ------------------------------------------------
# 一覧画面から、詳細画面へのURLを取得する処理
# ------------------------------------------------

# 一覧画面のHTMLを読み込んでNokogiriでparseする
index_url = 'https://game8.jp/splatoon-2/158447'
charset = nil
index_html = open(index_url) do |f|
  charset = f.charset
  f.read
end
index_doc = Nokogiri::HTML.parse(index_html, nil, charset)

# 特定のタグ・クラスの要素を取得して、取得数分処理を繰り返す
target_urls = []
index_doc.xpath('//table[@class="a-table a-table a-table "]').each do |node|
  # 取得した要素内のaタグのhref属性の値を配列にPushする
  node.css('a').each do |links|
    target_urls.push(links.attribute('href'))
  end
end

# ------------------------------------------------
# 取得した詳細画面URLから画像をダウンロードする
# ------------------------------------------------
target_urls.each do |url|
  
  # 一覧画面のHTMLを読み込んでNokogiriでparseする
  charset = nil
  html = open(url) do |f|
    charset = f.charset # 文字種別を取得
    f.read # htmlを読み込んで変数htmlに渡す
  end
  doc = Nokogiri::HTML.parse(html, nil, charset)

  # 特定の画像を抜き取るためにタグ・クラスを指定して目的の値を取得する
  img_urls = []
  doc.xpath('//div[@class="l-3colMain__center l-3colMain__center--shadow"]').each do |node1|
    node1.xpath('//div[@class="archive-style-wrapper"]').each do |node2|
      node2.xpath('//p[@class="a-paragraph"]').each do |node3|
        # imgタグのsrc属性から画像のURLを取得する
        unless node3.css('img').attribute('src').nil?
          img_urls.push(node3.css('img').attribute('src').value)
        end
      end
    end
  end

  # 取得した画像URLから画像をダウンロードする
  img_urls.each do |img_url|
    File.open(download_key + index.to_s + '.png','w+b') do |out|
      open(img_url) do |data|
        out.write(data.read)
      end
      index = index + 1
    end
  end

end

 

処理内容について

ぶっちゃけ雑なクオリティで、汎用性ゼロのプログラムですが、こんなのは別の用途があればチョチョっと手を入れて再利用できれば良いんす。

 

下記の処理でURLを元にHTMLを持ってきてオブジェクトを作っています。

url = '対象のURL'

charset = nil

html = open(url) do |f|

    charset = f.charset

    f.read

end

doc = Nokogiri::HTML.parse(html, nil, charset)

 

HTMLのオブジェクトから、タグ名とクラス名を指定して要素を取得して、

要素内に含まれるタグから欲しい属性を指定して値をとっています。

doc.xpath('//タグ名[@class="クラス名"]').each do |node|

    node.css('タグ名').each do |tag|

        puts tag.attribute('属性名'))

    end

end 

 

上記2点を組み合わせれば、画像のダウンロード以外にも色々できそうですね。

DBにデータを集めて分析かけるなんてことも今後やってみたいです。

 

動作確認

動かすとこんな感じで適当に画像が収集されました。

お目当の画像以外もありますが、プログラムを作り込むよりもこれくらいは手で間引く方を選択しましたw

f:id:polar_bear_tech:20190728203849p:plain

 

何かのご参考になれば!