簡體   English   中英

加速Python中的正則表達式

[英]Speeding up regular expressions in Python

我需要從HTML文件中快速提取文本。 我使用以下正則表達式而不是完整的解析器,因為我需要快速而不是准確(我有超過1 TB的文本)。 分析器顯示我的腳本中的大部分時間都花在re.sub過程中。 什么是加快我的過程的好方法? 我可以在C中實現一些部分,但我想知道這是否有用,因為 re.sub中花費的時間,我認為這將有效實現。

# Remove scripts, styles, tags, entities, and extraneous spaces:
scriptRx    = re.compile("<script.*?/script>", re.I)
styleRx     = re.compile("<style.*?/style>", re.I)
tagsRx      = re.compile("<[!/]?[a-zA-Z-]+[^<>]*>")
entitiesRx  = re.compile("&[0-9a-zA-Z]+;")
spacesRx    = re.compile("\s{2,}")
....
text = scriptRx.sub(" ", text)
text = styleRx.sub(" ", text)
....

謝謝!

首先,使用為此構建的HTML解析器,如BeautifulSoup:

http://www.crummy.com/software/BeautifulSoup/

然后,您可以使用分析器識別剩余的特定慢點:

http://docs.python.org/library/profile.html

為了學習正則表達式,我發現掌握正則表達式非常有價值,無論編程語言是什么:

http://oreilly.com/catalog/9781565922570

也:

如何在python中調試正則表達式?

由於用例的重新聲明,那么對於這個請求,我會說上面的不是你想要的。 我的另一個建議是: 加速Python中的正則表達式

你正在處理每個文件五次,所以你應該做的第一件事(如Paul Sanwald所說)是試圖通過將你的正則表達式組合在一起來減少這個數字。 我也會避免使用不情願的量詞,這些量詞是為了方便而犧牲效率而設計的。 考慮這個正則表達式:

<script.*?</script>

每一次. 去消費另一個角色,它首先必須確保</script>在那個位置不匹配。 這幾乎就像在每個位置做一個負向前瞻:

<script(?:(?!</script>).)*</script>

但是我們知道如果下一個字符除了<沒有任何意義,我們可以相應地調整正則表達式:

<script[^<]*(?:<(?!/script>)[^<]*)*</script>

當我使用此目標字符串在RegexBuddy中測試它們時:

<script type="text/javascript">var imagePath='http://sstatic.net/stackoverflow/img/';</script>

...不情願的正則表達式需要173步才能完成比賽,而量身定制的正則表達式僅需28步。

將前三個正則表達式合並為一個產生這個野獸:

<(?:(script|style)[^<]*(?:<(?!/\1)[^<]*)*</\1>|[!/]?[a-zA-Z-]+[^<>]*>)

你可能想要在它的同時刪除<HEAD>元素(即(script|style|head) )。

對於角色實體,我不知道你對第四個正則表達式做了什么 - 你也只是刪除它們嗎? 我猜第五個正則表達式必須單獨運行,因為它清理的一些空格是由前面的步驟生成的。 但嘗試將前三個正則表達式結合起來,看看它有多大差異。 這應該告訴你這種方法是否值得推進。

您可以做的一件事是使用反向引用組合腳本/樣式正則表達式。 這是一些示例數據:

$ cat sample 
<script>some stuff</script>
<html>whatever </html>
<style>some other stuff</style>

使用perl:

perl -ne "if (/<(script|style)>.*?<\/\1>/) { print $1; } " sample

它將匹配腳本或樣式。 我推薦“掌握正則表達式”,這是一本很好的書。

使用HTML解析器的建議很好,因為它很可能比正則表達式更快。 但我不確定BeautifulSoup是否適合這項工作,因為它從整個文件構造一個解析樹並將整個內容存儲在內存中。 對於1TB的HTML,你需要一個淫穢的RAM才能做到這一點;-)我建議你看一下HTMLParser ,它寫的級別比BeautifulSoup低,但我相信它是一個流解析器,所以它只會一次加載一些文本。

如果您的用例確實要為每一百萬個文檔解析一些內容,那么我的上述答案將無濟於事。 我推薦一些啟發式方法,比如在它們上面開始使用幾個“直接文本”正則表達式 - 就像普通/script//style/ ,如果可以的話,快速拋出一些東西。 事實上,你真的需要進行終端標簽檢查嗎? <style不夠好嗎? 為其他人留下驗證。 如果快速的成功,那么將其余部分放入單個正則表達式,例如/<script|<style|\\s{2,}|etc.../這樣它就不必經歷過如此多的文本每個正則表達式。

我會使用簡單的程序與常規的Python分區,比如,這個,但它僅使用一個樣式示例文件進行測試:

## simple filtering when not hierarchical tags inside other discarded tags

start_tags=('<style','<script')
end_tags=('</style>','</script>')

##print("input:\n %s" % open('giant.html').read())
out=open('cleaned.html','w')
end_tag=''

for line in open('giant.html'):
    line=' '.join(line.split())
    if end_tag:
        if end_tag in line:
            _,tag,end = line.partition(end_tags[index])
            if end.strip():
                out.write(end)
            end_tag=''
        continue ## discard rest of line if no end tag found in line

    found=( index for index in (start_tags.index(start_tag)
                                if start_tag in line else ''
                                for start_tag in start_tags)
            if index is not '')
    for index in  found:
        start,tag,end = line.partition(start_tags[index])
        # drop until closing angle bracket of start tag
        tag,_ ,end = end.partition('>')
        # check if closing tag already in same line
        if end_tags[index] in end:
            _,tag,end = end.partition(end_tags[index])
            if end.strip():
                out.write(end)
            end_tag = '' # end tag reset after found
        else:
            end_tag=end_tags[index]
            out.write(end) # no end tag at same line
    if not end_tag: out.write(line+'\n')

out.close()
##    print 'result:\n%s' % open('cleaned.html').read()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM