« 涼宮ハルヒの動揺:谷川 流 | トップページ | 密漁と中国四千年と首狩り族 »

2005年6月 4日

µTidylibを使ってみる

Python での HTML の処理ですが「よく考えたら直接テキストに変換するより一旦 Python のデータ構造にしてから処理する(つまり「Python のリテラル形式を使って HTML を作る」の逆をやる)ようにすれば応用が利いて便利じゃないか」と気付きました。

しかしそういう事に使えそうなモジュール htmllib が何やっているのかソースを見てみたらば……これ、テキストファイルに変換するのにしか使えないです。つまり「省略されたタグを補う」事をせず「省略可能なタグは(もし有っても)省略されている物として扱う」という実装。なので表示される段階で辻褄が合っている(ように見える)だけであって HTML の要素を認識しているわけではなく。ついでに言えば「属性値の表記に文字参照が有っても処理しない」等のバグもありますし。(これは HTMLParser も同じ)

いっそ自分で一から書こうかと思ったのですが HTML(SGML) のルールを調べるのが面倒でかつそれをコードに落とすにも無駄に手間がかかります。それに(ここも含め)そもそも世の HTML の大半はその HTML のルールに従っていないし。

車輪の再発明は止めだ!

という事で HTML Tidy を Python から使うライブラリ µTidylib を弄る事にしたのでした。以下はそのサンプルコード。

import sys
import xml.dom.minidom
import tidy
import urllib
ERRORS_OUT = True # Error を表示するなら WARNINGS_OUT = True # Warning を表示するなら def convert(html):
    u'''
    utf-8 文字列か Unicode文字列の HTML を正しい XHTML に変換して
    utf-8 文字列として返す。
    '''
    if isinstance(html, unicode):
        html = html.encode('utf-8')
    options = dict(
        # HTML, XHTML, XML #        'clean':True, # これで変換すると head要素内に script要素を追加してしまう         doctype='strict', # 強制的に DOCTYPE を付け数を揃えて解析を楽にする         drop_proprietary_attributes=True,
        enclose_text=True, # body要素にテキストを直接置かなくなる         output_xhtml=True,

        # Pretty Print         wrap=0,         # Character Encoding         char_encoding='utf8',
        newline='LF', # Python 内部での文字列の改行は \n のみ         # Miscellaneous         tidy_mark=False,
        )
    out = tidy.parseString(html, **options)
    for e in out.errors:
        if ((ERRORS_OUT and e.severity == 'E') or             (WARNINGS_OUT and e.severity == 'W') or             e.severity not in 'EW'):
            sys.stderr.write('%s\n' % e)
    file('test.html', 'w').write(str(out))
    return str(out)

def domview(xhtml):
    u'''
    XHTML を DOMツリーにして整形表示する
    '''
    d = xml.dom.minidom.parseString(xhtml)     _printnode(d.childNodes[1]) # <!DOCTYPE> 直後のhtml要素だけをターゲットに def _printnode(node, nest=0):
    out = '| '*nest+node.nodeName
    v = node.nodeValue
    if v:
        if v.strip():
            value = ' '.join(v.split())
        else:
            value = repr(v)
        out = '%s %s' % (out, value)
    print out
    for i in node.childNodes:
        _printnode(i, nest+1)

def test(html):
    print '\nHTML:'     print html
    xhtml = convert(html)
    print 'XHTML:'     print unicode(xhtml, 'utf-8')
    print 'DOM:'     domview(xhtml) html = u'''
<title>hoge</title>
<p>&euro; hoge ほげ <b>
<!--コメント-->
<font title="&yen;" color=green>Python</font>
</p>
<blink>&#10023; Twinkle, Twinkle &#x2727;</blink> Little Star
'''
test(html) test('hoge')
##test(unicode(urllib.urlopen(
##    'http://tuchinoko.moe-nifty.com/oboegaki/2004/12/post_6.html'
##    ).read(), 'utf-8'))
# 好きに流用してください。

一旦 XHTML に変換してしまえば xml.dom 等を使って容易にアクセス出来るわけで。楽々。

(元HTML文書のエンコーディング指定が何であっても) utf-8 にして処理するのが基本という事さえ押さえておけば面倒は起こらないと思います。「HTML の文字コードを調べる」で得たエンコーディング名を使って utf-8 に変換してから tidy.parseString に食わせれば良いでしょう。後の処理は上記コードを参考に。

« 涼宮ハルヒの動揺:谷川 流 | トップページ | 密漁と中国四千年と首狩り族 »

Python」カテゴリの記事

トラックバック


この記事へのトラックバック一覧です: µTidylibを使ってみる:

« 涼宮ハルヒの動揺:谷川 流 | トップページ | 密漁と中国四千年と首狩り族 »

他のアカウント

ブログ妖精

  • ココロ

Affiliate

無料ブログはココログ