lxml を使用して HTML をスクレイピングしようとしたときに発生するエンコーディングの問題を最終的に解決しようとしています。以下に、私が遭遇した 3 つのサンプル HTML ドキュメントを示します。
1.
<!DOCTYPE html>
<html lang='en'>
<head>
<title>Unicode Chars: 은 —’</title>
<meta charset='utf-8'>
</head>
<body></body>
</html>
2.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko-KR" lang="ko-KR">
<head>
<title>Unicode Chars: 은 —’</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body></body>
</html>
3.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Unicode Chars: 은 —’</title>
</head>
<body></body>
</html>
私の基本的なスクリプト:
from lxml.html import fromstring
...
doc = fromstring(raw_html)
title = doc.xpath('//title/text()')[0]
print title
結果は次のとおりです。
Unicode Chars: ì ââ
Unicode Chars: 은 —’
Unicode Chars: 은 —’
したがって、明らかにサンプル 1 と<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
タグの欠落の問題です。ここからのソリューションは、サンプル 1 を utf-8 として正しく認識し、元のコードと機能的に同等です。
lxml ドキュメントは競合しているように見えます。
ここから、この例は、UnicodeDammit を使用してマークアップを Unicode としてエンコードする必要があることを示唆しているようです。
from BeautifulSoup import UnicodeDammit
def decode_html(html_string):
converted = UnicodeDammit(html_string, isHTML=True)
if not converted.unicode:
raise UnicodeDecodeError(
"Failed to detect encoding, tried [%s]",
', '.join(converted.triedEncodings))
# print converted.originalEncoding
return converted.unicode
root = lxml.html.fromstring(decode_html(tag_soup))
ただし、ここでは次のように述べています。
ヘッダーのメタ タグで文字セットを指定する Unicode 文字列の HTML データを [解析] しようとすると、エラーが発生します。一般に、XML/HTML データをパーサーに渡す前に Unicode に変換することは避けてください。遅く、エラーが発生しやすいです。
lxml ドキュメントの最初の提案に従おうとすると、私のコードは次のようになります。
from lxml.html import fromstring
from bs4 import UnicodeDammit
...
dammit = UnicodeDammit(raw_html)
doc = fromstring(dammit.unicode_markup)
title = doc.xpath('//title/text()')[0]
print title
次の結果が得られました。
Unicode Chars: 은 —’
Unicode Chars: 은 —’
ValueError: Unicode strings with encoding declaration are not supported.
<?xml version="1.0" encoding="utf-8"?>
サンプル 1 は正常に動作するようになりましたが、サンプル 3 はタグが原因でエラーになります。
これらすべてのケースを処理する正しい方法はありますか? 以下よりも良い解決策はありますか?
dammit = UnicodeDammit(raw_html)
try:
doc = fromstring(dammit.unicode_markup)
except ValueError:
doc = fromstring(raw_html)