поиск элементов по атрибуту с помощью lxml

мне нужно разобрать xml-файл, чтобы извлечь некоторые данные. Мне нужны только некоторые элементы с определенными атрибутами, вот пример из документа:

<root>
    <articles>
        <article type="news">
             <content>some text</content>
        </article>
        <article type="info">
             <content>some text</content>
        </article>
        <article type="news">
             <content>some text</content>
        </article>
    </articles>
</root>

здесь я хотел бы получить только статью с типом "новости". Какой самый эффективный и элегантный способ сделать это с помощью lxml?

Я пробовал с помощью метода find, но это не очень приятно:

from lxml import etree
f = etree.parse("myfile")
root = f.getroot()
articles = root.getchildren()[0]
article_list = articles.findall('article')
for article in article_list:
    if "type" in article.keys():
        if article.attrib['type'] == 'news':
            content = article.find('content')
            content = content.text

2 ответов


вы можете использовать xpath, например root.xpath("//article[@type='news']")

это выражение xpath вернет список всех <article/> элементы с атрибутами " type "со значением"news". Затем вы можете перебирать его, чтобы делать то, что хотите, или передавать его куда угодно.

чтобы получить только текстовое содержимое, вы можете расширить xpath следующим образом:

root = etree.fromstring("""
<root>
    <articles>
        <article type="news">
             <content>some text</content>
        </article>
        <article type="info">
             <content>some text</content>
        </article>
        <article type="news">
             <content>some text</content>
        </article>
    </articles>
</root>
""")

print root.xpath("//article[@type='news']/content/text()")

и это выведет ['some text', 'some text']. Или, если вам просто нужны элементы контента, это будет "//article[@type='news']/content" -- и так далее.


как раз для справки, вы можете достигнуть такого же результата с findall:

root = etree.fromstring("""
<root>
    <articles>
        <article type="news">
             <content>some text</content>
        </article>
        <article type="info">
             <content>some text</content>
        </article>
        <article type="news">
             <content>some text</content>
        </article>
    </articles>
</root>
""")

articles = root.find("articles")
article_list = articles.findall("article[@type='news']/content")
for a in article_list:
    print a.text