首頁  >  問答  >  主體

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

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

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

str="<a>1</a>...<b>A</b>...<a>2</a>...<b>B</b>"

以下两种re分别匹配<a></a>之间内容,<b></b>之间内容

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

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

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

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

p4=re.compile(r'(?<=<a>)(.*?)(?=</a>)(?<=<b>)(.*?)(?=</b>))
#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'(?<=<a>)(.*?)(?=</a>).+?(?<=<b>)(.*?)(?=</b>))
#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的文本分类器。数据源就是抓取语料文档中标签<PLACES><TEXT>内的的信息,并且一一对应起来。这篇文章提到了该语料的处理体会:

这些数据文件貌似是有一定的格式的,我刚开始也试图把他们当做标准的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.

PHP中文网PHP中文网2743 天前950

全部回覆(4)我來回復

  • 天蓬老师

    天蓬老师2017-04-17 11:56:16

    雷雷

    回覆
    0
  • 高洛峰

    高洛峰2017-04-17 11:56:16

    說了幾次…我都嫌煩了…
    XML的有自己的函式庫lxml,BS4

    正規就應該用來幹它合適的活,而不是費那種腦子整XML

    回覆
    0
  • ringa_lee

    ringa_lee2017-04-17 11:56:16

    雷雷

    回覆
    0
  • 天蓬老师

    天蓬老师2017-04-17 11:56:16

    補充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 < B</a> is A < B instead of A < 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.
    這種「我把事搞砸了,就隨意地調整直到事情又工作」的方式是不可接受的。

    回覆
    0
  • 取消回覆