Próbuję uzyskać zarówno klucze, jak i wartości atrybutów jakiegoś tagu w pliku XML (za pomocą scrapy i xpath).
Tag to coś w stylu:
<element attr1="value1" attr2="value2 ...>
Nie znam klawiszy „attr1”, „attr2” i tak dalej,i mogą zmieniać się między dwoma elementami. Nie dowiedziałem się, jak uzyskać zarówno klucze, jak i wartości za pomocą xpath. Czy jest jakaś inna dobra praktyka?
Odpowiedzi:
6 dla odpowiedzi № 1Krótka wersja
>>> 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"}
>>>
Długa wersja
XPath ma name(node-set?)
funkcjonować aby uzyskać nazwy węzłów (atrybut jest węzłem, węzłem atrybutu):
The Nazwa funkcja zwraca ciąg znaków zawierający nazwę QN reprezentującą rozszerzoną nazwę węzła w argumencie zestaw węzłów, który jest pierwszy w kolejności dokumentów (...) Jeśli argument, który pominął, domyślnie jest to zestaw węzłów z węzeł kontekstu jako jedyny członek.
(źródło: 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"]
(Tutaj, przykuty name()
na wynik //element
wybór, aby zastosować funkcję do wszystkich wybranych węzłów elementu. Przydatna funkcja selektorów Scrapy)
Chciałoby się zrobić to samo z węzłami atrybutów, prawda? Ale to nie działa:
>>> selector.xpath("//element/@*").extract()
[u"value1", u"value2"]
>>> selector.xpath("//element/@*").xpath("name()").extract()
[]
>>>
Uwaga: nie wiem, czy to jest ograniczenie lxml/libxml2
, które Scrapy używa pod maską lub czy specyfikacje XPath go nie zezwalają. (Nie rozumiem, dlaczego tak się stanie.)
Możesz jednak użyć name(node-set)
formularz, tj. z niepustym zestawem węzłów jako parametrem. Jeśli przeczytasz uważnie część specyfikacji XPath 1.0, które wkleiłem powyżej, tak jak w przypadku innych funkcji łańcuchowych, name(node-set)
bierze pod uwagę tylko pierwszy węzeł w zestawie węzłów (w kolejności dokumentów):
>>> selector.xpath("//element").xpath("@*").extract()
[u"value1", u"value2"]
>>> selector.xpath("//element").xpath("name(@*)").extract()
[u"attr1"]
>>>
Węzły atrybutów mają również pozycje, dzięki czemu można zapętlić wszystkie atrybuty według ich pozycji. Tutaj mamy 2 (wynik count(@*)
w węźle kontekstu):
>>> 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
>>>
Teraz możesz zgadnąć, co możemy zrobić: zadzwoń name()
dla każdego @*[i]
>>> for element in selector.xpath("//element"):
... for i in range(1, 2+1):
... print element.xpath("name(@*[%d])" % i).extract_first()
...
attr1
attr2
>>>
Jeśli złożysz to wszystko razem, i załóż to @*
dostaniesz atrybuty w kolejności dokumentów (nie wspomniane w specyfikacjach XPath 1.0, myślę, ale to, co widzę dzieje się lxml
), skończysz z tym:
>>> 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 dla odpowiedzi nr 2
Próbuję uzyskać zarówno klucze, jak i wartości atrybutów jakiegoś tagu w pliku XML (za pomocą scrapy i xpath).
Potrzebujesz @*
, co oznacza „dowolny atrybut”. Wyrażenie XPath //element/@*
da ci wszystkie atrybuty elementów element
oraz z atrybutami, ich wartościami.