Как я могу сослаться на родителя и удалить родительский элемент в RSS XML через LXML в Python? - PullRequest
0 голосов
/ 17 мая 2018

У меня были проблемы с взломом этого. У меня есть RSS-канал в виде файла XML. Упрощенно это выглядит так:

<rss version="2.0">
    <channel>
        <title>My RSS Feed</title>
        <link href="https://www.examplefeedurl.com">Feed</link>
        <description></description>
        <item>...</item>
        <item>...</item>
        <item>...</item>
        <item>
            <guid></guid>
            <pubDate></pubDate>
            <author/>
            <title>Title of the item</title>
            <link href="https://example.com" rel="alternate" type="text/html"/>
            <description>
            <![CDATA[<a href="https://example.com" target="_blank" rel="noopener noreferrer">View Example</a>]]>
            </description>
            <description>
            <![CDATA[<p>This actually contains a bunch of text I want to work with. If this text contains certain strings, I want to get rid of the whole item.</p>]]>
            </description>
        </item>
        <item>...</item>
    </channel>
</rss>

Моя цель - проверить, содержит ли второй тег описания определенные строки. Если он содержит эту строку, я хотел бы полностью удалить ее. В настоящее время в моем коде у меня есть это:

doc = lxml.etree.fromstring(testString)
found = doc.findall('channel/item/description')


for desc in found:
    if "FORBIDDENSTRING" in desc.text:
        desc.getparent().remove(desc)

И он удаляет только второй тег описания, который имеет смысл, но я хочу, чтобы весь элемент исчез. Я не знаю, как удержать элемент item, если у меня есть только ссылка desc.

Я пробовал гуглить так же, как и поиск здесь, но ситуации, которые я вижу, просто хотят удалить тег, как я делаю сейчас, странно, я не наткнулся на пример кода, который хочет избавиться от всего родительского объекта , Любые указатели на документацию / учебные пособия или помощь очень приветствуются.

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Я большой поклонник XSLT, но другой вариант - просто выбрать item вместо description (выберите элемент, который вы хотите удалить; не его дочерний элемент).

Также, если вы используете xpath(), вы можете поставить проверку на запрещенную строку непосредственно в предикате xpath.

Пример ...

from lxml import etree

testString = """
<rss version="2.0">
    <channel>
        <title>My RSS Feed</title>
        <link href="https://www.examplefeedurl.com">Feed</link>
        <description></description>
        <item>...</item>
        <item>...</item>
        <item>...</item>
        <item>
            <guid></guid>
            <pubDate></pubDate>
            <author/>
            <title>Title of the item</title>
            <link href="https://example.com" rel="alternate" type="text/html"/>
            <description>
            <![CDATA[<a href="https://example.com" target="_blank" rel="noopener noreferrer">View Example</a>]]>
            </description>
            <description>
            <![CDATA[<p>This actually contains a bunch of text I want to work with. If this text contains certain strings, I want to get rid of the whole item.</p>]]>
            </description>
        </item>
        <item>...</item>
    </channel>
</rss>
"""

forbidden_string = "I want to get rid of the whole item"

parser = etree.XMLParser(strip_cdata=False)
doc = etree.fromstring(testString, parser=parser)
found = doc.xpath('.//channel/item[description[contains(.,"{}")]]'.format(forbidden_string))

for item in found:
    item.getparent().remove(item)

print(etree.tostring(doc, encoding="unicode", pretty_print=True))

это печатает ...

<rss version="2.0">
    <channel>
        <title>My RSS Feed</title>
        <link href="https://www.examplefeedurl.com">Feed</link>
        <description/>
        <item>...</item>
        <item>...</item>
        <item>...</item>
        <item>...</item>
    </channel>
</rss>
0 голосов
/ 17 мая 2018

Рассмотрим XSLT , специализированный язык, предназначенный для преобразования XML-файлов, таких как условное удаление узлов по значению.Python lxml может запускать сценарии XSLT 1.0 и даже передавать параметр из сценария Python в XSLT (почти как передача параметров в SQL!).Таким образом, вы избегаете любых циклов for или if логики или перестройки дерева на уровне приложения.

XSLT (сохраните как файл .xsl, специальный файл .xmlфайл)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" cdata-section-elements="description"/>
  <xsl:strip-space elements="*"/>

  <!-- VALUE TO BE PASSED INTO FROM PYTHON -->
  <xsl:param name="search_string" />       

  <!-- IDENTITY TRANSFORM -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- KEEP ONLY item NODES THAT DO NOT CONTAIN $search_string -->
  <xsl:template match="channel">
    <xsl:copy>
      <xsl:apply-templates select="item[not(contains(description[2], $search_string))]"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Python (для демонстрации ниже выполняется два поиска с использованием опубликованного образца)

import lxml.etree as et

# LOAD XML AND XSL
doc = et.parse('Input.xml')
xsl = et.parse('XSLT_String.xsl')

# CONFIGURE TRANSFORMER
transform = et.XSLT(xsl)    

# RUN TRANSFORMATION WITH PARAM
n = et.XSLT.strparam('FORBIDDENSTRING')
result = transform(doc, search_string=n)

print(result)
# <?xml version="1.0"?>
# <rss version="2.0">
#   <channel>
#     <item>...</item>
#     <item>...</item>
#     <item>...</item>
#     <item>
#       <guid/>
#       <pubDate/>
#       <author/>
#       <title>Title of the item</title>
#       <link href="https://example.com" rel="alternate" type="text/html"/>
#       <description><![CDATA[<a href="https://example.com" target="_blank" rel="noopener noreferrer">View Example</a>]]></description>
#       <description><![CDATA[<p>This actually contains a bunch of text I want to work with. If this text contains certain strings, I want to get rid of the whole item.</p>]]></description>
#     </item>
#     <item>...</item>
#   </channel>
# </rss>

# RUN TRANSFORMATION WITH PARAM
n = et.XSLT.strparam('bunch of text')
result = transform(doc, search_string=n)

print(result)    
# <?xml version="1.0"?>
# <rss version="2.0">
#   <channel>
#     <item>...</item>
#     <item>...</item>
#     <item>...</item>
#     <item>...</item>
#   </channel>
# </rss>

# SAVE TO FILE
with open('Output.xml', 'wb') as f:
    f.write(result)
...