Несколько сообщений на 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.
Во-первых, вы не можете поместить предикат ([]
) непосредственно после /
.
Во-вторых, 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)