、、等)。例如,给定以下XML/HTML结构:
<a node='1'>This</a>
<b node='2'>Is</b>
<c node='23'>A</c>
<d selector='g'>Loud</d>
<e node='4'>Dog</e>
如果我们想获取所有node属性值为特定值的元素的标签名,一个直观的想法是使用XPath的name()函数,例如//*[@node="1"]/name()。然而,在Scrapy的Selector对象上直接使用response.xpath(f'//*[@node="{node}"]/name()').get()时,可能会遇到ValueError: XPath error: Invalid expression的错误。这通常是因为name()函数旨在获取单个节点的本地名,而当它直接应用于一个可能返回多个节点的XPath表达式结果集时,或者在某些XPath引擎的特定上下文中,其行为可能不符合预期,导致无法直接返回字符串形式的标签名。
2. 解决方案:利用Scrapy的re()方法
Scrapy的Selector对象提供了一个强大的re()方法,它允许我们对XPath选择器返回的元素进行正则表达式匹配。关键在于,re()方法不是直接作用于DOM结构,而是作用于XPath选择器所匹配到的元素的序列化HTML/XML字符串。这使得我们可以灵活地提取标签名。
2.1 核心原理与正则表达式
要从元素的序列化字符串中提取标签名,我们可以使用以下正则表达式:r'
- <:>
- (\w+): 这是一个捕获组,\w+匹配一个或多个字母、数字或下划线字符。这正是我们需要的标签名(如a, b, c等)。
- \s: 匹配一个空白字符。这确保我们捕获到标签名后立即停止,避免匹配到属性名。
通过将此正则表达式应用于XPath选择器返回的元素集合,re()方法将返回一个包含所有匹配到的标签名的列表。
2.2 示例代码
以下是如何在Scrapy中使用re()方法提取元素标签名的具体步骤:
-
启动Scrapy Shell:
scrapy shell
-
准备测试Markup:
在Scrapy Shell中,我们首先定义包含待解析HTML/XML的字符串,并将其包装在一个根标签(如)中,以确保其为有效的XML/HTML文档:
In [1]: markup = """<html><a node='1'>This</a>
...: <b node='2'>Is</b>
...: <c node='23'>A</c>
...: <d selector='g'>Loud</d>
...: <e node='4'>Dog</e></html>"""
-
创建Scrapy Selector对象:
将markup字符串传递给scrapy.Selector(text=...)来创建一个Selector对象,这是进行XPath查询的基础:
In [2]: sel = scrapy.Selector(text=markup)
-
执行XPath查询并应用re()方法:
首先,使用XPath表达式//*[@node]选择所有包含node属性的元素。然后,对这些选中的元素集合调用.re()方法,并传入我们定义的正则表达式'
In [3]: sel.xpath('//*[@node]').re('<(\w+)\s')
Out[3]: ['a', 'b', 'c', 'e']