使用Scrapy XPATH获取属性的名称

时间:2015-09-25 20:57:01

标签: python xpath scrapy

我正在尝试在XML文件中获取某些标记的属性键和值(使用scrapy和xpath)。

标签类似于:

<element attr1="value1" attr2="value2 ...>

我不知道键“attr1”,“attr2”等等,它们可以在两个元素之间切换。我没有弄清楚如何用xpath获取键和值,还有其他好的做法吗?

2 个答案:

答案 0 :(得分:7)

简短版

>>> for element in selector.xpath('//element'):
...     attributes = []
...     # loop over all attribute nodes of the element
...     for index, attribute in enumerate(element.xpath('@*'), start=1):
...         # use XPath's name() string function on each attribute,
...         # using their position
...         attribute_name = element.xpath('name(@*[%d])' % index).extract_first()
...         # Scrapy's extract() on an attribute returns its value
...         attributes.append((attribute_name, attribute.extract()))
... 
>>> attributes # list of (attribute name, attribute value) tuples
[(u'attr1', u'value1'), (u'attr2', u'value2')]
>>> dict(attributes)
{u'attr2': u'value2', u'attr1': u'value1'}
>>> 

长版

XPath有一个name(node-set?) function来获取节点名称(an attribute is a node, an attribute node):

  

name 函数返回一个字符串,其中包含一个QName,表示参数node-set中文档顺序中第一个节点的扩展名。(.. 。)如果省略的参数,则默认为以上下文节点作为其唯一成员的节点集。

(来源:http://www.w3.org/TR/xpath/#function-name

>>> import scrapy
>>> selector = scrapy.Selector(text='''
...     <html>
...     <element attr1="value1" attr2="value2">some text</element>
...     </html>''')
>>> selector.xpath('//element').xpath('name()').extract()
[u'element']

(这里,我在name()选择的结果上链接//element,将函数应用于所有选定的元素节点.Scrapy选择器的一个便利功能

有人想对属性节点做同样的事,对吗?但它不起作用:

>>> selector.xpath('//element/@*').extract()
[u'value1', u'value2']
>>> selector.xpath('//element/@*').xpath('name()').extract()
[]
>>> 

注意:我不知道它是否是lxml/libxml2的限制,Scrapy使用它,或者如果XPath规范不允许它。 (我不明白为什么会这样。)

您可以做的是使用name(node-set)形式,即使用非空节点集作为参数。如果您仔细阅读上面粘贴的XPath 1.0规范部分,与其他字符串函数一样,name(node-set)仅考虑节点集中的第一个节点(按文档顺序) :

>>> selector.xpath('//element').xpath('@*').extract()
[u'value1', u'value2']
>>> selector.xpath('//element').xpath('name(@*)').extract()
[u'attr1']
>>> 

属性节点也有位置,因此您可以按位置循环所有属性。这里我们有2个(上下文节点上count(@*)的结果):

>>> for element in selector.xpath('//element'):
...     print element.xpath('count(@*)').extract_first()
... 
2.0
>>> for element in selector.xpath('//element'):
...     for i in range(1, 2+1):
...         print element.xpath('@*[%d]' % i).extract_first()
... 
value1
value2
>>> 

现在,您可以猜测我们可以做些什么:为每个name()致电@*[i]

>>> for element in selector.xpath('//element'):
...     for i in range(1, 2+1):
...         print element.xpath('name(@*[%d])' % i).extract_first()
... 
attr1
attr2
>>> 

如果你将所有这些放在一起,并假设@*将按文档顺序获取属性(我认为不是在XPath 1.0规范中说的,但这是我在lxml看到的情况),你最终得到了这个:

>>> attributes = []
>>> for element in selector.xpath('//element'):
...     for index, attribute in enumerate(element.xpath('@*'), start=1):
...         attribute_name = element.xpath('name(@*[%d])' % index).extract_first()
...         attributes.append((attribute_name, attribute.extract()))
... 
>>> attributes
[(u'attr1', u'value1'), (u'attr2', u'value2')]
>>> dict(attributes)
{u'attr2': u'value2', u'attr1': u'value1'}
>>> 

答案 1 :(得分:1)

  

我正在尝试在XML文件中获取某些标记的属性键和值(使用scrapy和xpath)。

您需要//element/@*,这意味着“任何属性”。 XPath表达式element将为您提供元素{{1}}的所有属性,以及属性及其值。