我正在试图弄清楚我是否正确使用了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!
答案 0 :(得分:6)
嗯,正如经常发生的那样,我在发布一个长而详细的问题后不久就找到了答案。 lxml
返回字节字符串的原因即使你仔细给它unicode也是因为a performance optimization in lxml
。来自FAQ:
在Python 2中,lxml的API返回纯ASCII文本值的字节字符串,无论是标签名称还是Element内容中的文本。
原因是ASCII编码的字节字符串与Python 2中的Unicode字符串兼容,但消耗的内存较少(通常为2或4倍)并且创建速度更快,因为它们不需要解码。纯ASCII字符串值在XML中非常常见,因此这种优化通常是值得的。
但是在Python 3中:
lxml总是返回文本和名称的Unicode字符串,ElementTree也是如此。从Python 3.3开始,只包含可以用ASCII或Latin-1编码的字符的Unicode字符串通常与字节字符串一样有效。在旧版本的Python 3中,上述缺点适用。
所以你有它。这是lxml中的性能优化,增加了字节和unicode字符串的混淆。
至少它已在Python 3中修复了!升级时间。