Mihail
Mihail

Reputation: 395

Strip some tags and rename them

Using lxml library, having this doc xml file I want to strip some tags and rename them: doc.xml

<html>
    <body>
        <h5>Fruits</h5>
        <div>This is some <span attr="foo">Text</span>.</div>
        <div>Some <span>more</span> text.</div>
        <h5>Vegetables</h5>
        <div>Yet another line <span attr="bar">of</span> text.</div>
        <div>This span will get <span attr="foo">removed</span> as well.</div>
        <div>Nested elements <span attr="foo">will <b>be</b> left</span> alone.</div>
        <div>Unless <span attr="foo">they <span attr="foo">also</span> match</span>.</div>
    </body>
</html>

Instead of html,body to wrap everything in 'p tag' and instead of having h5 and each div to wrap everything as example bellow using lxml: My question how from one format to wrap everything in the format bellow?

<p>
<h5 title='Fruits'> 
<div>This is some <span attr='foo'>Test</span>.</div>
<div>Some<span>more</span>text.</div>
</h5>
<h5 title='Vegetables'>
<div>Yet another line <span attr='bar'>of</span>text.</div>
....
</h5>
</p>

Using lxml, stripping the tags:

tree = etree.tostring(doc.xml)
tree1 = lxml.html.fromstring(tree)
etree.strip_tags(tree1, 'body')

Does anyone has any idea to this?

Upvotes: 1

Views: 73

Answers (2)

Cole Tierney
Cole Tierney

Reputation: 10324

Here's an xslt solution using lxml. It offloads the processing to libxml. I've added comments to the transformation stylesheet:

from lxml import etree

xsl = etree.XML('''
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <p>
            <xsl:apply-templates select="html/body"/>
        </p>
    </xsl:template>

    <!-- match body, but do not add content; this excludes /html/body elements -->
    <xsl:template match="body">
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="h5">
        <!-- record the current h5 title -->
        <xsl:variable name="title" select="."/>
        <h5>
            <xsl:attribute name="title">
                <xsl:value-of select="$title" />
            </xsl:attribute>

            <xsl:for-each select="following-sibling::div[preceding-sibling::h5[1] = $title]">
                <!-- deep copy of each consecutive div following the current h5 element -->
                <xsl:copy-of select="." />
            </xsl:for-each>
        </h5>
    </xsl:template>

    <!-- match div, but do not output anything since we are copying it into the new h5 element -->
    <xsl:template match="div" />
</xsl:stylesheet>
''')

transform = etree.XSLT(xsl)
with open("doc.xml") as f:
    print(transform(etree.parse(f)), end='')

If the stylesheet is stored in a file name doc.xsl, the same results can be achieved using the libxml utility xsltproc:

xsltproc doc.xsl doc.xml

Results:

<?xml version="1.0"?>
<p>
  <h5 title="Fruits">
    <div>This is some <span attr="foo">Text</span>.</div>
    <div>Some <span>more</span> text.</div>
  </h5>
  <h5 title="Vegetables">
    <div>Yet another line <span attr="bar">of</span> text.</div>
    <div>This span will get <span attr="foo">removed</span> as well.</div>
    <div>Nested elements <span attr="foo">will <b>be</b> left</span> alone.</div>
    <div>Unless <span attr="foo">they <span attr="foo">also</span> match</span>.</div>
  </h5>
</p>

Upvotes: 0

wwii
wwii

Reputation: 23773

  • Create a new document with just a <p> tag.
  • Iterate over the descendants of the <body> tag in the original document.
    • add tags from the original to the new document - as descendants of its <p> tag
      • if you encounter an <h5> tag; add the <h5> tag to <p> tag
        • and add subsequent tags as descendants to it (the <h5>)

Upvotes: 2

Related Questions