如何在Python中的两个xml标记之间获取整个内容?
我尝试在开始的xml标记和结束的xml标记之间获取整个内容 在像下面的如何在Python中的两个xml标记之间获取整个内容?,python,xml,xml-parsing,lxml,Python,Xml,Xml Parsing,Lxml,我尝试在开始的xml标记和结束的xml标记之间获取整个内容 在像下面的标题这样的直格中获取内容很容易,但是如果使用混合内容并且我想保留内部标记,如何在标记之间获取整个内容 <?xml version="1.0" encoding="UTF-8"?> <review> <title>Some testing stuff</title> <text sometimes="attribute">Some text with <e
标题
这样的直格中获取内容很容易,但是如果使用混合内容并且我想保留内部标记,如何在标记之间获取整个内容
<?xml version="1.0" encoding="UTF-8"?>
<review>
<title>Some testing stuff</title>
<text sometimes="attribute">Some text with <extradata>data</extradata> in it.
It spans <sometag>multiple lines: <tag>one</tag>, <tag>two</tag>
or more</sometag>.</text>
</review>
一些测试材料
一些包含数据的文本。
它跨越多条线:一、二
或者更多。
我想要的是两个text
标记之间的内容,包括任何标记:一些包含数据的文本。它跨越多行:一行、两行或多行。
现在我使用正则表达式,但它有点混乱,我不喜欢这种方法。我倾向于基于XML解析器的解决方案。我查看了
minidom
、etree
、lxml
和BeautifulSoup
,但没有找到解决此问题的方法(整个内容,包括内部标记)。刚刚找到了解决方案,非常简单:
In [31]: t = x.find('text')
In [32]: t
Out[32]: <Element text at 0xa87ed74>
In [33]: list(t.itertext())
Out[33]: ['Some text with ', 'data', ' in it.']
In [34]: ''.join(_)
Out[34]: 'Some text with data in it.'
[31]中的:t=x.find('text'))
In[32]:t
出[32]:
在[33]中:列表(t.itertext())
Out[33]:[“某些文本中有”“数据”“,”。]
在[34]中:''.join(u)
Out[34]:“一些包含数据的文本。”
itertext
绝对是这条路
编辑://对不起,我以为你只想要孩子们之间的文字,我的坏这里有一些对我和你的样本都有效的东西:
from lxml import etree
doc = etree.XML(
"""<?xml version="1.0" encoding="UTF-8"?>
<review>
<title>Some testing stuff</title>
<text>Some text with <extradata>data</extradata> in it.</text>
</review>"""
)
def flatten(seq):
r = []
for item in seq:
if isinstance(item,(str,unicode)):
r.append(unicode(item))
elif isinstance(item,(etree._Element,)):
r.append(etree.tostring(item,with_tail=False))
return u"".join(r)
print flatten(doc.xpath('/review/text/node()'))
从lxml导入etree
doc=etree.XML(
"""
一些测试材料
一些包含数据的文本。
"""
)
def展平(序号):
r=[]
对于序号中的项目:
如果isinstance(项,(str,unicode)):
r、 附加(unicode(项目))
elif isinstance(项目(etree.\U元素)如下所示:
r、 追加(etree.tostring(项,带\u tail=False))
返回u“”。加入(r)
打印展开(doc.xpath('/review/text/node()'))
收益率:
Some text with <extradata>data</extradata> in it.
一些包含数据的文本。
xpath选择
元素的所有子节点,如果它们是字符串/unicode子类(
),则直接将它们呈现为unicode;如果是元素,则调用etree.tostring
,with_tail=False
可避免尾部重复
如果存在其他节点类型,您可能需要处理它们。这对于*,使用parse()
和tostring()
函数非常容易:
from lxml.etree import parse, tostring
首先解析文档并获取元素(我使用的是XPath,但您可以使用任何您想要的内容):
tostring()
函数返回元素的文本表示形式:
>>> tostring(element)
'<text>Some <text>text</text> with <extradata>data</extradata> in it.</text>\n'
请注意,str.replace()
接收到1作为第三个参数,因此它将只删除第一个出现的开始标记。一个人也可以用结束标记来完成。现在,我们通过-1代替1:
>>> tostring(element).replace('</%s>'%element.tag, '', -1)
'<text>Some <text>text with <extradata>data</extradata> in it.\n'
获取第二个结果字符串:
>>> tostring(element).split('>', 1)[1]
'Some <text>text</text> with <extradata>data</extradata> in it.</text>\n'
>>>tostring(元素).split('>',1)[1]
'一些包含数据的文本。\n'
然后再拆分它:
>>> tostring(element).split('>', 1)[1].rsplit('</', 1)
['Some <text>text</text> with <extradata>data</extradata> in it.', 'text>\n']
>>tostring(元素).split('>',1)[1].rsplit('\n']
最后得到第一个结果:
>>> tostring(element).split('>', 1)[1].rsplit('</', 1)[0]
'Some <text>text</text> with <extradata>data</extradata> in it.'
>>tostring(元素).split('>',1)[1].rsplit('
或:
[6]中的:e=t.xpath('//text')[0]
在[7]中:(e.text+''.join(map(etree.tostring,e)).strip()
Out[7]:“一些包含数据的文本。”
我喜欢上面@Marcin的解决方案,但是我发现当使用他的第二个选项(转换子节点,而不是树的根)时,它不会处理实体
上面的His代码(修改为添加实体):
使用裸/未转义的“&”字符而不是正确的实体(“&;”)
我的解决方案是使用在节点级别(而不是在所有子节点上)调用etree.tostring,然后使用正则表达式去除开始和结束标记:
import re
from lxml import etree
t = etree.XML("""<?xml version="1.0" encoding="UTF-8"?>
<review>
<title>Some testing stuff</title>
<text>this & that.</text>
</review>""")
e = t.xpath('//text')[0]
xml = etree.tostring(e)
inner = re.match('<[^>]*?>(.*)</[^>]*>\s*$', xml, flags=re.DOTALL).group(1)
print inner
我使用re.DOTALL来确保它适用于包含换行符的XML。我想,我可以通过x.find('text').get_text()获得相同的结果
。但是这种方法排除了内部标记,我需要它们。实际上,这并不能以任何方式解决OP问题。它需要维护内部标记。它确实维护内部标记,只是不超过一个级别,请参见我的编辑,itertext
get的Everything迭代所有子级,而不仅仅是文本。文本替换是这里增加了很多脆弱性。如果您的输入文件上碰巧有属性?名称空间前缀?我觉得使用这种方法,我不会比纯正则表达式获得太多好处。因为开始标记至少有一个属性,它也会变得脆弱。replace(“”%element.tag,,-1)
应该可以工作,但我不能使用.replace('%element.tag',1)
因为有一个或多个属性,所以我必须再次使用regex(或者类似于内容[content.index('>'):]
)等。这可以写得更简洁。使用这一行:'.join(el if isinstance(el,str)els lxml.etree.tostring)(el,带_tail=False)用于doc.xpath('/review/text/node()')中的el
你可能只是不加区别地使用tostring
。@Marcin:当我尝试时,tostring
抱怨它无法序列化\u ElementStringResult
OP想要获取特定元素的内容。你的解决方案在这种情况下不起作用,至少不能直接使用。我得到了一个带有e的元素=t.xpath('//text')[0]
并尝试了它('.join(map(etree.tostring,e))
),但结果是'data in it.
。
需要在更多的情况下进行测试,但您的最后一个示例对我来说效果很好(到目前为止)。当使用find
而不是xpath
时,它也适用于标准的etree
。
>>> tostring(element).split('>', 1)[1]
'Some <text>text</text> with <extradata>data</extradata> in it.</text>\n'
>>> tostring(element).split('>', 1)[1].rsplit('</', 1)
['Some <text>text</text> with <extradata>data</extradata> in it.', 'text>\n']
>>> tostring(element).split('>', 1)[1].rsplit('</', 1)[0]
'Some <text>text</text> with <extradata>data</extradata> in it.'
from lxml import etree
t = etree.XML(
"""<?xml version="1.0" encoding="UTF-8"?>
<review>
<title>Some testing stuff</title>
<text>Some text with <extradata>data</extradata> in it.</text>
</review>"""
)
(t.text + ''.join(map(etree.tostring, t))).strip()
In [50]: (t.text + ''.join(map(etree.tostring, t))).strip()
Out[50]: '<title>Some testing stuff</title>\n <text>Some text with <extradata>data</extradata> in it.</text>'
In [6]: e = t.xpath('//text')[0]
In [7]: (e.text + ''.join(map(etree.tostring, e))).strip()
Out[7]: 'Some text with <extradata>data</extradata> in it.'
from lxml import etree
t = etree.XML("""<?xml version="1.0" encoding="UTF-8"?>
<review>
<title>Some testing stuff</title>
<text>this & that.</text>
</review>""")
e = t.xpath('//text')[0]
print (e.text + ''.join(map(etree.tostring, e))).strip()
this & that.
import re
from lxml import etree
t = etree.XML("""<?xml version="1.0" encoding="UTF-8"?>
<review>
<title>Some testing stuff</title>
<text>this & that.</text>
</review>""")
e = t.xpath('//text')[0]
xml = etree.tostring(e)
inner = re.match('<[^>]*?>(.*)</[^>]*>\s*$', xml, flags=re.DOTALL).group(1)
print inner
this & that.