文字列を返すJavaのHttpClientライブラリ
ブラウザなどのHttpClientではGET後に文字列として解釈するために主に以下の2つの方法でcharsetを順に調べる。
- ヘッダからcharsetを得る :
Content-Type: text/html; charset=utf-8
- 無い場合には、
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=euc-jp">
のようなタグからcharsetを得る
HttpClientライブラリが文字列を返す際には、このような情報で得たcharsetを使ってデコードした文字列を返して欲しい。 一方、特に後者は、取得したバイト列を何らかのcharsetで文字列にした上でcharsetを探す必要があるため、若干のコストがかかるので、HttpClientライブラリの範疇を超えているのでは、といった懸念点もある。
HttpClientライブラリには文字列をレスポンスボディとして返すものが多いと思うが、それらのライブラリでは文字列化にあたりどのような処理をしているのか見る。
各種ライブラリ検討
google-http-java-client (1.21.0-SNAPSHOT)
- ヘッダのContentTypeを取るが、無ければリクエストに予め付けていたContentTypeヘッダから取得
- 文字列として取得する際には、レスポンスヘッダとリクエストヘッダしか見ずに、html中のmetaタグは見ないようだ
okhttp (2.5.0-SNAPSHOT)
okhttp、読み込む対象を裏で動いてるワーカに対するキューに対して突っ込むみたいな実装だった
retrofit (2.0.0-SNAPSHOT)
okhttpの実装に基づく
AsyncHttpClient (2.0.0-SNAPSHOT)
jsoup (1.8.2)
- Jsoup.execute(): Response
Jsoup.parse(html)
は当然レスポンスヘッダが無いので、メタタグからcharsetを探すのみの処理を行う
ついで
Scalaのhttpクライアントも少し見る
dispatch (0.8.10)
- ヘッダから探し、無ければリクエストのものを利用する
- google-http-java-clientと同じ
skinny-http-client (1.3.18)
- ヘッダから探し、無ければリクエストのものを利用する
- google-http-java-clientと同じ
Scalaのコア機能のみでやる場合
「HTMLスクレイピング in Scala」を改造しながら俺流Scalaコーディング手法を紹介してみる - ( ꒪⌓꒪) ゆるよろ日記
結び
思いの他metaタグまで見て文字列にしているライブラリは少なかった。利用者側からしたら文字列が欲しいのは分かるが、世の中にはそううまく行かないサーバも存在する。ヘッダにcharsetが無く、metaにeuc-jpと書かれたページを文字化けせずに取ってこれるライブラリは正しい挙動になってると言える。
今回の例だと、最もシンプルに取るならjsoupでJsoup.get().html()
とするのが簡単に行えるものになる。まぁ実際は、execute()
でResponse取得後、charsetがあればbody
を、無ければresponse.parse(body).html
の結果を返すのが良いのだが。JsoupもともとDOM解析のような用途に用いられるものだし、この辺もそこそこ(使い方にもよるが)ちゃんとしている雰囲気を感じる。*1
apache HttpClientのように初めからbyte列を返し、文字列にする過程はHttpClientのライブラリがする範疇ではないと決めつけてしまうのも1つの手だろう。(HttpClientも文字列返す実装あるな...なんか勘違いしてた...)まぁ通常叩くドメインなども限られているし、取りに行くドメインさえ軽く調べて問題なければより柔軟性のある便利なライブラリを使った方が良いのだが...