Gentleちゃれんじ


lxmlでhtmlを処理する 補足

前回、「lxmlでhtmlを処理する」で、 pythonのxml/htmlを取り扱う便利モジュールlxmlの紹介をしましたが、 あの後からちょこちょこ新発見があったので補足としてメモしておく事にします。

目次

  1. 日本語のWebページを読み込む
  2. 日本語のWebページを出力する
  3. まとめ

1. 日本語のWebページを読み込む

前回は、lxmlで読み込んだhtmlのタグを追加したり消したりというところをメイ ンに書きました。でも、少し、htmlをプログラムでいじった人なら分かると思い ますが、この手のプログラミングで非常に困るのは、日本語の文字コードに関する 問題です。ランダムに日本語で書かれたWebページを読み込むと、そのWebページが Shift_JISで書かれていたり、EUC-JPで書かれていたり、UTF-8で書かれていたり します。

今回は、lxmlで日本語を含むhtmlソースをどう扱うかについて、 自分の知識の及ぶ限り書いていきたいと思います。 といっても、lxmlはかなりよく出来たモジュールなので、日本語の扱いも 楽なんですけどね。まだまだ、上手いやり方を探索中なので、 もしいい方法を知っている方がいらっしゃればメールや掲示板に 書き込んで頂ければ幸いです。

まず、lxmlに日本語のWebページを読み込む所から始めます。

>>> import urllib2
>>> import lxml.html
>>> html = urllib2.urlopen('http://www.cafe-gentle.jp/').read() # html 取得
>>> root = lxml.html.fromstring(html.decode('utf-8'))
>>> root.text_content()[:50]
u'\u5e7b\u60f3\u7d14\u55ab\u8336Gentle\n\n\n\n  \u5e7b\u60f3\u7d14\u55ab\u8336Gentle\n
\n    \n      \u6b21\u306e\u30ad\u30ea\u756a'

日本語のWebページを取り扱う際には、入力する文字列をデコードしたりしないといけない のかなーと思ったりしがちです。自分も最初は、decodeしてからlxmlに入力して いました。でも、lxmlは違います。

>>> html = urllib2.urlopen('http://www.cafe-gentle.jp/').read() # html 取得
>>> root = lxml.html.fromstring(html)
>>> root.text_content()[:50]
u'\u5e7b\u60f3\u7d14\u55ab\u8336Gentle\n\n\n\n  \u5e7b\u60f3\u7d14\u55ab\u8336Gentle\n
\n    \n      \u6b21\u306e\u30ad\u30ea\u756a'

これだけでいいのです。読み込んだ日本語のWebページをfromstringメソッドにそのまま 入力してやると、Unicode文字列で取り扱ってくれるようになります。これで、わ ざわざデコードしたり、文字コードを調べたりする必要は無いわけです。

2. 日本語のWebページを出力する

では、日本語を含むWebページを出力するときは、どうなるのでしょうか? さっきの例にあったtext_contentメソッドは、Unicode文字列が返ってきました。 .textや.tailに関してもUnicode文字列を返します(これが何を意味するか分から ない場合は前回の説明を参考にして下さ い。)。ならば、lxml.html.tostring()もUnicode文字列を返すのでしょうか。

>>> lxml.html.tostring(root)[200:300]
'#23567;説などを掲載、サークル[Gent'

Unicode文字列は返しません。日本語を文字参照で直した形で返します。 これは、一部の場合は非常に便利です。例えば、日本語に対応していない、htmlを操作するモジュール を使いたい場合は、この返り値を入力としてやれば使う事が出来ます。表示するときは、 ブラウザが日本語に直してくれるので安心です。

ただ、普通は、そんな使い方するよりも、出力した後のhtmlファイルを手で編集したり、 エディタで眺めたり、他のプログラムに日本語として入力したりしたいですよね。 そんな場合は、tostringメソッドに一つ引数を追加してやる必要があります。

>>> print lxml.html.tostring(root, encoding="utf-8")[200:300]
るサイト"><meta name="keywords" content="T-key,gentle,オリジナル,純喫茶,同人,創作"><

それが、encodingです。これを指定してやって、出力すると&#xxxx;の形ではなく、日本語が 指定した文字コードにエンコードされた形で返されます。しかも、 encoding=unicodeとしてやれば、Unicode文字列が返されるという親切設計です。 ただ、注意しないといけない事があります。HTMLは、ソース内にあらかじめ このHTMLがどんな文字コードで書かれているかを指定している場合があります。 ですから、HTML内ではUTF-8で書かれているとされているので、ここでencoding="euc-jp"で 出力すると、ブラウザで見た時文字化けしてしまいます。となると、場合によっては、 元々どんな文字コードで書かれていたのかを知っている必要があります。

文字コードを知る為の方法は、色々ありますが、ここでは、文字コードが何かを自動で解析すると サードパーティモジュール「Universal Encoding Detector」を使います。使い方は、リンク先のページに分かり易く書いているのでそれを 見てください。このモジュールを使えば、

>>> import chardet
>>> chardet.detect(html)
{'confidence': 0.98999999999999999, 'encoding': 'utf-8'} 

という感じで、文字コードを教えてくれます。これを、encodingに指定してやれば、 いいというわけです。

あと、もう一つ、問題があります。人によっては、lxmlから出力される値は、 XHTMLであって欲しいと思う人や、HTMLであって欲しいと思う人がいると思います。 この場合は、

>>> root = lxml.html.fragment_fromstring('<p>Hello<br>world!</p>')
>>> lxml.html.tostring(root)
'<p>Hello<br>world!</p>'
>>> lxml.html.tostring(root, method='html')
'<p>Hello<br>world!</p>'
>>> lxml.html.tostring(root, method='xml')
'<p>Hello<br/>world!</p>'
>>> lxml.html.tostring(root, method='text')
'Helloworld!'

methodに、上のように値を入れてやれば、それぞれ思った通りの値を返してもらえます。 あと、tostringメソッドは、Content-Typeを指定するmeta要素を デフォルトで消してしまうんですが(その方が、何かと便利なんですよ。解析する立場ならば)、 include_meta_content_type=Trueとしてやると、それをしないようにしてくれます。 他には、pretty_print=Trueとしてやると、出力されるhtmlがインデントで綺麗にされて 出てきます。出力後手作業でソースをいじりたい場合は、おすすめです。

まとめ

ここに載っている事は、lxml.htmlをhelp関数で調べれば全部載っています。 ただ、ちょっと使ってみたいと思っている時に、英語のhelpを全部読むのは苦痛ですし、 解説サイトを見たいと思いますよねw
ここに載っている事は間違いも含んでいるかもしれませんが、ちょっと試しに いじってみる人に取って出来る限り取っ付き易くなるよう紹介しているつもりで す。誰かの役に立てば、幸いです。また、なにか分かった事があれば、 徐々に追加していこうと思っています。

それでは、今回のTipsはこれまで!

ページのトップへ戻る