注意:Scrapy 选择器虽然基于 lxml,但在布尔 XPath 表达式或返回数字的结果方面与 lxml 不同。
让我们使用这个示例 HTML 文档来说明:
>>> html = '''<!DOCTYPE html>
... <html>
... <head>
... <title>This is a title</title>
... </head>
... <body>
... <p>Hello world!</p>
... </body>
... </html>'''
如果你直接使用lxml,你可以测试例如文档中是否存在<div>或<p>等元素:
>>> import lxml.html
>>> doc = lxml.html.fromstring(html)
>>> doc.xpath('boolean(//div)')
False
>>> doc.xpath('boolean(//p)')
True
lxml 的.xpath() 返回您所期望的:没有<div> 元素,但有一个<p>。
如果你将它与 Scrapy 选择器进行比较,调用 .xpath() 将返回一个 Selector 列表。 (这与是否使用布尔表达式无关。)
>>> import scrapy
>>> response = scrapy.Selector(text=html)
>>> response.xpath('boolean(//p)')
[<Selector xpath='boolean(//p)' data='1'>]
您需要调用.extract() 或.extract_first()(或新的.get() 快捷方式)来获取“有用的”数据以进行处理。而你从.extract()/.extract_first()/.get() 得到的是字符串:
>>> response.xpath('boolean(//p)').extract()
['1']
>>> response.xpath('boolean(//p)').extract_first()
'1'
>>> response.xpath('boolean(//p)').get()
'1'
您会看到用于 XPath true 的 '1'。您还可以得到一个用于 XPath 的 '0' false:
>>> response.xpath('boolean(//div)').get()
'0'
在 Python 中,非空字符串上的 bool() 将返回 True,无论字符串是什么:
>>> bool(response.xpath('boolean(//p)').get())
True
>>> bool(response.xpath('boolean(//div)').get())
True
一种解决方法是在“中间”使用int() 进行转换:
>>> bool(int(response.xpath('boolean(//p)').get()))
True
>>> bool(int(response.xpath('boolean(//div)').get()))
False
对于返回数字的 XPath 表达式,例如 count(...),lxml 返回浮点数:
>>> doc.xpath('count(//div)')
0.0
>>> doc.xpath('count(//p)')
1.0
而 Scrapy 选择器返回浮点数的字符串表示:
>>> response.xpath('count(//div)').get()
'0.0'
>>> response.xpath('count(//p)').get()
'1.0'
所以你想在处理结果之前将提取的字符串传递给float():
>>> float(response.xpath('count(//p)').get())
1.0
>>> float(response.xpath('count(//div)').get())
0.0