はじめに
以前、技術評論社さんのデータサイエンティスト養成読本という本を読みました。
いろいろな技術の紹介があり、非常に参考になりました。
その中でスクレイピングに関するサンプルコードもありました。
今回はPythonで書かれていたスクレイピングのコードをRubyで書き直した記事になります。
なお、Pythonで書かれたサンプルの方は実際に書籍をご覧になってください。
仕様
技術評論社さんのサイトに行き、直近の記事タイトルとそのリンクを取得してJSONで吐き出します。
ソースコード
実際に書いたソースコードはこちらになります。
その後、各項目について記載したいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | require'uri' require'open-uri' require'nokogiri' require'json' classGihyoCrawler BASE_URL='http://gihyo.jp'.freeze CONTENT_LIST_URL='http://gihyo.jp/contentslist'.freeze TITLE_XPATH='//*[@id="latestArticle"]/dl/dd/h3/a'.freeze NEXT_URL_XPATH='//*[@id="latestArticle"]/div[4]/p[2]/a'.freeze OUTPUT_FILE='output.json'.freeze PAGE_NUM=2 SLEEP_TIME=1 defself.run url=CONTENT_LIST_URL File.open(OUTPUT_FILE,'w:utf-8')do|f| f.write("{\n") PAGE_NUM.timesdo dom=read_html(url) scrape_content_page(dom).eachdo|title,url| data={"title":title,"url":url} f.write(JSON.dump(data)+",\n") end url=get_next_url(dom) breakifurl.nil? sleep(SLEEP_TIME) end f.write("}") end end defself.read_html(url) charset=nil user_agent={"User-Agent"=>"Gihyo Bot"} html=open(url,user_agent)do|f| charset=f.charset f.read end Nokogiri::HTML.parse(html,nil,charset) rescueStandardError=>e p"Error: #{e}" end defself.scrape_content_page(dom) ret_arr=[] dom.xpath(TITLE_XPATH).eachdo|dom_by_xpath| title=get_title(dom_by_xpath) url =get_article_url(dom_by_xpath) ret_arr<<[title,url] end ret_arr end defself.get_title(dom) dom.inner_text end defself.get_article_url(dom) url=dom.attribute('href').value URI.join(BASE_URL,url).to_s end defself.get_next_url(dom) next_url=dom.xpath(NEXT_URL_XPATH)[0].attribute('href').value returnURI.join(BASE_URL,next_url).to_s rescue returnnil end end GihyoCrawler.run |
ファイル名を指定してruby
コマンドで実行できます。
1 | $ruby gihyo_crawler.rb |
使用したモジュール、Gem
対象ページを取得
open-uriのopenメソッドを使用し、対象URLを開いて取得します。
その際、オプションとしてUser Agentが渡せるので、クローラーだと分かるような名前をつけてあげます。
取得したページはNokogiriでHTMLとして解析して返却します。
1 2 3 4 5 6 7 8 9 10 11 | defself.read_html(url) charset=nil user_agent={"User-Agent"=>"Gihyo Bot"} html=open(url,user_agent)do|f| charset=f.charset f.read end Nokogiri::HTML.parse(html,nil,charset) rescueStandardError=>e p"Error: #{e}" end |
XPATHから目的のものを抜き出す
Google Chromeなどを使用すれば、対象のHTMLタグのXPATHを取得することができるので、そちらから目的のものを取得します。
XPATHの説明は、下記の方の記事が分かりやすいかと思います。
同じ階層の、例えば<div>
などは配列で返ってくるので、ループして必要なだけ抽出します。
記事タイトル、リンク先それぞれ取得の方法が違うので、メソッドを用意してあげます。
1 2 3 4 5 6 7 8 9 | defself.scrape_content_page(dom) ret_arr=[] dom.xpath(TITLE_XPATH).eachdo|dom_by_xpath| title=get_title(dom_by_xpath) url =get_article_url(dom_by_xpath) ret_arr<<[title,url] end ret_arr end |
次のページのリンクを取得する
はじめに定数として、PAGE_NUM = 2
と与えています。
技術評論社さんの記事ページはページングされており、過去の記事を見るためには次のページへ行く必要があります。
このとき、次のページボタンのXPATHを指定し、そのDOMのURLを取得すれば、同様の処理を繰り返すことで次のページの記事も取得できます。
今回のサンプルでは、何ページ目まで取得するか、という意味でPAGE_NUM = 2
が与えられています。
もしそれ以上過去の記事がなければ、リンクが取得できないはずなので、その場合はbreak
します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | defself.run # 省略 PAGE_NUM.timesdo dom=read_html(url) scrape_content_page(dom).eachdo|title,url| data={"title":title,"url":url} f.write(JSON.dump(data)+",\n") end # 次のページへのリンクを取得 url=get_next_url(dom) # もし取得できなければループを抜ける breakifurl.nil? # 連続してアクセスすると迷惑なので、次のループに入る前に一拍入れる sleep(SLEEP_TIME) end # 省略 end |
さいごに
スクレイピングの勉強がてら書き換えてみました。
Railsで素早くWebページを作成することは違うので、Ruby自体の勉強にオススメです。