如何在 XML Python 中的同一父项中基于相同标签加入元素?
How join elements based on the same tag within the same parent in XML Python?
我有一个很长的 XML 结构如下:
<pages>
<page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
<textbox id="0" bbox="191.745,592.218,249.042,603.578">
<textline bbox="191.745,592.218,249.042,603.578">
<new_line>
<text font="QKWQNQ+ImprintMTnum-Bold" bbox="272.661,554.072,277.415,564.757" colourspace="DeviceGray" ncolour="0" size="10.685">1</text>
<text font="NUMPTY+ImprintMTnum" bbox="280.592,553.628,285.109,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">s</text>
<text font="NUMPTY+ImprintMTnum" bbox="284.964,553.628,290.760,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">v</text>
<text font="NUMPTY+ImprintMTnum" bbox="290.382,553.628,295.477,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">e</text>
<text font="NUMPTY+ImprintMTnum" bbox="295.333,553.628,301.707,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">n</text>
<text font="NUMPTY+ImprintMTnum" bbox="301.563,553.628,305.390,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">t</text>
<text font="NUMPTY+ImprintMTnum" bbox="305.245,553.628,311.620,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">u</text>
<text font="NUMPTY+ImprintMTnum" bbox="311.475,553.628,315.992,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">r</text>
<text font="NUMPTY+ImprintMTnum" bbox="315.847,553.628,320.942,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">a</text>
<text font="NUMPTY+ImprintMTnum" bbox="320.798,553.628,324.625,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">]</text>
<text font="NUMPTY+ImprintMTnum" bbox="324.480,553.628,327.384,566.110" colourspace="DeviceGray" ncolour="0" size="12.482"> </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="327.763,553.639,331.590,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">s</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="331.445,553.639,337.241,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">p</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="337.097,553.639,340.924,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">s</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="340.312,553.639,343.560,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">.</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="343.416,553.639,346.319,566.366" colourspace="DeviceGray" ncolour="0" size="12.727"> </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="346.709,553.639,352.505,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">a</text>
<text font="NUMPTY+ImprintMTnum" bbox="355.660,553.628,365.283,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">m</text>
<text font="NUMPTY+ImprintMTnum" bbox="365.139,553.628,368.387,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">i</text>
<text font="NUMPTY+ImprintMTnum" bbox="368.242,553.628,372.759,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">-</text>
</new_line>
</textline>
</textbox>
</page>
</pages>
实际 XML 更长,页数更多。
您可以看到 "size" 标签有不同的尺寸。我想在 <new_line>
标签中加入 text
标签的字母,它们具有相同的大小,保持它们出现的顺序。
我的预期输出是一个 XML 文件:
<pages>
<page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
<textbox id="0" bbox="191.745,592.218,249.042,603.578">
<textline bbox="191.745,592.218,249.042,603.578">
<new_line>
<text font="QKWQNQ+ImprintMTnum-Bold" bbox="272.661,554.072,277.415,564.757" colourspace="DeviceGray" ncolour="0" size="10.685">1</text>
<text font="NUMPTY+ImprintMTnum" bbox="280.592,553.628,285.109,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">sventura ] </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="327.763,553.639,331.590,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">sps. a</text>
<text font="NUMPTY+ImprintMTnum" bbox="355.660,553.628,365.283,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">mi-</text>
</textline>
</textbox>
</page>
</pages>
重要的是,必须保持字符的顺序。我尝试了很多方法但没有成功。如何实现我想要的输出?
编辑:我试过这样比较属性,但我需要保留标签:
words = []
root = ET.fromstring(xml)
pages = root.findall('.//page')
for page in pages:
previous_key = None
current_key = None
texts = page.findall('.//text')
for txt in texts:
if previous_key:
current_key = (txt.attrib.get('font',previous_key[0]),txt.attrib.get('size',previous_key[1]))
else:
current_key = (txt.attrib.get('font','empty'),txt.attrib.get('size','empty'))
if current_key != previous_key:
words.append([])
words[-1].append(txt.text)
previous_key = current_key
for group in words:
if group:
print(''.join(group))
您可以尝试以下方法:
- 遍历所有
new_line
个元素。对于所有这些 new_lines
:
- 查找所有子
text
元素并使用 findall
. 将其保存在列表中
- 使用
zip
使用当前和先前的元素迭代 text_list
(有关详细信息,请参阅 this discussion:zip(l[:-1], l[1:])
- 获取当前元素和上一个元素的大小
- 如果它们相等且不都为空:
- 获取当前和之前的文本
- 将它们添加到当前元素
- 使用
remove
移除前一个元素
代码
import lxml.etree as etree
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse('data.xml', parser)
root = tree.getroot()
# Iterate over //newline block
for new_line_block in tree.xpath('//new_line'):
# Find all "test" element in the new_line block
list_text_elts = new_line_block.findall('text')
# Iterate over all of them with the current and previous ones
for previous_text, current_text in zip(list_text_elts[:-1], list_text_elts[1:]):
# Get size elements
prev_size = previous_text.attrib.get('size')
curr_size = current_text.attrib.get('size')
# If they are equals and not both null
if curr_size == prev_size and curr_size is not None:
# Get current and previous text
pt = previous_text.text if previous_text.text is not None else ""
ct = current_text.text if current_text.text is not None else ""
# Add them to current element
current_text.text = pt + ct
# Remove preivous element
previous_text.getparent().remove(previous_text)
newtree = etree.tostring(root, encoding='utf-8', pretty_print=True)
newtree = newtree.decode("utf-8")
output.xml
<pages>
<page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
<textbox id="0" bbox="191.745,592.218,249.042,603.578">
<textline bbox="191.745,592.218,249.042,603.578">
<new_line>
<text font="QKWQNQ+ImprintMTnum-Bold" bbox="272.661,554.072,277.415,564.757" colourspace="DeviceGray" ncolour="0" size="10.685">1</text>
<text font="NUMPTY+ImprintMTnum" bbox="324.480,553.628,327.384,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">sventura] </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="346.709,553.639,352.505,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">sps. a</text>
<text font="NUMPTY+ImprintMTnum" bbox="368.242,553.628,372.759,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">mi-</text>
</new_line>
</textline>
</textbox>
</page>
</pages>
我让你适应它来处理不同的页面!
我有一个很长的 XML 结构如下:
<pages>
<page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
<textbox id="0" bbox="191.745,592.218,249.042,603.578">
<textline bbox="191.745,592.218,249.042,603.578">
<new_line>
<text font="QKWQNQ+ImprintMTnum-Bold" bbox="272.661,554.072,277.415,564.757" colourspace="DeviceGray" ncolour="0" size="10.685">1</text>
<text font="NUMPTY+ImprintMTnum" bbox="280.592,553.628,285.109,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">s</text>
<text font="NUMPTY+ImprintMTnum" bbox="284.964,553.628,290.760,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">v</text>
<text font="NUMPTY+ImprintMTnum" bbox="290.382,553.628,295.477,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">e</text>
<text font="NUMPTY+ImprintMTnum" bbox="295.333,553.628,301.707,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">n</text>
<text font="NUMPTY+ImprintMTnum" bbox="301.563,553.628,305.390,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">t</text>
<text font="NUMPTY+ImprintMTnum" bbox="305.245,553.628,311.620,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">u</text>
<text font="NUMPTY+ImprintMTnum" bbox="311.475,553.628,315.992,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">r</text>
<text font="NUMPTY+ImprintMTnum" bbox="315.847,553.628,320.942,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">a</text>
<text font="NUMPTY+ImprintMTnum" bbox="320.798,553.628,324.625,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">]</text>
<text font="NUMPTY+ImprintMTnum" bbox="324.480,553.628,327.384,566.110" colourspace="DeviceGray" ncolour="0" size="12.482"> </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="327.763,553.639,331.590,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">s</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="331.445,553.639,337.241,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">p</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="337.097,553.639,340.924,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">s</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="340.312,553.639,343.560,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">.</text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="343.416,553.639,346.319,566.366" colourspace="DeviceGray" ncolour="0" size="12.727"> </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="346.709,553.639,352.505,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">a</text>
<text font="NUMPTY+ImprintMTnum" bbox="355.660,553.628,365.283,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">m</text>
<text font="NUMPTY+ImprintMTnum" bbox="365.139,553.628,368.387,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">i</text>
<text font="NUMPTY+ImprintMTnum" bbox="368.242,553.628,372.759,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">-</text>
</new_line>
</textline>
</textbox>
</page>
</pages>
实际 XML 更长,页数更多。
您可以看到 "size" 标签有不同的尺寸。我想在 <new_line>
标签中加入 text
标签的字母,它们具有相同的大小,保持它们出现的顺序。
我的预期输出是一个 XML 文件:
<pages>
<page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
<textbox id="0" bbox="191.745,592.218,249.042,603.578">
<textline bbox="191.745,592.218,249.042,603.578">
<new_line>
<text font="QKWQNQ+ImprintMTnum-Bold" bbox="272.661,554.072,277.415,564.757" colourspace="DeviceGray" ncolour="0" size="10.685">1</text>
<text font="NUMPTY+ImprintMTnum" bbox="280.592,553.628,285.109,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">sventura ] </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="327.763,553.639,331.590,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">sps. a</text>
<text font="NUMPTY+ImprintMTnum" bbox="355.660,553.628,365.283,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">mi-</text>
</textline>
</textbox>
</page>
</pages>
重要的是,必须保持字符的顺序。我尝试了很多方法但没有成功。如何实现我想要的输出?
编辑:我试过这样比较属性,但我需要保留标签:
words = []
root = ET.fromstring(xml)
pages = root.findall('.//page')
for page in pages:
previous_key = None
current_key = None
texts = page.findall('.//text')
for txt in texts:
if previous_key:
current_key = (txt.attrib.get('font',previous_key[0]),txt.attrib.get('size',previous_key[1]))
else:
current_key = (txt.attrib.get('font','empty'),txt.attrib.get('size','empty'))
if current_key != previous_key:
words.append([])
words[-1].append(txt.text)
previous_key = current_key
for group in words:
if group:
print(''.join(group))
您可以尝试以下方法:
- 遍历所有
new_line
个元素。对于所有这些new_lines
:- 查找所有子
text
元素并使用findall
. 将其保存在列表中
- 使用
zip
使用当前和先前的元素迭代text_list
(有关详细信息,请参阅 this discussion:zip(l[:-1], l[1:])
- 获取当前元素和上一个元素的大小
- 如果它们相等且不都为空:
- 获取当前和之前的文本
- 将它们添加到当前元素
- 使用
remove
移除前一个元素
- 查找所有子
代码
import lxml.etree as etree
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse('data.xml', parser)
root = tree.getroot()
# Iterate over //newline block
for new_line_block in tree.xpath('//new_line'):
# Find all "test" element in the new_line block
list_text_elts = new_line_block.findall('text')
# Iterate over all of them with the current and previous ones
for previous_text, current_text in zip(list_text_elts[:-1], list_text_elts[1:]):
# Get size elements
prev_size = previous_text.attrib.get('size')
curr_size = current_text.attrib.get('size')
# If they are equals and not both null
if curr_size == prev_size and curr_size is not None:
# Get current and previous text
pt = previous_text.text if previous_text.text is not None else ""
ct = current_text.text if current_text.text is not None else ""
# Add them to current element
current_text.text = pt + ct
# Remove preivous element
previous_text.getparent().remove(previous_text)
newtree = etree.tostring(root, encoding='utf-8', pretty_print=True)
newtree = newtree.decode("utf-8")
output.xml
<pages>
<page id="1" bbox="0.000,0.000,462.047,680.315" rotate="0">
<textbox id="0" bbox="191.745,592.218,249.042,603.578">
<textline bbox="191.745,592.218,249.042,603.578">
<new_line>
<text font="QKWQNQ+ImprintMTnum-Bold" bbox="272.661,554.072,277.415,564.757" colourspace="DeviceGray" ncolour="0" size="10.685">1</text>
<text font="NUMPTY+ImprintMTnum" bbox="324.480,553.628,327.384,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">sventura] </text>
<text font="PYNIYO+ImprintMTnum-Italic" bbox="346.709,553.639,352.505,566.366" colourspace="DeviceGray" ncolour="0" size="12.727">sps. a</text>
<text font="NUMPTY+ImprintMTnum" bbox="368.242,553.628,372.759,566.110" colourspace="DeviceGray" ncolour="0" size="12.482">mi-</text>
</new_line>
</textline>
</textbox>
</page>
</pages>
我让你适应它来处理不同的页面!