C# SgmlReaderの内部でDecoderクラスが「デコードできない 2 バイトのシーケンスを REPLACEMENT_CHARACTER (U+FFFD) に置き換え」て例外が発生するケースがあってハマった。と、その回避方法
業務で、さくっとWebスクレイピングを書くときに、C#をつかってます。
neue cc - C#でスクレイピング:HTMLパース(Linq to Html)のためのSGMLReader利用法
NuGetからSGMLReaderライブラリをインストールして、数行でお手軽にスクレイプできるので重宝するんですよね。
たったこれだけで、Webページ→HTML→XDocumentができるので、生産性が高い理由がわかる(同じ行数でC++なら受信も終わってない)。
HTML→XML(XDocument)のサンプル
突然のException!
このサンプルの
部分で例外がでました。
クラスライブラリ内のソースなので、デバッグすることができなくて困っていたのですが、
GitHubにプロジェクトページがあって、ライブラリ参照している部分をソースに差し替えることで、SGMLReaderの処理にステップして問題箇所を探していきました。
Decoderが無効なバイトを<?>(65533)に変換している部分が問題
SgmlParser.csに、HTML/XMLの変換部分を担当してるHtmlStreamクラスがあって、そのバッファからReadしてきた文字が文字化けたナニカを返しているのが気になりました。調べると、DecoderクラスのUnicodeDecoderの既定の動作。
Unicode デコーダーでは、デコードできない 2 バイトのシーケンスが REPLACEMENT_CHARACTER (U+FFFD) に置き換えられます
これを止めれば件の例外は止まるはず。
デコーダの置換フォールバックを止める
止める。というよりは、REPLACEMENT_CHARACTER (U+FFFD)じゃなくて空文字に置換するようにして回避できました。