Получить текст XML-узла с заданным текстом соседнего узла с помощью xpath

1

Несколько сообщений на SO были полезны, но я не нашел ответа на эту конкретную проблему.

Я использую python3 и lxml.etree

Учитывая XML:

<collection xmlns="http://www.loc.gov/MARC21/slim">
<record>
<datafield tag="856" ind1="4" ind2=" ">
<subfield code="y">English</subfield>
<subfield code="s">387115</subfield>
<subfield code="u">
http://some_url/record/1475606/files/COOLPDF-EN.pdf
</subfield>
</datafield>
</record>
</collection>

Коллекция содержит несколько сотен записей с несколькими десятками полей данных (это все очень загадочная библиотека Конгресса)

Если поле данных имеет метку 856 и имеет подполе с текстом English, я хочу текст ссылки в подполе узла node = "u".

Я пытался:

import lxml.etree as ET
ns = '{http://www.loc.gov/MARC21/slim}'
tree = ET.parse('example.xml')
root = tree.getroot()
eng = root.findall(
    './/{0}datafield[@tag="856"]/[{0}descendant::text="English"]/[{0}following-sibling::code="u"]'.format(ns))
print([e.text for e in eng])

Но это просто дает мне пустой список.

Любая помощь приветствуется.

ТИА

Теги:
xpath
lxml

1 ответ

2
Лучший ответ

Есть несколько проблем с вашим XPath.

Во-первых, вы не можете поместить предикат ([]) непосредственно после /.

Во-вторых, descendant::text выбирает элемент потомка с именем text (которого у вас нет в вашем XML). Аналогично, following-sibling::code выбирает элемент с именем code а не атрибут.

Попробуйте это вместо этого:

eng = root.findall('.//{0}datafield[@tag="856"][{0}subfield="English"]/{0}subfield[@code="u"]'.format(ns))

Если вы хотите использовать более сложные XPath, используйте вместо этого xpath(). Например, если вы хотите только проверить элемент subfield со значением атрибута code "y" для текста на English, вы можете сделать это (это приводит к недопустимой ошибке предиката с помощью функции findall()):

eng = root.xpath('.//s:datafield[@tag="856"][s:subfield[@code="y"]="English"]/s:subfield[@code="u"]', namespaces=ns)

Кроме того, нет ничего плохого в том, как вы обрабатываете пространство имен, но мне легче сопоставить префиксы для пространства имен uris; особенно когда существует несколько пространств имен.

Пример...

ns = {'s': 'http://www.loc.gov/MARC21/slim'}
eng = root.findall('.//s:datafield[@tag="856"][s:subfield="English"]/s:subfield[@code="u"]', namespaces=ns)
  • 1
    Как раз то, что мне было нужно, спасибо за помощь!
  • 0
    @fiacre - пожалуйста! Рад, что смог помочь!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню