LXML-HTML解析でのエンコードの問題

この投稿は、すべてのpythonistsの永遠の問題-エンコーディングに捧げられています。 最近、私は私の友人がプログラムのフォームの行を持っていると不平を言っている手紙を受け取りました:

u'\xd0\x9a\xd1\x83\xd1\x80\xd1\x83\xd0\xbc\xd0\xbe\xd1\x87'

何かおかしいことに気づきましたか? そして、ここにいます。 行はUnicodeのようですが、内部にはutf-8でエンコードされたバイトがあります。 ここで何かが間違っています。 さらに理解し、これを生成するスクリプトを要求すると、データがWebから取得されることが明らかになります。 urllibを介した非常に通常の方法で、解析のためにlxml.htmlにフィードされます。 urllibはバイト文字列のみを扱うため、そのようなUnicodeに変換することはできませんでした。つまり、 lxmlせいです。

一般に、 lxml非常にクールなライブラリであり、高速で機能的であり、 ElementTree下のインターフェースを模倣してElementTreeと対話できます。 xml何らかの形で便利に使用する必要がある場合、Pythonistには長い間人気があります。

しかし、これは少し異なるケースです。 これはhtmlパーサーを使用します。 そして、文字列でこれらの不愉快な変態が起こるのはその中にあります。

私は問題が何であり、この行動を克服する方法を理解することにしました。

始めるために、私はyandex.ruに行き、どのようなhtmlがそこに与えられているかを見ました。 Utf8コンテンツエンコーディング。 すぐに私の目を引いたのは、エンコード宣言の欠如であり、必須ではありませんが、まだ頻繁に使用されています。 同様のhtmlを作成することにより:

data = """<html>
<head>
</head>
<body> </body>
</html>"""
html = lxml.html.document_fromstring(data)


そして、それをlxml.htmlに詰め込んで、悲しいかな、予想される結果を受け取りました:

>>> s
u'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82 \xd0\xbc\xd0\xb8\xd1\x80'
>>> print s
Привет м


s-これはまさに「Hello World」という行であり、xpathを通じて引き裂かれています。 ご覧のとおり、デコードされていません。 概して、この問題はその場で解決できます。 このような特別なコーデックraw-unicode-escapeがあり、このような行からはバイトが作成されますが、変換も行われません。

>>> print s.encode('raw-unicode-escape')


しかし、そのような決定は悪いです。 どういうわけかlxml.html非ASCII文字のモックにしないでください。

嫌なメタヘッダーhtmlでエンコードを指定するとどうなりますか?

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body> </body>
</html>


すべてがすぐに配置されます。

>>> print s


もちろん、httpヘッダーからエンコードに関する情報を取得する方が論理的ですが、lxml.htmlの場合、謎が入ったプロトコルであり、それを当てにすることはできません。

これを解決する別の方法は、lxml.htmlに入力をバイト文字列ではなくユニコードにすることです(もちろん、エンコードを自分で知っている場合を除きます)。

>>> html = lxml.html.document_fromstring(data.decode('utf-8'))
...
>>> print s


私の意見では、 lxml.htmlが「すべてのコストで生き残り」、コンテンツを台無しにしようとしないが、xmlを解析する場合のように、エンコードが設定されていないことを明示的に通知する方がより正しいでしょう。 しかし、いずれにしても、回避策があります。

警戒してください。

Source: https://habr.com/ru/post/J128381/


All Articles