RubyのBenchmarkモジュールを使って、sleepメソッドが効いているか確認してみる。

open-uriとnokogiriを使ってホテル予約サイトをスクレイピングしているが、sleepメソッドがきちんと効いているか気になったので、Benchmarkモジュールを使って計測してみた。

方法

sleepメソッドを含むメソッドと含まないメソッドを2種類作る。

コード

Benchmark.bm(15) do |x| #sleepありの詳細ページ情報メソッド
  x.report "hotel_infor" do
    push_hotel_information_detail(row)
  end
end
Benchmark.bm(15) do |x|#sleep無しの詳細ページ情報メソッド
  x.report "hotel_infor_no" do
    push_hotel_information_detail_no_sleep(row)
  end
end

処理はホテル予約サイトの情報を取得して配列に入れいている。

結果

                      user     system      total        real
hotel_infor       0.150000   0.010000   0.160000 (  4.790870)
                      user     system      total        real
hotel_infor_no    0.170000   0.000000   0.170000 (  1.675842)

わかりやすく3秒スリープを入れてみたが、動作を確認できた。

参考記事。 Rubyでベンチマークをとる

Excelで文字化けせずに開けるCSVをRubyで作るついでに文字コードについて学んでみた。(あいまい)

RubyCSVファイルを作り、Excelで開くと文字化けする。 解決法を探した所、この記事の通りやったら解決できた。

BOMなしUTF8をBOMつきUTF8にするためのワンライナー

解決できたはいいが、やっていることが全くわからないのでいろいろ調べてみる。

そもそもExcelで文字化けする理由は何か?

UTF-8 でエンコーディングされたCSVファイルを開く

UTF-8エンコーディングされたCSVファイルをExcelで開くと、下図のように文字が化けてしまう場合があります。Excelでは何も指定がない場合はファイルをShift-JISで開く動作のため文字化けが発生します。

指定がないとUTF-8エンコーディングされたCSVファイルをShift-JISで開いてしまうらしい。でBOMヘッダを付けてあげるとUTF-8エンコーディングされいているファイルとして認識してくれるらしい。BOMとは文字コードを認識するラベル用な印象?

そもそもUTF-8とかShift-JISとか規格が違うくらいのイメージしか無かったからこれを機会に少し学んで見る。

参考サイト
符号化文字集合と文字符号化方式 - 「プログラマのための文字コード技術入門」を読んだ
符号化文字集合と文字符号化方式の違い

まず符号化文字集合について。文字を表現するためには、まずは文字の集合が必要である。またコンピュータでそれを表すためには、それぞれの文字に一意な番号をふって置く必要がある。例えば、スペースなら32番、「a」なら97番という感じ。番号をふっておけば、その番号に従って、ディスプレイ上に文字を表示することができる。

符号化文字集合の具体例
ASCII – Wikipedia
JIS X 0201Wikipedia
JIS X 0208Wikipedia
補助漢字Wikipedia
JIS X 0213Wikipedia
UnicodeWikipedia

コンピュータに認識できるようにいろいろな文字にラベルを貼っているらしい。そしてそのラベルを貼った文字の集合を符号化文字情報というとのこと。

コンピューター上では最終的には全てが0と1のデジタルで表現されます。もちろん文字もそうです。ですので符号化文字集合だけがあっても実際にそれをコンピューター上ではどのような0,1の並びで表現するのか、ということが決まっていないとコンピューターでは扱えません。その0,1の並びを定義しているのが文字符号化方式です。
(中略)

文字符号化方式の具体例
ISO-2022-JPWikipedia
EUC-JP – Wikipedia
Shift_JISWikipedia
Microsoftコードページ932 – Wikipedia
UTF-8Wikipedia
UTF-16Wikipedia
UTF-32Wikipedia

そしてラベルが貼ってある文字集合をコンピュータが読み込めるようにする方式を定義してあるのが、文字符号化方式らしい。UTF-8Shift_JISが含まれるとのこと。

つまり文字を整理した文字集合と、文字集合をコンピュータに読み込ませるための方式の2つに規格があるらしく、Excelが文字化けした今回のケースは、文字集合UTF-8方式でコンピュータに認識できるように表現しなくてはいけないのにShift_JISで読みこんだから文字化けしたということのようだ。

f:id:rootjun:20160325134329j:plain 符号化文字集合と文字符号化方式 - 「プログラマのための文字コード技術入門」を読んだ

この図はわかりやすい。

ちなみに名前だけよく聞くUnicodeは世界で使われる全ての文字を共通の文字集合にて利用できるようにしようという考えでつくられているらしい。

Unicode

Unicode は世界で使われる全ての文字を共通の文字集合にて利用できるようにしようという考えで作られ、UnixWindowsMac OS XPlan 9[2]、Javaなどで利用されている。

Ruby "no implicit conversion of Hash into String (TypeError)" BOM付きファイルを読み込んだ時に起きるエラー

require 'open-uri'
require 'nokogiri'

UserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36'

File.open("scraping_hotels7.csv"){|f|
  f.each do |url|
    url.chomp!
    doc = Nokogiri::HTML(open(url,'User-Agent' => UserAgent))
    puts doc
    sleep 1
  end
}
/Users/user_name/.rbenv/versions/2.1.4/lib/ruby/2.1.0/open-uri.rb:36:in `initialize': no implicit conversion of Hash into String (TypeError)

エラー内容を確認してみる。

TypeError

文字列であるべきところにハッシュが入っているよーとのこと。

Nokogiriで解析するために集めたURLをリスト化したCSVを開いているが、ゴミがありそうな項目はない。 いろいろと調べていると、CSVを作成する際に文字化けせずにExcelでも開けるようにBOMを入れた事を思い出す。

Excel(エクセル)で文字化けしないUTF-8のCSVファイルを出力する方法

BOMが付いていない場合は、シフトJISと解釈して開いてしまうようで、そのため文字化けします。

BOMはfile コマンドを使って調べることができる。

$ file scraping_hotels7.csv 
scraping_hotels7.csv: UTF-8 Unicode (with BOM) text, with very long lines

BOMを除いたCSVを作成し、実行した所問題なく処理ができた。

参考記事:Excelで文字化けせずに開けるCSVをRubyで作るついでに文字コードについて学んでみた。(あいまい)

Rubyで文字列から半角数字だけを抽出する方法。

"文字列".gsub(/[^0-9]/,"")

文字クラスの [ の直後の文字がキャレット(^)である場合、列挙「されていない」 文字にマッチするようになります(これは否定文字クラスと呼ばれます)。 Ruby 正規表現 文字クラス

[^0-9]で0-9以外の文字列を抽出して、""と置換しています。

find コマンドでの BOM の確認方法

fileコマンドを使えば、わざわざテキストエディタを使わずに確認できるよ!

$ file *.cpp
foo.cpp:   UTF-8 Unicode text
bar.cpp:   UTF-8 Unicode (with BOM) text

BOMが付いてるテキストは「(with BOM)」となるので、エディタなどで開かずにBOMが付いてるかどうかを確認できる。

コマンドラインでUTF-8テキストのBOMを追加したり削除したりする

宿泊予約サイトの情報をopen-uriで開いて、nokogiriで解析してみる。

Booking.com: 東京のホテル. 今すぐホテル予約!

require 'kconv'
require 'open-uri'
require 'nokogiri'

UserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36'
doc = Nokogiri::HTML(open('http://www.booking.com/searchresults.ja.html?label=gen173nr-1DCAEoggJCAlhYSBViBW5vcmVmaHWIAQGYARW4AQfIAQzYAQPoAQH4AQKoAgM;sid=feb3e89b7a094231eeb4d815e4f6d9b7;dcid=12;checkin_monthday=03&checkin_year_month=2016-05&checkout_monthday=04&checkout_year_month=2016-05&dest_id=-246227&dest_type=city&from_history=1&group_adults=2&group_children=0&have_hh=1&no_rooms=1&si=ad&si=ai&si=ci&si=co&si=di&si=la&si=re&;link_search_history=1&tfl_cwh=1','User-Agent' => UserAgent))


File.open("scraping_hotels.csv", "w:utf-8", ){|f|
f.print "\uFEFF"
doc.css('.sr_item').each{|row|
  line_ary = []
  line_ary.push(row.css(".hotel_name_link").first.text)
  line_ary.push(row.css(".score_from_number_of_reviews").first.text)
  if row.css('.smart_price_style').length > 0 then
    line_ary.push(row.css(".smart_price_style").first.text)
  end
  f.puts line_ary.map{|e|"\"#{e.gsub("\n","")}\""}.join(",")
}
}