正则表达式 - python Regex:匹配XML标签中内容

 思紅顏0114 发布于 2022-11-06 10:47

总结
Parser具有通用性,处理良性的xml,解析完后你可以得到xml文档任何位置的信息.优先选择
Regex具有针对性,处理非良性的xml,当你预先知道需要匹配的信息位置,尝试Regex
在Update3中给出了一个实例。

我现在有这样的一个字符串:

str="1...A...2...B"

以下两种re分别匹配之间内容,之间内容

p1=re.compile(r'(?<=)(.*?)(?=)')
#p1.findall(str)=['1','2']
p2=re.compile(r'(?<=)(.*?)(?=)')
#p2.findall(str)=['A','B']

问题1:是否能利用'|'操作,使一个pattern来完成如下的匹配:

p3=re.compile(r'(?<=)|(?<=)(.*?)(?=)|(?=)')
#p3.findall(str)=['1','2','A','B']

问题2:能否使用group来完成如下匹配:

p4=re.compile(r'(?<=)(.*?)(?=)(?<=)(.*?)(?=))
#p4.findall(str)=[('1','A'),('2','B')]

Updata1问题2已解决
问题2来源手册中写到:

If one or more groups are present in the pattern, return a list of groups; this will be a list of tuples if the pattern has more than one group.

p=re.compile(r'(?<=)(.*?)(?=).+?(?<=)(.*?)(?=))
#p.findall(str)=[('1','A'),('2','B')]

再推荐一个Python正则交互式的网站regex101。
改变正则式,匹配结果能即时更新,很方便测试自己的正则式是否正确。

update2
匹配xml文档的内容,Regex or Parser?
Parser适合解析,Parser更robust一些,解析完后你可以得到xml文档任何位置的信息;Regex适合针对性的匹配,处理非良性的xml时,当你预先知道需要匹配的信息位置时,尝试Regex。在**Update3中给出了一个实例。

Update3
问题的回答逐渐转变到同一个声音告诉你"一定不要用Regex解析xml"。
对此我的粗浅看法:
1.我的问题是"Regex匹配XML内容,不是用Regex来解析XML文档"。问题源自基于新闻语料Reuters-21578的文本分类器。数据源就是抓取语料文档中标签内的的信息,并且一一对应起来。这篇文章提到了该语料的处理体会:

这些数据文件貌似是有一定的格式的,我刚开始也试图把他们当做标准的xml文档来处理(因为下载包里还像模像样的包含了一个SGML DTD 的文件),但老是报错。最终发现很多的记录格式是错误的,而且错误千奇百怪。所以干脆放弃,直接把它们全部看做文本文件来处理得了。

我用python中lxml库来尝试parse,结果当然是parse失败,error_log中提示很多mismatch.当然lxml也提供了处理broken xml的方法,即recover - try hard to parse through broken XML.recover的代价是不易处理PLACESTEXT 信息的对应关系。但换作Regex,匹配规则就类比上述问题:只有当两个group同时匹配到内容,这样的配对信息就保留。如果其中一个为空,这样的配对信息就丢弃。

2.具体问题具体分析,少说绝对
这个问题同样在stackoverflow中出现,回答各式各样。
得票最高的是"不要用Regex来解析xml"。同时也有其它一些启发性的回答,摘录一个

While it is true that asking regexes to parse arbitrary HTML is like asking Paris Hilton to write an operating system, it's sometimes appropriate to parse a limited, known set of HTML.
If you have a small set of HTML pages that you want to scrape data from and then stuff into a database, regexes might work fine. For example, I recently wanted to get the names, parties, and districts of Australian federal Representatives, which I got off of the Parliament's Web site. This was a limited, one-time job.
Regexes worked just fine for me, and were very fast to set up.

4 个回答
  • 补充3:

    这里把直接面对问题的积极回答,从补充2里单独提出来。

    对于这个匹配问题本身,我的建议是:

    • 如果AB是配对的,那最好能够观察是否存在断行、父标签等,能用来区分每个<a><b>组的明确依据。例如有这样的数据源那是最好:<r><a></a><b></b></r>
    • 如果没有,那就只好想其他办法了。中心思想仍然是“尽量别被坑”。
    • 主要坑人的地方在于:可能会出现连续的<a><b>。例如ABABAAABAB,那么中间的3个A中前两个最好是丢弃。
    • 所以稳妥起见,最好不要一次到位的<a>(?P<texta>.*)</a>.*<b>(?P<textb>.*)</b>
    • 我推荐的用法是:(?:<(?P<tagname>a)>(?P<text>.*)</a>)|(?:<(?P<tagname>b)>(?P<text>.*)</b>),一次把所有的标签不论是AB全部拿到。
    • 然后扫描一遍,只把相邻的AB看作一组有效数据。

    注意以上代码全部空手写的,没做测试甚至没详细看,仅供表意参考。


    补充2:

    XML不标准是一个合理的理由。应对这一实际情况,我的建议是:

    • 尽量选用支持混合/容忍模式的XML解释器。容忍一些XML的毛病其实是很多HTML分析器的底层基础。
    • 不做一步到位的事情。先把每条记录断开,再在每条记录的范围内,去分析各个字段的详情。这样至少可以把所有的问题控制在1条记录之内,免于“牵一发而动全身”。(参考这个答案)
    • 永远把Regex作为最后选择

    另外我必须非常严肃的批评楼主:你又是一个XY PROBLEM的反面教材。

    最开始只拿出一个非常简单和规范的XML片段,结果两次Update才把最后“XML可能不规范”这么重要的内情说出口。

    你这是故意留着什么王牌,用于被批评的时候维护你脆弱的自尊吗?!

    你还能不能再脆弱点!!!


    补充1:

    不能同意问题正文的Update 2

    用正则匹配正则XML,就意味着只要在XML的规则之内挖几个小坑,偷懒的程序员就会掉进去。

    Regex解析XML我认为就是“绝对不适合实际应用”,不该是有什么疑问的事情。如果硬要做,那就意味着实际做出的程序只能适应一些特定情况。并且如果数据源有丝毫的改动(例:程序员把少量标签临时注释掉了)也可能需要人类做hotfix。结果就是把摩天大楼盖在散沙之上,程序员辛苦写出的程序不久就不能用了。这将是一场永无止境的循环。

    “只要是事情,就都没有绝对”,这个判断本身难道不是“绝对”的?我认为原则就是原则,部分问题有明确的是非之分,有些浑水是不能搅的。如果在原则问题上这里可以退后一点,那里可以放过一些,那这样写出的程序,恐怕只能陷入一种神出鬼没、不可捉摸的结局。

    SOF上有其他说法太正常了,难道见到了就非得认可?!

    唯一能肯定的就是如果用XML当作学习正则的例子,倒是做做无妨。


    我宁愿把这个问题彻底掀翻。

    怎么总有人喜欢用正则表达式解析XML/HTML啊?!

    啥时候用Parser还是Regex解析XML居然能够“各有所长”,还成了个能够商量、可以讨论的问题了啊??!!

    这是需要讨论的问题吗???!!!

    永远不要用正则取代XML解释器

    铁的原则!

    一步不退!

    再简单的XML也不行!

    因为你不能用一个简单的正则表达式,覆盖XML所有的复杂结构。XML的情况之多,什么地方虽然怪异但正确,什么地方只是可以容忍,什么地方应该干脆报错,这不是正则覆盖的了的。

    例如以下几种情况,扪心自问:如果用正则来做,会条条都考虑到吗?

    • 注释:<!-- this <a></a> should be ignored -->
    • 无解析文本段:“CDATA Section” <![CDATA[ this <a></a> should be ignored too ]]>
    • 实体的转义:content of <a>A &lt; B</a> is A < B instead of A &lt; B
    • 自封闭标签:<a /> is an equivalent to <a></a>, shouldn't be ignored
    • 一个元素有多个属性值时,属性的顺序可能是随意的

    所以正则和XML解释器是完全不同复杂度的两个东西。混用的结果就是:代价一定会在某一天连本带利还给你。不要因为“这样做能达到目的”,就放弃写坚固的代码。这是用身体上看似的“勤快”,去掩盖思想上绝对的懒惰。

    参加过中学信息学奥赛,或大学ACM/ICPC的玩家们都明白一个浅显的道理:

    样例数据能通过,和整道题能够Accepted是两个完全不同的概念。

    实际的编程也是如此。对于这个需求,考虑到XML是一个标准,所以涉及XML的代码必须要“保证”他对于符合标准的XML都能工作,而不是不断的折腾让代码“看起来”适用于你设定的片面的“样例数据”

    看看这篇文章《Linux 2.6.39-rc3的一个插曲》,记住Linus Torvalds的教导:

    This kind of “I broke things, so now I will jiggle things randomly until they unbreak” is not acceptable.
    这种“我把事搞砸了,就随意地调整直到事情又工作”的方式是不可接受的。

    2022-11-12 01:56 回答
  • str = '<a>1</a>...<b>A</b>...<a>2</a>...<b>B</b>'
    p5 = re.compile(r'(?<=<[ab]>)(.*?)(?=</[ab]>)')
    p5.findall(str) # ['1', 'A', '2', 'B']
    
    2022-11-12 01:56 回答
  • 说了多少次……我都嫌烦了……
    XML的有自己的库lxml,BS4

    正则就应该用来干它合适的活,而不是费那种脑子整XML

    2022-11-12 01:56 回答
  • >>> str = "<a>1</a>...<b>A</b>...<a>2</a>...<b>B</b>"                                                                                                  
    >>> p3 = re.compile(r'(?<=<(?P<tag>a|b)>)(.*?)(?=</(?P=tag)>)')
    >>> [m.group() for m in p3.finditer(str)]                                                                                                              
    ['1', 'A', '2', 'B']
    >>> p3.findall(str)
    [('a', '1'), ('b', 'A'), ('a', '2'), ('b', 'B')]
    
    2022-11-12 01:56 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有