/ / Pobieranie nazw atrybutów za pomocą Scrapy XPATH - python, xpath, scrapy

Pobieranie nazw atrybutów za pomocą Scrapy XPATH - python, xpath, scrapy

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 № 1

Kró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 elementoraz z atrybutami, ich wartościami.