読者です 読者をやめる 読者になる 読者になる

あかんわ

ブログで日誌でも書くようにすれば多少はやる気が出るかと思ったんです

Mac OS X El CapitanでOga(大鋸)を使ったRubyのWebスクレイピング

記事の概要

Rubyとチャットボットの学習に使用している書籍に、Google検索を利用して、チャットボットの応答文を生成する方法が出てきたので、RubyでWebスクレイピングする方法を調べました。

目次

大鋸の準備

RubyでWebスクレイピングだとNokogiri(鋸)が有名らしいのですが、少し前のPOSTDの記事Oga(大鋸)が推奨されていた事を思い出し、Nokogiriを使ったことが無く、Nokogiriの有用さを知らない私は、Ogaを使うことにしました。

そんなわけで、ogaをインストールします。

$gem install oga
Fetching: ast-2.2.0.gem (100%)
Successfully installed ast-2.2.0
Fetching: ansi-1.5.0.gem (100%)
Successfully installed ansi-1.5.0
Fetching: ruby-ll-2.1.2.gem (100%)
Building native extensions.  This could take a while...
Successfully installed ruby-ll-2.1.2
Fetching: oga-2.2.gem (100%)
Building native extensions.  This could take a while...
Successfully installed oga-2.2
4 gems installed

ちなみに、nokogiriは何かの折にインストールされていたらしく、既に入ってました。

$gem list nokogiri

*** LOCAL GEMS ***

nokogiri (1.6.6.2)

大鋸でHTMLを切り出す

ogaのインストールができたので、試しに、『スクレイピング』をGoogleで検索した場合のURLからopen-uriでHTMLを読み込み、Ogaでパースしてみます。

$pry
[1] pry(main)> require 'open-uri'
=> true
[2] pry(main)> require 'Oga'
=> true
[3] pry(main)> url = 'https://www.google.co.jp/search?q=%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0&ie=utf-8&oe=utf-8&hl=ja'
=> "https://www.google.co.jp/search?q=%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0&ie=utf-8&oe=utf-8&hl=ja"
[4] pry(main)> html = open(url) do |f|
[4] pry(main)*   f.read
[4] pry(main)* end  
=> "<!doctype html><html itemscope=\"\" itemtype=\"http://schema.org/SearchResultsPage\" lang=\"ja\"><head><meta content=\"text/html;   
charset=UTF-8\" http-equiv=\"Content-Type\"><meta content=\"/images/branding/googleg/1x/googleg_standard_color_128dp.png\" itemprop=\"image  
\"><link href=\"/images/branding/product/ico/googleg_lodp.ico\" rel=\"shortcut icon\"><title>スクレイピング - Google 検索</title>"  
[5] pry(main)> doc = Oga.parse_html(html)
=> Document(
  doctype: Doctype(name: "html")
  children: NodeSet(Element(name: "html" attributes: [Attribute(name: "itemscope" value: ""), Attribute(name: "itemtype" value:
[6] pry(main)> 

無事にパースできたようなので、XPathを使った要素の探査を利用して『検索結果のリンク』を切り出してみます。

ブラウザの開発ツールを使って調べてみると、

f:id:b0npu:20160321052643p:plain

『検索結果のリンク』はh3タグ内のaタグに記述されているようなので、XPath//h3/aからhref属性の値の取得を試みます。

[6] pry(main)> doc.xpath('//h3/a').each do |node|
[6] pry(main)*   puts node.get('href')  
[6] pry(main)* end  
/url?q=https://ja.wikipedia.org/wiki/%25E3%2582%25A6%25E3%2582%25A7%25E3%2583%2596%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggYMAE&usg=AFQjCNHZAORThLyskf3nDxY9SlIMZY3Mvg
/url?q=https://ja.wikipedia.org/wiki/%25E3%2582%25A6%25E3%2582%25A7%25E3%2583%2596%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggcMAI&usg=AFQjCNHZAORThLyskf3nDxY9SlIMZY3Mvg
/url?q=http://vsanna.sakura.ne.jp/wp/2015/01/scraping_start_up/&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggmMAM&usg=AFQjCNHXVN2xrEYNm28Ku2s7nwGVPdAiSQ
/url?q=http://nelog.jp/import-io&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggtMAQ&usg=AFQjCNGVG386yqDmhygjyhbNdhZtEMWuKg
/url?q=http://qiita.com/tags/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggzMAU&usg=AFQjCNF90oue6qE9iY7-7aX18l3_xbGTCg
/url?q=http://qiita.com/advent-calendar/2015/crawler&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFgg5MAY&usg=AFQjCNH1d8pi6VFqXc05SoGLgmnWA7sUxQ
/url?q=http://qiita.com/muran001/items/7a76a1c70aa12ed68cb6&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFgg-MAc&usg=AFQjCNFp7u_XDLi3XxSR3puErDpJ3TEHfw
/url?q=http://www.sophia-it.com/content/Web%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFghEMAg&usg=AFQjCNFwJP1ZWTJYT-PdMBKsWpM2L69prw
/url?q=http://tech.respect-pal.jp/web-scraping/&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFghKMAk&usg=AFQjCNHFBm-FNZQClN7ssKveEsjz3GD4eQ
/url?q=http://www.usamimi.info/~ryouchi/scraping/&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFghQMAo&usg=AFQjCNF5l0OwwjJCYUnJZTTiTXN9DZLQOQ
[7] pry(main)>

なんか、ようさん変なんが出てきた…大丈夫やろか(´・ω・`)

大鋸で切り出した部分を加工する

『検索結果のリンク』らしきものがたくさん出てきましたが、今のところ『検索結果のリンク』は1つあれば事足りるので、最初の値だけ取得します。

[7] pry(main)> link = doc.xpath('//h3/a').first.get('href')
=> "/url?q=https://ja.wikipedia.org/wiki/%25E3%2582%25A6%25E3%2582%25A7%25E3%2583%2596%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggYMAE&usg=AFQjCNHZAORThLyskf3nDxY9SlIMZY3Mvg"
[8] pry(main)>

取得した値を見てると、/url?q=が記述されていて、https://www.google.co.jpとかを付け加えたら『検索結果のリンク』になりそうな気がしてきたので、試してみます。

[8] pry(main)> process_link = "https://www.google.co.jp" + link
=> "https://www.google.co.jp/url?q=https://ja.wikipedia.org/wiki/%25E3%2582%25A6%25E3%2582%25A7%25E3%2583%2596%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwjr7M-nzNDLAhWClZQKHVtnDdQQFggYMAE&usg=AFQjCNHZAORThLyskf3nDxY9SlIMZY3Mvg"
[9] pry(main)> process_link_html = open(process_link) do |f|
[9] pry(main)*   f.read
[9] pry(main)* end  
=> "<!DOCTYPE html>\n<html lang=\"ja\" dir=\"ltr\" class=\"client-nojs\">\n<head>\n<meta charset=\"UTF-8\" />\n<title>
ウェブスクレイピング - Wikipedia</title>\n<script>document.documentElement.className = document.documentElement.className.replace(
 /(^|\\s)client-nojs(\\s|$)/, \"$1client-js$2\" );</script>\n<script>(window.RLQ = window.RLQ || []).push(fu"
[10] pry(main)> 

やった!リンクが開けた!適当に思いつきやってみたらうまくいったヽ(・ω・)/

大鋸を使ったWebスクレイピング

上述のpry*1で試した手順で、なんとか、『検索結果のリンク』を取得することが出来ましたので、これに手を加えて、スクリプトファイルにまとめてみます。

# gugulu.rb
require 'open-uri'    # URLを開く
require 'Oga'         # HTMLやXMLをパースする
require 'cgi'         # 日本語をURLエンコード

google_url = "https://www.google.co.jp"

# Google検索開始
loop do
  # 検索文字列をURLエンコードして取得
  # 文字の入力が無ければ検索を終了
  print("Search Word: ")
  search_word = CGI.escape(gets.chomp)
  break if search_word.empty?

  # Google検索の結果のリンクを作成
  begin
    link = []
    # 検索結果のURLからHTMLを取得
    url = "#{google_url}/search?q=#{search_word}"
    html = open(url) do |f|
      f.read
    end

    # HTMLをパースしてhref属性の値を取得
    doc = Oga.parse_html(html)
    doc.xpath('//h3/a').each do |node|
      link.push(node.get('href'))
    end
    # 取得した値から検索結果のリンクを作成
    process_link = "#{google_url}#{link.shift}"
  rescue => e
    # Google検索ができなかった場合はエラーを表示
    puts("Search Error: #{e.message}")
  end

  # 検索結果のリンクを開く
  begin
    # 開いたリンクのURLを表示
    process_link_html = open(process_link) do |f|
      f.read
    end
    puts("Link Open Success: #{process_link}")
  rescue => e
    # リンクが開けない場合は次の値からリンクを作成
    puts("Link Open Error: #{e.message}")
    if link
      process_link = "#{google_url}#{link.shift}"
      retry
    end
    # 作成したリンクが全て開けない場合は諦める
    puts("Please Try Other Word")
  end
end

"動けば良い"の精神で作成しましたので、エラー処理とか推敲する余地がありまくりな気がしますが、こいつを使って『スクレイピング』を検索してみます。

$ruby gugulu.rb
Search Word: スクレイピング
Link Open Success: https://www.google.co.jp/url?q=https://ja.wikipedia.org/wiki/%25E3%2582%25A6%25E3%2582%25A7%25E3%2583%2596%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AC%25E3%2582%25A4%25E3%2583%2594%25E3%2583%25B3%25E3%2582%25B0&sa=U&ved=0ahUKEwi6mtK7itHLAhVBFZQKHXqJDUYQFggYMAE&usg=AFQjCNHZAORThLyskf3nDxY9SlIMZY3Mvg
Search Word: 

おお、リンクが取得できた良かった(´;ω;`)

参考記事

RubyのWebスクレイピングXPathについては、こちらの記事を参考にさせていただきました。

開発環境

  • OSX 10.11.2 El Capitan
    - ターミナルエミュレータMacターミナル
    - シェル: zsh
    - パッケージマネージャ: Homebrew
  • Ruby 2.0.0p645
    - バージョンマネージャ: rbenv