昨日書いたエントリでふれたようにサーバ側のJSON生成部分から見直すことにするため028-Crawlerは一応完成というエントリで触れたCrawlerから手をつけました
これまでのCrawlerと今後実装するべき処理検討
あすなろブログの各エントリ情報を取得する際に
- ブログタイトル
- ブログ本文
- permalink
は取得していたけれど、投稿日を抽出してなかったのでその処理を実装することにしました
あすなろブログは、当然CMS使って管理しているのですが、投稿日の情報取得が結構やりづらい構造になっています。
具体的に016:Lifestyle アディダスのランニングシューズの期間限定レンタルサービス登場というエントリの該当箇所を引用するとこんな感じになってます。
<p class="posted"> 投稿者 : 小山田 浩 | 投稿日時 : 2011.04.05 07:10 </p>
上記が仮にこうなっていたらとっても処理がラクだったのですがこればっかりは嘆いていても仕方が無いので、実装することにしました。
<div class="blogger_name">小山田 浩</div> <div class="post_date">2011/04/05 7:10</div>
投稿日の処理試行錯誤
投稿日のフォーマット見る限り、正規表現を使わないとダメだと思ったのですが、正直正規表現は苦手なのでうまく出来るかどうか自信がありませんでした
そのためirbで、試行錯誤しながら作業を進めていきました。
最初考えたのは、エントリ中から、投稿日の部分を抜き出す所。
Ruby+Nokogiriで
require 'nokogiri' require 'open-uri' starturl = "http://blog.pasonatech.co.jp/counselor/career_blog/102/15947.html" http = open( starturl, "User-Agent" => "MySpider", "From" => "xxxxx@gmail.com", "Referer" => "http://d.hatena.ne.jp/h5y1m141" ) doc = Nokogiri::HTML(http) doc.search('//p[@class="posted"]').to_html.match(/\d{4}\.\d{2}\.\d{2}.*/i)
とすることで最終的には取得出来ましたが、1つはまったのが正規表現マッチさせる所。
Nokogiri使って得られた結果をString型にしないとmatch()が利用できないみたいで、最初は
doc.search('//p[@class="posted"]').match(/\d{4}\.\d{2}\.\d{2}.*/i)
としてしばらく悩んでいました。
次に考えたのは投稿日のフォーマット変換
投稿日が
yyyy.mm.dd<空白スペース>hh:mm
となっているため、これだと汎用的に利用しづらいと思ったので、扱いやすい形に変換する必要があると思ってirbで確認しながらひとまずこんな処理を思いつきました
# 〜中略〜 temp_post_date = doc.search('//p[@class="posted"]').to_html.match(/\d{4}\.\d{2}\.\d{2}.*/i) yyyymm = temp_post_date.to_s.split(".") dd = yyyymm[2].to_s.split("\302\240") hhmm = dd[1].to_s.split(":") post_date = Time.local( yyyymm[0].to_s, yyyymm[1].to_s, dd[0].to_s, hhmm[0].to_s, hhmm[1].to_s, "00" )
これでひとまずイメージ通りにできそうなので、実際にCrawlerを修正しました
最終的なソースコード
require 'rubygems' require 'nokogiri' require 'open-uri' require 'entry.rb' class Crawler def initialize(blogger) @blogger = blogger end attr_accessor :html_body, :next_link, :title, :current_page def run(starturl) entry = Entry.new() http = open( starturl, "User-Agent" => "MySpider", "From" => "xxxxx@gmail.com", "Referer" => "http://d.hatena.ne.jp/h5y1m141" ) doc = Nokogiri::HTML(http) entry.permalink = starturl entry.blogger = @blogger entry.title = doc.search('//div[@class="entry"]').search("h1").text entry.html_body = doc.search('//div[@class="entryText"]').inner_html entry.post_date = self.conv_date_formt(doc.search('//p[@class="posted"]').to_html) entry.save result = doc.search('//p[@class="entrylink"]') if result.text.include?("次のエントリー»") then @next_link = result.search("a")[1]['href'] else @next_link = nil end if @next_link === nil then exit else sleep(2) self.run(@next_link) end end def conv_date_formt(contents) temp_post_date = contents.match(/\d{4}\.\d{2}\.\d{2}.*/i) yyyymm = temp_post_date.to_s.split(".") dd = yyyymm[2].to_s.split("\302\240") hhmm = dd[1].to_s.split(":") post_date = Time.local( yyyymm[0].to_s, yyyymm[1].to_s, dd[0].to_s, hhmm[0].to_s, hhmm[1].to_s, "00" ) return post_date end end