Return 使用 lxml 和请求的来自 XPath 的 utf-8
Return utf-8 from XPath using lxml and requests
我想弄清楚我是否正确使用了 lxml 的 xpath
函数。这是我当前的代码,包括我们在一个相当大的抓取库中慢慢积累的所有解决方法,该库处理可怕的,可怕的 输入:
import certifi, requests
from lxml import html
s = requests.session()
r = s.get(
url,
verify=certifi.where(),
**request_dict
)
# Throw an error if a bad status code is returned.
r.raise_for_status()
# If the encoding is iso-8859-1, switch it to cp1252 (a superset)
if r.encoding == 'ISO-8859-1':
r.encoding = 'cp1252'
# Grab the content
text = r.text
html_tree = html.fromstring(text)
因此,如果这一切正常,requests
使用 r.encoding
来决定在调用 r.text
时如何创建 unicode 对象。伟大的。我们获取该 unicode 对象 (text
) 并将其发送到 ltml.html.fromstring()
,后者识别出它是 unicode,并返回给我们一个 ElementTree
.
这一切似乎都在正常工作,但令人不安的是,当我这样做时:
html_tree.xpath('//text()')[0]
这应该给我树中的第一个文本节点,我得到一个字符串,而不是一个 unicode 对象,我发现自己不得不写:
html_tree.xpath('//text()')[0].decode('utf8')
这很糟糕。
我一开始所做的所有工作的全部想法是创建 Mythical Unicode Sandwich,但无论我做什么,我都会得到二进制字符串。我在这里错过了什么?
这是为您提供的概念证明:
import certifi, requests
from lxml import html
s = requests.session()
r = s.get('https://www.google.com', verify=certifi.where())
print type(r.text) # <type 'unicode'>, GREAT!
html_tree = html.fromstring(r.text)
first_node = html_tree.xpath('//text()', smart_strings=False)[0]
print type(first_node) # <type 'str'>, TERRIBLE!
好吧,就像经常发生的那样,我在发布了一个又长又详细的问题后不久就找到了答案。 lxml
returns 字节字符串的原因,即使你小心地给它 unicode 也是因为 a performance optimization in lxml
。来自常见问题解答:
In Python 2, lxml's API returns byte strings for plain ASCII text values, be it for tag names or text in Element content.
The reasoning is that ASCII encoded byte strings are compatible with Unicode strings in Python 2, but consume less memory (usually by a factor of 2 or 4) and are faster to create because they do not require decoding. Plain ASCII string values are very common in XML, so this optimisation is generally worth it.
但是在 Python 3:
lxml always returns Unicode strings for text and names, as does ElementTree. Since Python 3.3, Unicode strings containing only characters that can be encoded in ASCII or Latin-1 are generally as efficient as byte strings. In older versions of Python 3, the above mentioned drawbacks apply.
好了。这是 lxml 中的性能优化,增加了字节和 unicode 字符串的混淆。
至少它在 Python 3 中修复了!是时候升级了。
我想弄清楚我是否正确使用了 lxml 的 xpath
函数。这是我当前的代码,包括我们在一个相当大的抓取库中慢慢积累的所有解决方法,该库处理可怕的,可怕的 输入:
import certifi, requests
from lxml import html
s = requests.session()
r = s.get(
url,
verify=certifi.where(),
**request_dict
)
# Throw an error if a bad status code is returned.
r.raise_for_status()
# If the encoding is iso-8859-1, switch it to cp1252 (a superset)
if r.encoding == 'ISO-8859-1':
r.encoding = 'cp1252'
# Grab the content
text = r.text
html_tree = html.fromstring(text)
因此,如果这一切正常,requests
使用 r.encoding
来决定在调用 r.text
时如何创建 unicode 对象。伟大的。我们获取该 unicode 对象 (text
) 并将其发送到 ltml.html.fromstring()
,后者识别出它是 unicode,并返回给我们一个 ElementTree
.
这一切似乎都在正常工作,但令人不安的是,当我这样做时:
html_tree.xpath('//text()')[0]
这应该给我树中的第一个文本节点,我得到一个字符串,而不是一个 unicode 对象,我发现自己不得不写:
html_tree.xpath('//text()')[0].decode('utf8')
这很糟糕。
我一开始所做的所有工作的全部想法是创建 Mythical Unicode Sandwich,但无论我做什么,我都会得到二进制字符串。我在这里错过了什么?
这是为您提供的概念证明:
import certifi, requests
from lxml import html
s = requests.session()
r = s.get('https://www.google.com', verify=certifi.where())
print type(r.text) # <type 'unicode'>, GREAT!
html_tree = html.fromstring(r.text)
first_node = html_tree.xpath('//text()', smart_strings=False)[0]
print type(first_node) # <type 'str'>, TERRIBLE!
好吧,就像经常发生的那样,我在发布了一个又长又详细的问题后不久就找到了答案。 lxml
returns 字节字符串的原因,即使你小心地给它 unicode 也是因为 a performance optimization in lxml
。来自常见问题解答:
In Python 2, lxml's API returns byte strings for plain ASCII text values, be it for tag names or text in Element content.
The reasoning is that ASCII encoded byte strings are compatible with Unicode strings in Python 2, but consume less memory (usually by a factor of 2 or 4) and are faster to create because they do not require decoding. Plain ASCII string values are very common in XML, so this optimisation is generally worth it.
但是在 Python 3:
lxml always returns Unicode strings for text and names, as does ElementTree. Since Python 3.3, Unicode strings containing only characters that can be encoded in ASCII or Latin-1 are generally as efficient as byte strings. In older versions of Python 3, the above mentioned drawbacks apply.
好了。这是 lxml 中的性能优化,增加了字节和 unicode 字符串的混淆。
至少它在 Python 3 中修复了!是时候升级了。