python和正则表达式抽取文本化的信息
很多人的日常工作,都是要和大量的文本打交道的。例如學者需要閱讀大量的文獻材料,從中找到靈感、數據與論據;學生需要閱讀很多教科書和論文,然后寫自己的報告或者做幻燈;財經分析師,需要從大量的新聞報道中,找到行業的發展趨勢和目標企業動態的蛛絲馬跡。
但不是所有的文本處理,都那么新鮮而有趣。有一項重要但繁瑣的工作,就是從大量的文本當中抽取結構化的信息。
然而,結構化的信息不一定就在那里,靜候你來使用。很多時候,它蘊藏在以往生成的非結構化文本中。
你可能早已習慣,人工閱讀文本信息,把關鍵點摘取出來,然后把它們拷貝粘貼放到一個表格中。從原理上講,這樣做無可厚非。但是實際操作里面,效率太低,而且太麻煩。
大部分人,是不愿意從事這種簡單重復的枯燥工作的。一遍遍機械重復鼠標劃定文本范圍,?Ctrl+C、切換到表格文檔、找準輸入位置,再 ?Ctrl+V……這種事兒做得太多,對你的肩肘關節,甚至是身心健康,都有可能造成不利影響。
想不想嘗試用一種更簡單的自動化方式,替你快速完成這些煩人的操作步驟呢?讀過本文后,希望你能找到答案。樣例
這里,我們舉一個極端簡化的中文文本抽取信息例子。之所以這樣做,是為了避免你在解讀數據上花費太多時間。我更希望,你能夠聚焦于方法,從而掌握新知。
假設一個高中班主任,高考后讓班長統計一下學生們的畢業去向。班長很認真地進行了調查,然后做了如下匯報:
張華考上了北京大學
 李萍進了中等技術學校
 韓梅梅進了百貨公司
 ……
為了讓你對樣例足夠熟悉,甚至有共鳴,這里我從 1998 年版的新華字典中,「借鑒」了部分內容。夠貼心吧?
現實生活中,一個班大概不會只有 3 個人,因此你可以想象這是一個長長的句子列表。但其實班主任有個隱含的意思沒有表達出來,即:
我想要一張表格!
所以,看到這一長串的句子,你可以想象他的表情。班長估計也很難堪:
想要表格你早說啊!
這時候,假設你是班長,怎么辦?信息都在文本里面。但如果需要轉換成表格,就得一個個信息點去尋找和處理。其實,對于四五十人的班級來說,手動操作也不是什么太難的事情。但是設想一下,如果你需要處理的數據量,是這個例子的十倍、百倍甚至千萬倍呢?繼續堅持手動處理?這不僅麻煩,而且不現實。
我們需要找到一種簡單的方法,幫助我們自動抽取相應的信息。此處我們使用的方法,是正則表達式。
正則
「正則表達式」這個名字,初聽起來好像很玄妙。實際上,它是從英文 「regular expression」翻譯過來的。如果譯成白話,那就是 「有規律的表述形式」。
這,聽起來,是不是就更加接地氣了?但是,給你補一下 「假行家 101」課程:
說別人聽得懂的話,你能唬得住誰?
約定俗成,咱們繼續沿用 「正則表達式」,來稱呼它好了。從創生之日起,它就給文本處理帶來了高效率。但是,用它的主要人群,卻不是時常跟文字打交道的作家、編輯、學者、文員,而是……程序員!
程序員寫的代碼,是文本;程序員處理的數據,很多也是文本格式。其中便有很多顯著的規律可循。
正是靠著正則表達式這種獨門秘籍,許多別人做起來,需要昏天黑地一整周的任務,程序員可以半小時搞定,然后喝著咖啡等下班。
即便到了泛人工智能的今天,正則表達式依然有許多令你意想不到的應用。例如人機對話系統。你可能看了新聞報道,總以為人機對話都是靠著知識圖譜或者深度學習搞出來的。不能說這里面,沒有上述炫酷技術的參與。但它們充其量,只占其中一部分,或許還只是一小部分。
生產實踐里面,大量的對話規則后面,并不是讓你倍感神奇深奧的神經網絡,而是一堆正則表達式。你可能會擔心,這樣高端的應用技術,自己能掌握嗎?
答案是:當然!
正則表達式,并不難學。尤其是當你把它和 Python 結合到一起,那簡直就是效率神器了。我們這就來看看,正則表達式怎么幫我們識別出樣例文本里面 「人名」和 「去向」信息。
試練
請你開啟一個瀏覽器,鍵入這個 網址。
你會看見如下界面:
它可是一個正則表達式實驗的利器。我教 INFO 5731 課程時,學生們就是在掌握了這個工具以后,迅速玩兒轉了正則表達式。
這么好的工具,一定要價不菲吧?不,它是免費的。你放心大膽使用就好了。
我們首先把左側的編程語言,從默認的 PHP ,調整為 Python。之后,把需要進行處理的文本,貼到中間空白的大文本框里面。
下面我們來嘗試進行 「匹配」。什么叫做匹配呢?就是你寫一個表達式,電腦便拿著雞毛當令箭,在每一行文本上,都認認真真地找有沒有符合該表達式的文本段落。如有,則會高亮顯示出來。
這里我們觀察一下,發現每個句子里面,人員去向前面,都有一個 「了」字。好,我們就在中部上方小文本框里,把 「了」字輸入進去。可以看到,三句話里面的 「了」,全都亮了。
這就是你接觸到的第一種匹配方式 —— 按照字符原本的意思來查找一致的內容。因為樣例文本的規律性,我們可以把 「了」當成一個定位符,它后面,到句子結束位置,是 「去向」信息。咱們需要找的一半結構化信息,不就是這個 「去向」嗎?
我們嘗試匹配 「去向」。怎么匹配呢?這次每一行的字兒都不一樣啊?沒關系,正則表達式強大之處,此時就顯示出來了。你可以用一個點號,也就是 .,表示任意字符。字母、數字、標點…… 甚至是中文,也能涵蓋在內。
然后我們繼續想想看,去向信息這里,會有幾個字呢?不好說。例子里面這簡單的三句話,就有 「4 個字」或者 「6 個字」兩種情況。所以,我們無法指定去向信息里面字符的長度。但這也沒關系,我們只需要用一個星號(*),就可以代表出現次數,從 0 到無窮大都可以匹配。當然,實際情況中,是不會真出現無窮大的。
我們在剛才輸入的基礎上,加上 .*,結果就成了這個樣子:
不錯嘛!不過似乎去向信息和 「了」字兒都是一樣顏色的高亮。那不就混到了一起嗎?我們可不想這樣。
怎么辦?請你在 .* 的兩側,嘗試加入一對小括號(注意,不要用中文全角符號)試試看。
你會發現,這次 「了」依然用藍色表示,而后面的去向信息,已經變成了綠色。這一對小括號,很重要,它叫做 「分組」,是提取信息的基本單位。
我們的任務已經解決了一半了,是吧?下面我們來嘗試把人名一并抽取出來。我們來找人名的錨定位置。細細觀察,你不難發現,每個人名的后面,都有個動詞跟著。升學的同學,用的是 「考」字,而就業的同學,用的是 「進」字。
我們先嘗試一下 「考」字。這里我們嘗試直接把 「考」字放在 「了」字以前。但是你會發現,什么匹配結果也沒有。
為什么?回看數據,你會發現,人家用的原詞是 「考上了」。當然這里我們可以輸入 「上」字。不過你要考慮一下更為通用的情況。好比說,「考取了」怎么辦?「考入了」呢?更好的方式,是繼續使用我們剛才學會的 「大招」,在 「考」和 「了」之間,插入一個 .*。
這時候,你的正則表達式的樣子是 考.*了(.*):
看,第一行的信息成功匹配了吧?但是,那后面還有兩行沒有匹配,怎么辦?
我們依樣畫葫蘆,就會發現,使用進.*了(.*) 就能正確匹配后兩行:
問題來了:匹配第一行的,匹配不了后兩行,反之亦然。這不好。我們希望寫的表達式,能夠更通用。
怎么辦?我們看看正則表達式當中 「或」關系的表示。這里,我們可以把兩個字符用豎線隔開,旁邊用中括號括起來,代表兩者任一出現,都算匹配成功。
也就是,把正則表達式,寫成這樣:[考|進].*了(.*):
太棒了,三行的內容都已經匹配成功。這里,動詞詞組,和代表時態的 「了」作為中間錨定信息,我們可以放心大膽,把之前的人名信息,提取出來了。
也就是這樣寫:(.*)[考|進].*了(.*):
注意此時,人名分組是綠色,去向分組是紅色的。我們成功提取了兩組信息!慶祝一下!
可是,如果你給班主任看這里的結果,估計他不會滿意。
表格,我要表格!
別著急,該 Python 出場了。下面我們嘗試在 Python 把數據正式提取出來。
代碼
首先,讀入 Python 正則表達式包:
import re然后,我們把數據準備好。注意為了演示代碼的通用性,我這里在最后加了一行文字,區別于之前的文字規律,看看我們的代碼能否正確處理它:
data = """張華考上了北京大學 李萍進了中等技術學校 韓梅梅進了百貨公司 他們都有光明的前途"""然后,該寫正則表達式了。你真的需要自己手動來寫嗎?當然不必。
強大的 regex101 網站,已經幫助我們準備好了:
請你點擊上圖中紅色圈出的按鈕,網站會為你準備好一個初始代碼的模板,可以匹配你需要的模式:
你不需要完全照搬代碼。其中有這樣一句,是很重要的,拷貝過來,貼到 Colab Notebook 就好:
regex = r"(.*)[考|進].*了(.*)"以上就是你的正則表達式,在 Python 里面應有的樣子。
我們準備一個空列表,用來接收數據:
mylist = []接著,寫一個循環:
for line in data.split('\n'):mysearch = re.search(regex, line)if mysearch:name = mysearch.group(1)dest = mysearch.group(2)mylist.append((name, dest))我給你解釋一下這個循環里面,各條語句的含義:
- data.split('\n') 把文本數據按行來拆分開。這樣我們就可以針對每一行,來獲取數據;
- mysearch = re.search(regex, line) 這一句嘗試匹配模式到該行內容;
- if mysearch 這個判斷語句,是讓程序分辨一下,該行是否有我們要找的模式。例如最后一行文字,里面并沒有咱們前面分析的文字模式。遇到這樣的行,直接跳過;
- name = mysearch.group(1) 是說匹配的第一組內容,也就是 regex101 網站里綠色代表的人名分組存到 name 變量里。下一句依次類推。注意 group 對應你正則表達式里面小括號出現的順序,從 1 開始計數;
- mylist.append((name, dest)) 把該行抽取到的信息,存入到咱們之前定義的空列表里面。
注意,如果不加 mysearch = re.search(regex, line) 這一句,程序會對每一行都嘗試匹配并且抽取分組內容,那么結果就會報這樣的錯誤:
所以你看,用正則表達式抽取信息時,不能蠻干。
此時,我們查看一下 mylist 這個列表里面的內容:
mylist結果為:
[('張華', '北京大學'), ('李萍', '中等技術學校'), ('韓梅梅', '百貨公司')]不錯,一個不多,一個不少,恰好是我們需要的。
我們要把它導出成為表格。方法有很多,但是最簡便順手的,是用 Pandas 數據分析軟件包:
import pandas as pd只需要利用 pd.DataFrame 函數,我們就能把上面列表和元組(tuple)組成的一個二維結構,變成數據框。
df = pd.DataFrame(mylist) df.columns = ['姓名', '去向']注意,這里我們還非常細心地修改了表頭。
看看你的勞動成果吧:
df有了數據框,轉換成為 Excel ,就是一行代碼的事情了:
df.to_excel("dest.xlsx", index=False)進入 Files 標簽頁,刷新并且查看一下當前目錄下的內容:
這個 dest.xlsx 就是輸出的結果了。下載之后我們可以用 Excel 打開查看。
任務完成!你可以把結果提交給班主任,看他滿意的笑容了。
小結
這篇教程里面,咱們談了如何利用文本字符規律,借助 Python 和正則表達式,來提取結構化信息。
希望你已經掌握了以下本領:
- 了解正則表達式的功用;
- 用 regex101 網站嘗試正則表達式匹配,并且生成初步的代碼;
- 用 Python 批量提取信息,并且根據需求導出結構化數據為指定格式。
再次強調一下,對于這么簡單的樣例,使用上述方法,絕對是大炮轟蚊子。然而,如果你需要處理的數據是海量的,這個方法給你節省下來的時間,會非常可觀。希望你能夠舉一反三,在自己的工作中靈活運用它。
(.*)[考|進].*了(.*) 方括號中不需要使用 | ,因為方括號本身的作用就是任選其中的一個字符,[考|進] 會匹配 考、|、進 三個中的任意一個。總結
以上是生活随笔為你收集整理的python和正则表达式抽取文本化的信息的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 有理函数多项式分解以及系数的留数法求解
- 下一篇: java db4o 教程_面向Java开
