将一个包含多条记录和无效字符的XML文件按元素拆分成多个文本文件

Split an XML file with multiple records and invalid characters into multiple text files by element

我有一组 100K XML-ish(稍后会详细介绍)具有一致结构的遗留文件 - 具有多个日期和数据对记录的存档包装器。

我需要提取单个记录并将它们写入单个文本文件,但由于非法字符和随机 CR/space/tab 前导和尾随数据,我无法解析数据。

关于 XML 文件

这些文件是从已退役的系统继承而来的,无法重新生成。每个文件都非常小(不到 5 MB)。

每条数据记录对应一个日期记录:

vendor-1-records.xml

<Archive>
<Date>10 Jan 2019</Date>
<Data>Vendor 1 Record 1</Data>
<Date>12 Jan 2019</Date>
<Data>Vendor 1 Record 2</Data>
(etc)
</Archive>

vendor-2-records.xml

<Archive>
<Date>22 September 2019</Date>
<Data>Vendor 2 Record 1</Data>
<Date>24 September 2019</Date>
<Data>Vendor 2 Record 2</Data>
(etc)
</Archive>

...

vendor-100000-records.xml
<Archive>
<Date>12 April 2019</Date>
<Data>Vendor 100000 Record 1</Data>
<Date>24 October 2019</Date>
<Data>Vendor 100000 Record 2</Data>
(etc)
</Archive>


我想提取每条数据记录并使用日期条目定义一个唯一的文件名,然后将数据记录的内容写入该文件

filename: vendor-1-record-1-2019-1Jan-10.txt contains
file contents: Vendor 1 record 1 
(no tags, just the record terminated by CR)

filename: vendor-1-record-2-2019-1Jan-12.txt contains
file contents: Vendor 1 record 2 

filename: vendor-2-record-1-2019-9Sep-22.txt contains
file contents: Vendor 2 record 1

filename: vendor-2-record-2-2019-9Sep-24.txt contains
file contents: Vendor 2 record 2

问题 1:XML 数据记录中的非法字符

一个问题是元素包含 XML 像 Etree/etc 这样的库终止的多个字符,包括控制字符、格式化字符和各种 Alt+XXX 类型的字符。

我在网上搜索并找到了各种解决方法和正则表达式以及搜索和替换脚本,但似乎在 Python 中唯一有效的是 lxml 的 etree with recover=True。

然而,这并不总是有效,因为有些文件显然不是 UTF-8,所以我收到错误:

lxml.etree.XMLSyntaxError: Input is not proper UTF-8, indicate encoding !

问题 2 - 数据记录具有随机数量的前导和跟随 CR 和空格

对于我可以用lxml.etree解析的文件,实际的数据记录也包含在CR和随机空格中:

<Data>
(random numbers of CR + spaces and sometimes tabs)
*content<CR>*
(random numbers of CR + spaces and sometimes tabs)
</Data>

因此当我 运行

    parser = etree.XMLParser(recover=True)
    tree = etree.parse('vendor-1-records.xml', parser=parser)
    tags_needed = tree.iter('Data')
    for it in tags_needed:
        print (it.tag,it.attrib)

我得到了一组空数据标签(一个用于文件中的每个数据记录),例如

Data {}
Data {}

问题

  1. 有没有比Python的lxml更有效的language/module来忽略非法字符?正如我所说,我已经浏览了许多食谱博客 posts、SE 文章等以对 XML 进行预处理,但似乎没有任何效果 - 总有一个控制 character/etc 挂起解析器。

SE 建议 post 关于清理 XML,它引用了旧的 Atlassian 工具 (Stripping Invalid XML characters in Java)。我做了一些基本测试,它似乎可以工作,但可以接受其他建议。

  1. 我没有在 Python 中使用正则表达式 - 关于如何处理清理数据标签中 leading/trailing CR/space/tab 随机性的任何建议?我在该数据标记中想要的实际记录字符串在末尾也有一个 CR,并且可能还包含制表符,因此我不能只搜索和替换。也许有一种正则表达式的方法可以做到这一点,但我的正则表达式功能很弱。

对于我的问题 1 和问题 2,我有点解决了我自己的问题:

  • 问题 1(解析和无效字符)
    • 我 运行 通过批处理脚本在 (Stripping Invalid XML characters in Java) 中引用的 Atlassian jar 的整个文件集:
for %%f in (*.xml) do (
    java -jar atlassian-xml-cleaner-0.1.jar %%f >  clean\%%~f
)

此实用程序标准化了所有 XML 文件并使它们可由 lxml 解析。

  • 问题 2(数据元素中的 CR、空格、制表符)
    • lxml 的此配置去除了所有空格并处理了无效字符问题
from lxml import etree
    parser = etree.XMLParser(encoding = 'utf-8',recover=True,remove_blank_text=True)
    tree = etree.parse(filepath, parser=parser)

通过这两个步骤,我现在可以开始提取记录并将它们写入单个文件:

# for each date, finding the next item gives me the Data element and I can strip the tab/CR/whitespace:
for item in tree.findall('Date'):
        dt = parse_datestamp(item.text.strip())
        content = item.getnext().text.strip()