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)のサンプル

SGMLReaderToXDocumnet

突然のException!

このサンプルの

xml = XDocument.Load(sgml);

部分で例外がでました。

クラスライブラリ内のソースなので、デバッグすることができなくて困っていたのですが、

github.com

GitHubにプロジェクトページがあって、ライブラリ参照している部分をソースに差し替えることで、SGMLReaderの処理にステップして問題箇所を探していきました。

 

Decoderが無効なバイトを<?>(65533)に変換している部分が問題

stackoverflow.com

SgmlParser.csに、HTML/XMLの変換部分を担当してるHtmlStreamクラスがあって、そのバッファからReadしてきた文字が文字化けたナニカを返しているのが気になりました。調べると、DecoderクラスのUnicodeDecoderの既定の動作。

Unicode デコーダーでは、デコードできない 2 バイトのシーケンスが REPLACEMENT_CHARACTER (U+FFFD) に置き換えられます

.NET Framework における文字エンコーディング

これを止めれば件の例外は止まるはず。

 

デコーダの置換フォールバックを止める

SgmlParserのデコーダ置換フォールバックを指定する

 

止める。というよりは、REPLACEMENT_CHARACTER (U+FFFD)じゃなくて空文字に置換するようにして回避できました。