【Ruby】WebAPIからxmlをhttps通信でgetする時文字化けした。その対象方法(REXML::ParseException invalid byte sequence in UTF-8)

プログラミング,Ruby

「文字コードで苦しんだ」というエントリです。

公開していたwebサービスが動いていないという連絡を受けてから、ずっと悩んでいました。

REXML::ParseException(#<ArgumentError: invalid byte sequence in UTF-8)の対処方法

動いていた自作のアプリが動かなくなった

RubyとHerokuを使って自作のアプリを運営していました。

「ちえのわ」というサジェストキーワード検索サービスです。SEO対策用のものですね。

しかし今年に入って表示がうまくいかない現象が発生していました。

パソコンを買い替えていたのでherokuやgitの設定に時間がかかってしまったのが復旧遅れの原因です。ご迷惑おかけしてしまい申し訳ございません。

無事gitからcloneして、ローカルで動かせる環境ができたのですが、エラーの内容を見てみるとREXML:ParseEceptionというエラーが吐かれていました。

サービスの中でGoogle サジェストAPIを叩いているのですが、返ってくるxmlをパース(解析)する際に何らかのエラーがでてるよというものでした。

なぜパース(解析)エラーがおきたのか

REXML::ParseException が起きたxmlファイル

返ってきたxmlを出力してみると原因がわかります。

原因は文字コードにありました。

suggestion dataに�(黒いダイヤのはてな)が含まれていますね。 黒いダイヤのはてなはリプレイスメントキャラクターといいます。リプレイスメントキャラクターとは、文字コードを変換した際に対応するような文字がない時に自動で変換される文字列のことです。要するに意味のない文字列ですね。なのでこのリプレイスメントキャラクターを含む文字コードをデコードすることはできません。

なので返ってくるxmlファイル自体にこのリプレイスメントキャラクターが含まれていると、エンコードして元の文字列に戻すことができないということです。

困った困った・・・。

解決方法:Nokogiriでxmlを取得する。

まずはエラーが出ていたコードを掲載しておきます。

url2 = URI.escape('https://www.google.com/complete/search?hl=ja&output=toolbar&ie=utf_8&oe=utf_8&q=' + params['input1']+' ')
url = URI(url2)
https = Net::HTTP.new(url.host,443)
https.use_ssl = true
https.ca_file = 'GTE_CyberTrust_Global_Root.pem'
https.verify_mode=OpenSSL::SSL::VERIFY_NONE
https.verify_depth = 5
response = https.get(url)

実際に「ちえのわ」で使われていたgetのコードです。net/httpでgetしています。

今回の問題はgetしたあとのコードをエンコードすることではなく、文字化けが起きない文字コードでgetすることがエラー回避をするために必要でした。

残念ながら、net/httpで文字コードを指定してgetする方法を見つけられませんでした。(もしわかる方がいましたらtwitterまで連絡もらえるとすごく嬉しいです!)

なので解決策として、Nokogiriを使うことに決めました。

doc = open(url,{:ssl_verify_mode => OpenSSL::SSL::VERIFY_NONE}).read
doc= doc.toutf8 

10行近かったコードが2行にまとまりました。シンプルで気に入っています。

ここでnet/httpとNokogiriで取得した時の文字コードを見比べてみます。

net/httpで取得
Nokogiriで取得

net/httpで取得するとなんと返ってきたxmlがASCII-8BITという文字コードがになってしました。

原因が知りたくて、ググって見るとどうやらデフォルトの文字コードは決まっていないという情報がちらほらありました・・・。

Rubyの内部文字コードはUTF-8ではない…だと…?!

対してNokogiriで取得するとちゃんとShift_JISで取得してくれていますね。どうやらRubyはデフォルトの文字コードが決まっていないので、それらしい文字コードに自動で変換してくれるらしいです。ここらへんは詳しく調べていないのでなんともですが、Nokogiriのデフォルトの文字コードがShift_JISなのかなと考えています。

ただ今まで動いていたわけですから、他にもなにか原因があるはずです。

今年になってから動かなくなった原因についてですが、消去法で考えるにAPI側の仕様変更が濃厚かなと思っています。私が使っているRubyのバージョンをあげたとかそういったことをしていなかったので、外部要因によるものと仮説立てしています。

注意

結果的にはWebAPIからxmlを取得する際、Nokogiri使うべきだと結論にします。

今後RubyでなにかAPIを叩く際は注意するようにしておきます。

ただし、net/httpでも取得する際に文字コードが指定できると思います。指定できないとこのようなことが結構起こりそうです。

Rubyの文字コードについて結構ハマりそうな人が多いと思うのですが、いかんせんここの情報がありませんでした。

何かしらわかったら追記しておきたいと思います。

以上です。

最近は電子書籍の参考書で勉強しています。最初は参考書は紙じゃないと嫌だと思っていましたが、慣れるととても楽です。

読書のための専用端末 - E Ink(イーインク)ディスプレイで、紙のように読みやすい。直接目を照らさないフロントライト方式だから、目に優しく、長時間の読書でも疲れにくい。本数千冊(一般的な書籍の場合)がこの一台に