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で作るついでに文字コードについて学んでみた。(あいまい)
RubyでCSVファイルを作り、Excelで開くと文字化けする。 解決法を探した所、この記事の通りやったら解決できた。
BOMなしUTF8をBOMつきUTF8にするためのワンライナー
解決できたはいいが、やっていることが全くわからないのでいろいろ調べてみる。
そもそもExcelで文字化けする理由は何か?
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 0201 – Wikipedia
JIS X 0208 – Wikipedia
補助漢字 – Wikipedia
JIS X 0213 – Wikipedia
Unicode – Wikipedia
コンピュータに認識できるようにいろいろな文字にラベルを貼っているらしい。そしてそのラベルを貼った文字の集合を符号化文字情報というとのこと。
コンピューター上では最終的には全てが0と1のデジタルで表現されます。もちろん文字もそうです。ですので符号化文字集合だけがあっても実際にそれをコンピューター上ではどのような0,1の並びで表現するのか、ということが決まっていないとコンピューターでは扱えません。その0,1の並びを定義しているのが文字符号化方式です。
(中略)文字符号化方式の具体例
ISO-2022-JP – Wikipedia
EUC-JP – Wikipedia
Shift_JIS – Wikipedia
Microsoftコードページ932 – Wikipedia
UTF-8 – Wikipedia
UTF-16 – Wikipedia
UTF-32 – Wikipedia
そしてラベルが貼ってある文字集合をコンピュータが読み込めるようにする方式を定義してあるのが、文字符号化方式らしい。UTF-8やShift_JISが含まれるとのこと。
つまり文字を整理した文字集合と、文字集合をコンピュータに読み込ませるための方式の2つに規格があるらしく、Excelが文字化けした今回のケースは、文字集合をUTF-8方式でコンピュータに認識できるように表現しなくてはいけないのにShift_JISで読みこんだから文字化けしたということのようだ。
この図はわかりやすい。
ちなみに名前だけよく聞くUnicodeは世界で使われる全ての文字を共通の文字集合にて利用できるようにしようという考えでつくられているらしい。
Unicode は世界で使われる全ての文字を共通の文字集合にて利用できるようにしようという考えで作られ、Unix、Windows、Mac OS X、Plan 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)
エラー内容を確認してみる。
文字列であるべきところにハッシュが入っているよーとのこと。
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を作成し、実行した所問題なく処理ができた。
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が付いてるかどうかを確認できる。
宿泊予約サイトの情報を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(",") } }