ファイルとストリームで読み込む要素が変わる..訳がない

Tagsoup+SAXHandlerでHTMLをパースしているのだが、入力がFileの場合とSocket(サーバ)の場合で読み込むタグ要素に違いが出る現象が発生していた。

以下のような構造のHTMLがあったとしよう。(閉じるタグは省略)

<body>
<table>
  <tbody>
  <tr>
    <td>
      <table>
  <tr>
    <td>
      <table>

これがファィルに格納されている場合、真っ当に全てのタグ要素をパースしてくれるのだが、全く同じHTMLをWebサーバから読み込むと、

<body>
<table>
  <tr>
    <td>
      <table>

と、すっぽりとtbodyタグ以下が抜け落ちるのだ。
これは一大事、とんでもないバグだと調べていたのだが、SAXハンドラのログを採取したらすぐに分かった。

  • charactersハンドラ
public void characters(char[] ch, int start, int length) throws SAXException {
    Log.d(AttendantService.class.getSimpleName(), "start characters = [" 
            + new String(ch, start, length) + "]");
}
  • 実行結果
D/AttendantService(  452): start characters = [
D/AttendantService(  452): java.lang.NumberFormatException: For input string: ""
D/AttendantService(  452):  at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
D/AttendantService(  452):  at java.lang.Integer.parseInt(Integer.java]
:
:

なんてことはない、サーバ側にリクエストしたパラメタのせいで例外を発生、スタックトレースがレスポンスボディとして戻っていたのである。

SAXハンドラを使う場合全てのメソッドをオーパライドするのは面倒なので、DefaultHandlerを使用して使うハンドラ(startElementやendElement)だけをオーバライドすることが多いのだが、そうすると今回のようにサーバ側が不意に返した文字列を全く捕まえられなくなることをを注意しなくてはならない。これは決められた応答、エラーや例外をハンドルできないスクレイピングの場合、非常に重要だ。

結局、原因はクエリパラメタに渡した数値が2桁ではなく1桁だった為なのだが...月の判定なんだから1桁でも食うようにしておけよと。あと、普通にスタックトレースをサーバレスポンスに流すなよなぁ。