正則表達式re.sub替換不完整的問題現象及其根本緣由

問題描述

問題的原由來自於一段正則替換。爲了從一段HTML代碼裏面提取出正文,去掉全部的HTML標籤和屬性,能夠寫一個Python函數:html

import re


def remove_tag(html):
    text = re.sub('<.*?>', '', html, re.S)
    return text
複製代碼

這段代碼的使用了正則表達式的替換功能re.sub。這個函數的第一個參數表示須要被替換的內容的正則表達式,因爲HTML標籤都是使用尖括號包起來的,所以使用<.*?>就能夠匹配全部<xxx yyy="zzz"></xxx>python

第二個參數表示被匹配到的內容將要被替換成什麼內容。因爲我須要提取正文,那麼只要把全部HTML標籤都替換爲空字符串便可。第三個參數就是須要被替換的文本,在這個例子中是HTML源代碼段。正則表達式

至於re.S,在4年前的一篇文章中我講到了它的用法:Python正則表達式中的re.S編程

如今使用一段HTML代碼來測試一下:bash

import re


def remove_tag(html):
    text = re.sub('<.*?>', '', html, re.S)
    return text


source_1 = ''' <div class="content">今天的主角是<a href="xxx">kingname</a>,咱們掌聲歡迎!</div> '''


text = remove_tag(source_1)
print(text)
複製代碼

運行效果以下圖所示,功能徹底符合預期編程語言

再來測試一下代碼中有換行符的狀況:函數

import re


def remove_tag(html):
    text = re.sub('<.*?>', '', html, re.S)
    return text

source_2 = ''' <div class="content"> 今天的主角是 <a href="xxx">kingname</a> ,咱們掌聲歡迎! </div> '''
text = remove_tag(source_2)
print(text)
複製代碼

運行效果以下圖所示,徹底符合預期。 測試

通過測試,在絕大多數狀況下,可以從的HTML代碼段中提取出正文。但也有例外。ui

例外狀況

有一段HTML代碼段比較長,內容以下:url

<img></span><span>碰見kingname</span></a ><a  ><span class='url-icon'>< img '></span><span >溫柔</span></a ><a ><span >#青南#</span></a > <br />就在這裏…<br />個人小侯爺呢??? 複製代碼

運行效果以下圖所示,最後兩個HTML標籤替換失敗。

一開始我覺得是HTML裏面的空格或者引號引發的問題,因而我把HTML代碼進行簡化:

<img></span><span>碰見kingname</span></a><a><span><img></span><span>溫柔</span></a><a><span>#青南#</span></a><br/>就在這裏…<br/>個人小侯爺呢
複製代碼

問題依然存在,以下圖所示。

並且更使人驚訝的是,若是把第一個標籤<img>刪了,那麼替換結果裏面就少了一個標籤,以下圖所示。

實際上,不只僅是刪除第一個標籤,前面任意一個標籤刪了均可以減小結果裏面的一個標籤。若是刪除前面兩個或以上標籤,那麼結果就正常了。

答疑解惑

這個看起來很奇怪的問題,根本緣由在re.sub的第4個參數。從函數原型能夠看到:

def sub(pattern, repl, string, count=0, flags=0)
複製代碼

第四個參數是count表示替換個數,re.S若是要用,應該做爲第五個參數。因此若是把remove_tag函數作一些修改,那麼結果就正確了:

def remove_tag(html):
    text = re.sub('<.*?>', '', html, flags=re.S)
    return text
複製代碼

那麼問題來了,把re.S放在count的位置,爲何代碼沒有報錯?難道re.S是數字?實際上,若是打印一下就會發現,re.S確實能夠做爲數字:

>>> import re
>>> print(int(re.S))
16
複製代碼

如今回頭數一數出問題的HTML代碼,發現最後多出來的兩個<br>標籤,剛恰好是第17和18個標籤,而因爲count填寫的re.S能夠當作16來處理,那麼Python就會把前16個標籤替換爲空字符串,從而留下最後兩個。

至此問題的緣由搞清楚了。

這個問題沒有被及早發現,有如下幾個緣由:

  1. 被替換的HTML代碼是代碼段,大多數狀況下HTML標籤不足16個,因此問題被隱藏。
  2. re.S是一個對象,但也是數字,count接收的參數恰好也是數字。在不少編程語言裏面,常量都會使用數字,而後用一個有意義的大寫字母來表示。
  3. re.S 處理的狀況是<div class="123" \n> 而不是<div class="123">\n</div>但測試的代碼段標籤都是第二種狀況,因此在代碼段裏面實際上加不加re.S效果是同樣的。

關注公衆號:未聞Code

個人公衆號:未聞Code

相關文章
相關標籤/搜索