python - 宽松的 XML python 解析器:解决 xml 标签重叠
问题描述
我正在寻找“错误”XML 输入的友好错误(在 BeautifulSoup 的术语中是宽松的)python 解析器。问题是标签重叠。一个示例输入是:
<trn>choya - <i><b>a cholla cactus </i> lat. <i>Cylindropuntia</b></trn></i>
我想得到什么和符合 XML 的结果,例如(我希望的好结果)
<trn>choya - <b><i>a cholla cactus </i> lat. <i>Cylindropuntia</i></b></trn>
BeautifulSoup
with html.parser
orhtml5lib
给了我别的东西(我不想要的坏结果):
<trn>choya - <i><b>a cholla cactus </b></i> lat. <i>Cylindropuntia</i></trn>
注意<i>
和<b>
标签的顺序。如果我将标记<i>
为斜体和<b>
粗体,那么好的答案是
choya -一种 cholla 仙人掌 lat。 柱眼
不好的答案是
choya -一种 cholla 仙人掌lat。柱眼
我也试过 old tidyhtml
,无法得到必要的结果。而对于新tidy-html5
的找不到python接口。你能帮我吗?
- 找到一个解析器可以完成这项工作
- 如果没有,建议算法或与此类算法有关的任何知识来源
谢谢!
解决方案
html.parser.HTMLParser
擅长解析标签汤,SAXXMLGenerator
类有方便的 API 可以根据事件生成 XML。
并非所有位都在此处实现,尤其是标签的“刚性”/“重量”约束(现在我们所做的只是用我们期望的使嵌套正确的方式关闭标签),但基本的想法似乎有效。
输出是
<trn>choya - <i><com>a cholla cactus </com> lat. <i>Cylindropuntia</i></i> native to US</trn>
这是有效的 XML,嵌套方式。
祝你好运!
import html.parser
import io
from xml.sax.saxutils import XMLGenerator
class Reconstructor(html.parser.HTMLParser):
def __init__(self):
super().__init__()
self.op_stream = []
self.tag_stack = []
def handle_startendtag(self, tag, attrs):
self.op_stream.append(('startendtag', (tag, attrs)))
def handle_starttag(self, tag, attrs):
self.op_stream.append(('starttag', (tag, attrs)))
self.tag_stack.append(tag)
def handle_endtag(self, tag):
expected_tag = self.tag_stack[-1]
if tag != expected_tag:
print('mismatch closing <{}>, expected <{}>'.format(tag, expected_tag))
# TODO: implement logic to figure out the correct order for the tags here
# and reorder tag_stack accordingly.
stack_tag = self.tag_stack.pop(-1)
self.op_stream.append(('endtag', (stack_tag, tag)))
def handle_charref(self, name):
self.op_stream.append(('charref', (name,)))
def handle_entityref(self, name):
self.op_stream.append(('entityref', (name,)))
def handle_data(self, data):
self.op_stream.append(('data', (data,)))
def handle_comment(self, data):
self.op_stream.append(('comment', (data,)))
def handle_decl(self, decl):
self.op_stream.append(('decl', (decl,)))
def handle_pi(self, data):
self.op_stream.append(('pi', (data,)))
def generate_xml(self):
stream = io.StringIO()
xg = XMLGenerator(stream, encoding='utf-8')
for op, args in self.op_stream:
if op in ('startendtag', 'starttag'):
tag, attrib = args
xg.startElement(tag, dict(attrib))
if op == 'startendtag':
xg.endElement(tag)
elif op == 'endtag':
tag = args[0]
xg.endElement(tag)
elif op == 'data':
xg.characters(args[0])
else:
raise NotImplementedError('Operator not implemented: %s' % op)
xg.endDocument()
return stream.getvalue()
xr = Reconstructor()
xr.feed('<trn>choya - <i><com>a cholla cactus </i> lat. <i>Cylindropuntia</com></trn> native to US</i>')
y = xr.generate_xml()
print(y)
推荐阅读
- c# - Process.MainWindowHandle 在 .NET Framework 中不为零,但在 .NET Core 中为零,除非调试
- python - 在 Google Colab 上使用 Cartopy:每次都崩溃
- python - 在 xarray 中按多个时间坐标分组的有效解决方法
- .net - 在 2 个 Web 应用程序之间共享缓存
- javascript - 动态导入JS库?
- javascript - 防止div顶部的绝对定位元素阻止div的悬停样式?
- python-3.x - LineString - 获取坐标作为 DataFrame
- ruby - 在 Windows 10 中安装 ruby 2.6.5
- python - 分配某个变量,然后将值分配给嵌套字典而不修改原始变量
- flutter - 颤振/飞镖 - '列表
' 不是类型 'List 的子类型 '