python lxml append element after another element
Solution 1
Instead of appending to contentnav
, go up to the parent (contentdiv
) and insert
the new div
at a particular index. To find that index, use contentdiv.index(contentnav)
, which gives the index of contentnav
within contentdiv
. Adding one to that gives the desired index.
import lxml.etree as ET
content='''\
<div id="contents">
<div id="content_nav">
something goes here
</div>
<p>
some contents
</p>
</div>
'''
tree = ET.fromstring(content, parser=ET.HTMLParser())
contentnav = tree.find(".//div[@id='content_nav']")
contentdiv = contentnav.getparent()
contentdiv.insert(contentdiv.index(contentnav)+1,
ET.XML("<div style='clear: both'></div>"))
print(ET.tostring(tree))
yields
<html><body><div id="contents">
<div id="content_nav">
something goes here
</div>
<div style="clear: both"/><p>
some contents
</p>
</div></body></html>
Solution 2
Use addprevious
and addnext
for prepending and appending siblings.
An lxml.etree _Element
has two methods: addprevious
and addnext
for doing exactly what you want.
import lxml.etree as ET
content='''\
<div id="contents">
<div id="content_nav">
something goes here
</div>
<p>
some contents
</p>
</div>
'''
tree = ET.fromstring(content, parser=ET.HTMLParser())
contentnav = tree.find(".//div[@id='content_nav']")
contentnav.addnext(ET.XML("<div style='clear: both'></div>"))
print(ET.tostring(tree))
Output:
<html><body><div id="contents">
<div id="content_nav">
something goes here
</div><div style="clear: both"/>
<p>
some contents
</p>
</div>
</body></html>
Solution 3
I believe that a generic function addressing the question "insert an element after another element" might be useful, even if it's just a reformulation of the accepted answer:
def insert_after(element, new_element):
parent = element.getparent()
parent.insert(parent.index(element)+1, new_element)
which allows to insert a new_element
after an existing element
with just
insert_after(element, new_element)
Comments
-
Tu Hoang almost 2 years
I have the following HTML markup
<div id="contents"> <div id="content_nav"> something goes here </div> <p> some contents </p> </div>
To fix some CSS issue, I want to append a div tag
<div style="clear:both"></div>
after thecontent_nav
div like this<div id="contents"> <div id="content_nav"> something goes here </div> <div style="clear:both"></div> <p> some contents </p> </div>
I am doing it this way:
import lxml.etree tree = lxml.etree.fromString(inputString, parser=lxml.etree.HTMLParser()) contentnav = tree.find(".//div[@id='content_nav']") contentnav.append(lxml.etree.XML("<div style='clear: both'></div>"))
But that doesn't append the new
div
right aftercontent_nav
div but inside.<div id="content_nav"> something goes here <div style="clear:both"></div> </div>
Is there any way to add a
div
in the middle ofcontent_nav
div and somep
like that insidecontents
?Thanks
-
Tu Hoang over 12 yearsYeah, I did the same thing after asking the question. :)
-
shrewmouse over 6 yearsAll that you've done is re-implement
element.appendnext()
. -
mmj over 6 years@Shrewmouse I guess you mean
element.addnext()
. I don't know when it was added to the API but now it is definitively the best solution. -
pguardiario almost 5 yearsShould probably be ET.HTML in this case