DirtyなHTMLなのにXHTMLと名乗っているページをC#でスクレイピングする

C#でスクレイピング - DENKENを参考にやってみた。

元ネタはGoogleが吐いたHTMLを処理してたので(比較的キレイなHTMLなんだろう)、HTMLtoXHTMLは無事にXHTMLに変換できていたが、とある有名なブログのページを変換しようとしたら、XHTMLの宣言部が下記のように見事に壊れて、XDocument.Parse(xhtml)でXmlExceptionで落ちる。

<?xml version="1.0" encoding="Shift_JIS"="="  ?>
<!DOCTYPE html="html" PUBLIC="PUBLIC" -="-"  xmlns="http://www.w3.org/1999/xhtml"  />
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja" xmlns:xml="urn:xml" >

XHTMLの宣言部を削って変換を掛けたらうまくいった。

// original: http://d.hatena.ne.jp/uesama99/20080219/1203394007

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using HTML2XHTMLLib;
using System.Net;
using System.IO;

namespace ScrapingSample
{
    class Program
    {
        static void Main(string[] args)
        {
            WebClient wc = new WebClient();
            string html = wc.DownloadString("http://someblog.com/somepage.html");

            html = html.Replace("<?xml version=\"1.0\" encoding=\"Shift_JIS\"?>", "");
            html = html.Replace("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">", "");
            html = html.Replace("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"ja\" lang=\"ja\">", "<html>");

            XHTMLUtilities util = new XHTMLUtilities();
            string xhtml = util.convertToXHTML(html);

            const string XHTML = "{http://www.w3.org/1999/xhtml}";

            XDocument xdoc = XDocument.Parse(xhtml);
            var query = from a in xdoc.Descendants(XHTML + "a")
                        where a.Attributes("class").FirstOrDefault(atr => atr.Value == "l") != null
                        select a;

            foreach (var item in query)
            {
                Console.WriteLine("Title={0}", item.Value);
            }

            Console.WriteLine("完了しました。");
            Console.Read();

        }
    }
}

XHTMLじゃないのにXHTML宣言をするのは有害」だと、どこかのページで見たが、ここでもそれが見事に当てはまってる。

LINQのお勉強しなきゃw

HTML特殊文字のエスケープ処理

いわゆる「サニタイズ」です。

仕事で検索文字列の強調表示処理(Googleのキャッシュ表示みたいなやつ)を作ってたのですが、記号は全て無視して検索する仕様だったので強調用に挿入したspanタグの不等号を含めて強調してしまうというバグを作りこんでしまい、HTML特殊文字のエスケープ処理について調べてみました。

とりあえずの資料はhttp://www.asahi-net.or.jp/~wv7y-kmr/note/2002-05.html
考え方はhttp://takagi-hiromitsu.jp/diary/20051227.html

改修方法は、DBから取り出した強調表示対象の文字列を丸ごとエスケープ処理し、その結果を強調してspanタグを挿入するというふうにしました。

全体を俯瞰した改修方法が取れたのは、高木浩光さんのおかげです。m(_ _)m

parseInt()とゼロパディング10進数

懸命なみなさまは、タイトルだけでオチが読めちゃうかもしれませんが・・・

JavaScriptで"hh:mm"フォーマットの時刻(ただし、時間・分とも1桁を許容)をそれぞれ2桁に整形する処理を書いていたときのお話です。

"09:40"を整形しようとしてparseInt("09")をやってみるとゼロが返ってくる!!!
ぐぐってみると、http://www.b-s-c.co.jp/~moritake/oboegaki/h_js_clm03.htmlとか見つかりました。
parseInt()の第二引数を省略した場合は、プレフィックスをもとに基数は自動認識なんですね。16進数が"0x"で8進数が"0"の例のやつ。
第二引数を省略した場合は、ディフォルト値=10扱いになると勘違いしてました。(;_;)

類似障害見直しで第二引数なしのparseInt()をgrepしてみたら500件近く見つかりました。(^^;
なんと、RemoteScriptのScriptLibraryの中からも!

まぁ、ゼロパディングされていない10進数を扱う分には問題ないんですけどねー。
新たな火種を見つけてしまい、がっくしです。orz