如何用 Python 和正則表達(dá)式抽取文本結(jié)構(gòu)化信息?

簡(jiǎn)單、直接、高效地從文本中提煉關(guān)鍵內(nèi)容。讓你體驗(yàn)正則表達(dá)式的威力。

痛點(diǎn)

很多人的日常工作,都是要和大量的文本打交道的。

例如學(xué)者需要閱讀大量的文獻(xiàn)材料,從中找到靈感、數(shù)據(jù)與論據(jù)。

學(xué)生需要閱讀很多教科書和論文,然后寫自己的報(bào)告或者做幻燈。

財(cái)經(jīng)分析師,需要從大量的新聞報(bào)道中,找到行業(yè)的發(fā)展趨勢(shì)和目標(biāo)企業(yè)動(dòng)態(tài)的蛛絲馬跡。

不是所有的文本處理,都那么新鮮而有趣。

有一項(xiàng)重要但繁瑣的工作,就是從大量的文本當(dāng)中抽取結(jié)構(gòu)化的信息。

許多數(shù)據(jù)分析的場(chǎng)景,都要求輸入結(jié)構(gòu)化的信息。

例如在咱們之前介紹過(guò)的《貸還是不貸:如何用Python和機(jī)器學(xué)習(xí)幫你決策?》和《如何用Python和深度神經(jīng)網(wǎng)絡(luò)鎖定即將流失的客戶?》中,你都看到了,機(jī)器模型更喜歡被結(jié)構(gòu)化的表格信息來(lái)喂養(yǎng)。

然而,結(jié)構(gòu)化的信息,不一定就在那里,靜候你來(lái)使用。很多時(shí)候,它蘊(yùn)藏在以往生成的非結(jié)構(gòu)化文本中。

你可能早已習(xí)慣,人工閱讀文本信息,把關(guān)鍵點(diǎn)摘取出來(lái),然后把它們拷貝粘貼放到一個(gè)表格中。從原理上講,這樣做無(wú)可厚非。但是實(shí)際操作里面,效率太低,而且太麻煩。

大部分人,是不愿意從事這種簡(jiǎn)單重復(fù)的枯燥工作的。

一遍遍機(jī)械重復(fù)鼠標(biāo)劃定文本范圍,“Ctrl+C”、切換到表格文檔、找準(zhǔn)輸入位置,再“Ctrl+V”……

這種事兒做得太多,對(duì)你的肩肘關(guān)節(jié),甚至是身心健康,都有可能造成不利影響。

想不想嘗試用一種更簡(jiǎn)單的自動(dòng)化方式,替你快速完成這些煩人的操作步驟呢?

讀過(guò)本文后,希望你能找到答案。

樣例

這里,我們舉一個(gè)極端簡(jiǎn)化的中文文本抽取信息例子。

之所以這樣做,是為了避免你在解讀數(shù)據(jù)上花費(fèi)太多時(shí)間。

我更希望,你能夠聚焦于方法,從而掌握新知。

假設(shè)一個(gè)高中班主任,高考后讓班長(zhǎng)統(tǒng)計(jì)一下學(xué)生們的畢業(yè)去向。班長(zhǎng)很認(rèn)真地進(jìn)行了調(diào)查,然后做了如下匯報(bào):

張華考上了北京大學(xué)
李萍進(jìn)了中等技術(shù)學(xué)校
韓梅梅進(jìn)了百貨公司
……

為了讓你對(duì)樣例足夠熟悉,甚至有共鳴,這里我從1998年版的新華字典中,“借鑒”了部分內(nèi)容。

夠貼心吧?

現(xiàn)實(shí)生活中,一個(gè)班大概不會(huì)只有3個(gè)人,因此你可以想象這是一個(gè)長(zhǎng)長(zhǎng)的句子列表。

但其實(shí)班主任有個(gè)隱含的意思沒(méi)有表達(dá)出來(lái),即:

我想要一張表格

所以,看到這一長(zhǎng)串的句子,你可以想象他的表情。

班長(zhǎng)估計(jì)也很難堪:

想要表格你早說(shuō)啊!

這時(shí)候,假設(shè)你是班長(zhǎng),怎么辦?

信息都在文本里面。但如果需要轉(zhuǎn)換成表格,就得一個(gè)個(gè)信息點(diǎn)去尋找和處理。

其實(shí),對(duì)于四五十人的班級(jí)來(lái)說(shuō),手動(dòng)操作也不是什么太難的事情。

但是設(shè)想一下,如果你需要處理的數(shù)據(jù)量,是這個(gè)例子的十倍、百倍甚至千萬(wàn)倍呢?

繼續(xù)堅(jiān)持手動(dòng)處理?

這不僅麻煩,而且不現(xiàn)實(shí)。

我們需要找到一種簡(jiǎn)單的方法,幫助我們自動(dòng)抽取相應(yīng)的信息。

此處我們使用的方法,是正則表達(dá)式。

正則

“正則表達(dá)式”這個(gè)名字,初聽起來(lái)好像很玄妙。實(shí)際上,它是從英文“regular expression”翻譯過(guò)來(lái)的。

如果譯成白話,那就是“有規(guī)律的表述形式”。

這,聽起來(lái),是不是就更加接地氣了?

但是,給你補(bǔ)一下“假行家101”課程:

說(shuō)別人聽得懂的話,你能唬得住誰(shuí)?

約定俗成,咱們繼續(xù)沿用“正則表達(dá)式”,來(lái)稱呼它好了。

從創(chuàng)生之日起,它就給文本處理帶來(lái)了高效率。

但是,用它的主要人群,卻不是時(shí)常跟文字打交道的作家、編輯、學(xué)者、文員,而是……

程序員!

程序員寫的代碼,是文本;程序員處理的數(shù)據(jù),很多也是文本格式。其中便有很多顯著的規(guī)律可循。

正是靠著正則表達(dá)式這種獨(dú)門秘籍,許多別人做起來(lái),需要昏天黑地一整周的任務(wù),程序員可以半小時(shí)搞定,然后喝著咖啡等下班。

即便到了泛人工智能的今天,正則表達(dá)式依然有許多令你意想不到的應(yīng)用。

例如人機(jī)對(duì)話系統(tǒng)。

你可能看了新聞報(bào)道,總以為人機(jī)對(duì)話都是靠著知識(shí)圖譜或者深度學(xué)習(xí)搞出來(lái)的。

不能說(shuō)這里面,沒(méi)有上述炫酷技術(shù)的參與。但它們充其量,只占其中一部分,或許還只是一小部分。

生產(chǎn)實(shí)踐里面,大量的對(duì)話規(guī)則后面,并不是讓你倍感神奇深?yuàn)W的神經(jīng)網(wǎng)絡(luò),而是一堆正則表達(dá)式。

你可能會(huì)擔(dān)心,這樣高端的應(yīng)用技術(shù),自己能掌握嗎?

答案是:

當(dāng)然!

正則表達(dá)式,并不難學(xué)。

尤其是當(dāng)你把它和 Python 結(jié)合到一起,那簡(jiǎn)直就是效率神器了。

我們這就來(lái)看看,正則表達(dá)式怎么幫我們識(shí)別出樣例文本里面“人名”和“去向”信息。

試練

請(qǐng)你開啟一個(gè)瀏覽器,鍵入這個(gè)網(wǎng)址(https://regex101.com/)。

你會(huì)看見如下界面。

它可是一個(gè)正則表達(dá)式實(shí)驗(yàn)的利器。我教 INFO 5731 課程時(shí),學(xué)生們就是在掌握了這個(gè)工具以后,迅速玩兒轉(zhuǎn)了正則表達(dá)式。

這么好的工具,一定要價(jià)不菲吧?

不,它是免費(fèi)的。你放心大膽使用就好了。

我們首先把左側(cè)的編程語(yǔ)言,從默認(rèn)的 PHP ,調(diào)整為 Python。

之后,把需要進(jìn)行處理的文本,貼到中間空白的大文本框里面。

下面我們來(lái)嘗試進(jìn)行“匹配”。

什么叫做匹配呢?

就是你寫一個(gè)表達(dá)式,電腦便拿著雞毛當(dāng)令箭,在每一行文本上,都認(rèn)認(rèn)真真地找有沒(méi)有符合該表達(dá)式的文本段落。

如有,則會(huì)高亮顯示出來(lái)。

這里我們觀察一下,發(fā)現(xiàn)每個(gè)句子里面,人員去向前面,都有一個(gè)“了”字。

好,我們就在中部上方小文本框里,把“了”字輸入進(jìn)去。

可以看到,三句話里面的“了”,全都亮了

這就是你接觸到的第一種匹配方式——按照字符原本的意思來(lái)查找一致的內(nèi)容。

因?yàn)闃永谋镜囊?guī)律性,我們可以把“了”當(dāng)成一個(gè)定位符,它后面,到句子結(jié)束位置,是“去向”信息。

咱們需要找的一半結(jié)構(gòu)化信息,不就是這個(gè)“去向”嗎?

我們嘗試匹配“去向”。

怎么匹配呢?這次每一行的字兒都不一樣?。?/p>

沒(méi)關(guān)系,正則表達(dá)式強(qiáng)大之處,此時(shí)就顯示出來(lái)了。

你可以用一個(gè)點(diǎn)號(hào),也就是.,表示任意字符。

字母、數(shù)字、標(biāo)點(diǎn)……甚至是中文,也能涵蓋在內(nèi)。

然后我們繼續(xù)想想看,去向信息這里,會(huì)有幾個(gè)字呢?

不好說(shuō)。

例子里面這簡(jiǎn)單的三句話,就有“4個(gè)字”或者“6個(gè)字”兩種情況。

所以,我們無(wú)法指定去向信息里面字符的長(zhǎng)度。

但這也沒(méi)關(guān)系,我們只需要用一個(gè)星號(hào)(*),就可以代表出現(xiàn)次數(shù),從0到無(wú)窮大都可以匹配。

當(dāng)然,實(shí)際情況中,是不會(huì)真出現(xiàn)無(wú)窮大的。

我們?cè)趧偛泡斎氲幕A(chǔ)上,加上.*,結(jié)果就成了這個(gè)樣子:

不錯(cuò)嘛!

不過(guò)似乎去向信息和“了”字兒都是一樣顏色的高亮。那不就混到了一起嗎?

我們可不想這樣。

怎么辦?

請(qǐng)你在.*的兩側(cè),嘗試加入一對(duì)小括號(hào)(注意,不要用中文全角符號(hào))試試看。

你會(huì)發(fā)現(xiàn),這次“了”依然用藍(lán)色表示,而后面的去向信息,已經(jīng)變成了綠色。

這一對(duì)小括號(hào),很重要,它叫做“分組”,是提取信息的基本單位。

我們的任務(wù)已經(jīng)解決了一半了,是吧?

下面我們來(lái)嘗試把人名一并抽取出來(lái)。

我們來(lái)找人名的錨定位置。

細(xì)細(xì)觀察,你不難發(fā)現(xiàn),每個(gè)人名的后面,都有個(gè)動(dòng)詞跟著。

升學(xué)的同學(xué),用的是“考”字,而就業(yè)的同學(xué),用的是“進(jìn)”字。

我們先嘗試一下“考”字。

這里我們嘗試直接把“考”字放在“了”字以前。但是你會(huì)發(fā)現(xiàn),什么匹配結(jié)果也沒(méi)有。

為什么?

回看數(shù)據(jù),你會(huì)發(fā)現(xiàn),人家用的原詞是“考上了”。

當(dāng)然這里我們可以輸入“上”字。不過(guò)你要考慮一下更為通用的情況。

好比說(shuō),“考取了”怎么辦?“考入了”呢?

更好的方式,是繼續(xù)使用我們剛才學(xué)會(huì)的“大招”,在“考”和“了”之間,插入一個(gè).*。

這時(shí)候,你的正則表達(dá)式的樣子是 考.*了(.*)

看,第一行的信息成功匹配了吧?

但是,那后面還有兩行沒(méi)有匹配,怎么辦?

我們依樣畫葫蘆,就會(huì)發(fā)現(xiàn),使用進(jìn).*了(.*)就能正確匹配后兩行。

問(wèn)題來(lái)了:

匹配第一行的,匹配不了后兩行,反之亦然。

這不好。我們希望寫的表達(dá)式,能夠更通用。

怎么辦?

我們看看正則表達(dá)式當(dāng)中“或”關(guān)系的表示。

這里,我們可以把兩個(gè)字符用豎線隔開,旁邊用中括號(hào)括起來(lái),代表兩者任一出現(xiàn),都算匹配成功。

也就是,把正則表達(dá)式,寫成這樣:[考|進(jìn)].*了(.*)

太棒了,三行的內(nèi)容都已經(jīng)匹配成功。

這里,動(dòng)詞詞組,和代表時(shí)態(tài)的“了”作為中間錨定信息,我們可以放心大膽,把之前的人名信息,提取出來(lái)了。

也就是這樣寫:(.*)[考|進(jìn)].*了(.*)

注意此時(shí),人名分組是綠色,去向分組是紅色的。

我們成功提取了兩組信息!慶祝一下!

可是,如果你給班主任看這里的結(jié)果,估計(jì)他不會(huì)滿意。

表格,我要表格!

別著急,該 Python 出場(chǎng)了。

下面我們嘗試在 Python 把數(shù)據(jù)正式提取出來(lái)。

環(huán)境

本文的配套源代碼,我放在了 Github 上。

你可以在我的公眾號(hào)“玉樹芝蘭”(nkwangshuyi)后臺(tái)回復(fù)“regex”,查看完整的代碼鏈接。

如果你對(duì)我的教程滿意,歡迎在頁(yè)面右上方的 Star 上點(diǎn)擊一下,幫我加一顆星。謝謝!

注意這個(gè)頁(yè)面的中央,有個(gè)按鈕,寫著“在 Colab 打開”(Open in Colab)。請(qǐng)你點(diǎn)擊它。

然后,Google Colab 就會(huì)自動(dòng)開啟。

我建議你點(diǎn)一下上圖中紅色圈出的 “COPY TO DRIVE” 按鈕。這樣就可以先把它在你自己的 Google Drive 中存好,以便使用和回顧。

Colab 為你提供了全套的運(yùn)行環(huán)境。你只需要依次執(zhí)行代碼,就可以復(fù)現(xiàn)本教程的運(yùn)行結(jié)果了。

如果你對(duì) Google Colab 不熟悉,沒(méi)關(guān)系。我這里有一篇教程,專門講解 Google Colab 的特點(diǎn)與使用方式。

為了你能夠更為深入地學(xué)習(xí)與了解代碼,我建議你在 Google Colab 中開啟一個(gè)全新的 Notebook ,并且根據(jù)下文,依次輸入代碼并運(yùn)行。在此過(guò)程中,充分理解代碼的含義。

這種看似笨拙的方式,其實(shí)是學(xué)習(xí)的有效路徑。

代碼

首先,讀入 Python 正則表達(dá)式包。

import re

然后,我們把數(shù)據(jù)準(zhǔn)備好。注意為了演示代碼的通用性,我這里在最后加了一行文字,區(qū)別于之前的文字規(guī)律,看看我們的代碼能否正確處理它。

data = """張華考上了北京大學(xué)
李萍進(jìn)了中等技術(shù)學(xué)校
韓梅梅進(jìn)了百貨公司
他們都有光明的前途"""

然后,該寫正則表達(dá)式了。你真的需要自己手動(dòng)來(lái)寫嗎?

當(dāng)然不必。

強(qiáng)大的 regex101 網(wǎng)站,已經(jīng)幫助我們準(zhǔn)備好了。

請(qǐng)你點(diǎn)擊上圖中紅色圈出的按鈕,網(wǎng)站會(huì)為你準(zhǔn)備好一個(gè)初始代碼的模板,可以匹配你需要的模式。

你不需要完全照搬代碼。其中有這樣一句,是很重要的,拷貝過(guò)來(lái),貼到 Colab Notebook 就好。

regex = r"(.*)[考|進(jìn)].*了(.*)"

以上就是你的正則表達(dá)式,在 Python 里面應(yīng)有的樣子。

我們準(zhǔn)備一個(gè)空列表,用來(lái)接收數(shù)據(jù)。

mylist = []

接著,寫一個(gè)循環(huán)。

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))

我給你解釋一下這個(gè)循環(huán)里面,各條語(yǔ)句的含義:

  • data.split('\n') 把文本數(shù)據(jù)按行來(lái)拆分開。這樣我們就可以針對(duì)每一行,來(lái)獲取數(shù)據(jù)。
  • mysearch = re.search(regex, line) 這一句嘗試匹配模式到該行內(nèi)容。
  • if mysearch 這個(gè)判斷語(yǔ)句,是讓程序分辨一下,該行是否有我們要找的模式。例如最后一行文字,里面并沒(méi)有咱們前面分析的文字模式。遇到這樣的行,直接跳過(guò)。
  • name = mysearch.group(1) 是說(shuō)匹配的第一組內(nèi)容,也就是 regex101 網(wǎng)站里綠色代表的人名分組存到 name 變量里。下一句依次類推。注意 group 對(duì)應(yīng)你正則表達(dá)式里面小括號(hào)出現(xiàn)的順序,從1開始計(jì)數(shù)。
  • mylist.append((name, dest)) 把該行抽取到的信息,存入到咱們之前定義的空列表里面。

注意,如果不加 mysearch = re.search(regex, line) 這一句,程序會(huì)對(duì)每一行都嘗試匹配并且抽取分組內(nèi)容,那么結(jié)果就會(huì)報(bào)這樣的錯(cuò)誤:

所以你看,用正則表達(dá)式抽取信息時(shí),不能蠻干。

此時(shí),我們查看一下 mylist 這個(gè)列表里面的內(nèi)容:

mylist

結(jié)果為:

[('張華', '北京大學(xué)'), ('李萍', '中等技術(shù)學(xué)校'), ('韓梅梅', '百貨公司')]

不錯(cuò),一個(gè)不多,一個(gè)不少,恰好是我們需要的。

我們要把它導(dǎo)出成為表格。方法有很多,但是最簡(jiǎn)便順手的,是用 Pandas 數(shù)據(jù)分析軟件包。

import pandas as pd

只需要利用 pd.DataFrame 函數(shù),我們就能把上面列表和元組(tuple)組成的一個(gè)二維結(jié)構(gòu),變成數(shù)據(jù)框。

df = pd.DataFrame(mylist)
df.columns = ['姓名', '去向']

注意,這里我們還非常細(xì)心地修改了表頭。

看看你的勞動(dòng)成果吧:

df

有了數(shù)據(jù)框,轉(zhuǎn)換成為 Excel ,就是一行代碼的事情了:

df.to_excel("dest.xlsx", index=False)

進(jìn)入 Files 標(biāo)簽頁(yè),刷新并且查看一下當(dāng)前目錄下的內(nèi)容:

這個(gè) dest.xlsx 就是輸出的結(jié)果了。下載之后我們可以用 Excel 打開查看。

任務(wù)完成!

你可以把結(jié)果提交給班主任,看他滿意的笑容了。

小結(jié)

這篇教程里面,咱們談了如何利用文本字符規(guī)律,借助 Python 和正則表達(dá)式,來(lái)提取結(jié)構(gòu)化信息。

希望你已經(jīng)掌握了以下本領(lǐng):

  • 了解正則表達(dá)式的功用;
  • 用 regex101 網(wǎng)站嘗試正則表達(dá)式匹配,并且生成初步的代碼;
  • 用 Python 批量提取信息,并且根據(jù)需求導(dǎo)出結(jié)構(gòu)化數(shù)據(jù)為指定格式。

再次強(qiáng)調(diào)一下,對(duì)于這么簡(jiǎn)單的樣例,使用上述方法,絕對(duì)是大炮轟蚊子。

然而,如果你需要處理的數(shù)據(jù)是海量的,這個(gè)方法給你節(jié)省下來(lái)的時(shí)間,會(huì)非??捎^。

希望你能夠舉一反三,在自己的工作中靈活運(yùn)用它。

資源

先別急著走。

由于篇幅所限,教程中我只給你介紹了幾項(xiàng)最簡(jiǎn)單正則語(yǔ)法。肯定會(huì)有很多你感興趣的語(yǔ)法知識(shí),沒(méi)來(lái)得及一一講給你聽。

如果你對(duì)于正則表達(dá)式很感興趣,因?yàn)榭蒲谢蛘吖ぷ髂康?,需要馬上學(xué)習(xí),也請(qǐng)不要著急。

我這里剛好有一份免費(fèi)的優(yōu)秀教程資源分享給你。

我在 UNT 講授 INFO 5731 課程的時(shí)候,就是用這份教程結(jié)合翻轉(zhuǎn)教學(xué),讓從沒(méi)聽說(shuō)過(guò)正則表達(dá)式的學(xué)生們,在一兩周之內(nèi)掌握并且熟練應(yīng)用。相信對(duì)你來(lái)說(shuō),這份教程資源也會(huì)很有用。

你可以在我的公眾號(hào)“玉樹芝蘭”(nkwangshuyi)后臺(tái)回復(fù)“regex”,查看教程鏈接。

祝學(xué)習(xí)愉快!

喜歡別忘了點(diǎn)贊。

還可以微信關(guān)注我的公眾號(hào)“玉樹芝蘭”(nkwangshuyi)。別忘了加星標(biāo),以免錯(cuò)過(guò)新推送提示。

如果本文可能對(duì)你身邊的親友有幫助,也歡迎你把本文通過(guò)微博或朋友圈分享給他們。讓他們一起參與到我們的討論中來(lái)。

延伸閱讀

你可能也會(huì)對(duì)以下話題感興趣。點(diǎn)擊鏈接就可以查看。

題圖: Photo by Tim St. Martin on Unsplash

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容