Python的正則表達(dá)式
Python通過導(dǎo)入標(biāo)準(zhǔn)庫re實(shí)現(xiàn)正則表達(dá)式(regular expression)茂装,Python的正則表達(dá)式引擎和Perl一樣驳阎,并且兼容Perl流派的元字符仿村。
元字符
Python支持的元字符很多间学,一種是比較常見入偷,我之前也就只會(huì)用這些
-
.
表示任意一個(gè)字符,默認(rèn)不匹配換行符眉枕,制表符 -
|
表示或恶复,ca|bd
會(huì)匹配ca或bd,而不是cab, cbd, 如果想要匹配后者,則需要用到()
進(jìn)行分組 -
^
,$
表示位置符號(hào)速挑,行首和行尾 如^ab$
匹配ab, 不匹配eab, abe,aeb - 量詞寂玲,表示重復(fù)數(shù),
*
任意多次,+
一次以上,?
0次或一次,{m,n}
m~n次,{m}
重復(fù)m次,{m,}
重復(fù)大于m次 - 在上述量詞后接
?
, 就從貪婪模式變?yōu)榉秦澙纺J焦R Ee個(gè)例子,對(duì)于abbbbbb
這個(gè)字符串想许,ab*
和ab*?
的結(jié)果不同伶授,前者匹配abbbbbb
,后者匹配a
流纹,也就是貪婪模式盡可能多匹配糜烹。 -
[...]
表示多選項(xiàng),比如a[bc]
就可以匹配ab,ac, 如果是[a-z]
那么表示從a到z范圍. 所有元字符在[]
中都會(huì)被認(rèn)為是普通字符漱凝。所有元字符在[]
-
(...)
表示捕獲型分組疮蹦,被(...)
匹配到部分,可以用\1
,\2
進(jìn)行引用 - "" 表示轉(zhuǎn)義茸炒,由于該符號(hào)也是字符串的元字符愕乎,那么在構(gòu)建模式的時(shí)候要萬分小心阵苇,因?yàn)镻ython會(huì)先對(duì)字符串進(jìn)行加工,然后才會(huì)傳入到正則引擎中感论。也就是說绅项,也就是如果你想匹配"" , 你的模式寫法得是
\\\\
,因?yàn)槿绻粚?code>\\,會(huì)被Python先翻譯成\
,所以必須寫成\\\\
比肄。因此建議用使用原始字符串(raw string),即r"\\"
下面的一些比較高級(jí)快耿,在我寫作時(shí)能記得的元字符,基本上都是(?...)
一類的增強(qiáng)型標(biāo)記芳绩,具體含義和?
后緊接的第一個(gè)字符有關(guān)
-
(?:...)
: 非捕獲型分組掀亥,也就是僅僅分組,正則引擎不會(huì)記住他用于后續(xù)引用 -
(?=...)
: 向后檢查妥色,要求當(dāng)前位置后符合...
表示的模式,(?!...)
也是向后檢查搪花,只不過要求當(dāng)前位置緊接的內(nèi)容不能被...
匹配 -
(?<=...)
和(?<!...)
是向前檢查。
在《精通正則表達(dá)式》中垛膝,作者舉了一個(gè)例子鳍侣,將"12345679"變?yōu)楦菀组喿x的"12,345,679"形式。 也就是找到一個(gè)位置前面是數(shù)字吼拥,后面是3的倍數(shù)個(gè)數(shù)字的位置插入逗號(hào)
re.sub(r"(?<=\d)(?=(\d\d\d)+$)",",","1234567")
下面是我需要翻閱資料才能記得
-
(?P<name>...)
: 在之前捕獲型括號(hào)的基礎(chǔ)上倚聚,將捕獲到的內(nèi)容賦值給name
, 其中該內(nèi)容可以用(?P=name)
進(jìn)行引用 -
(?#...)
: 這個(gè)僅僅是注釋,不做任意匹配 -
(?aiLmsux)
比較復(fù)雜凿可,記不太起來 -
(?(id/name)yes-pattern|no-pattern)
更加復(fù)雜揣非,需要舉一個(gè)例子宛琅。(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)
來解釋,當(dāng)然這個(gè)例子理解起來也不容易。解釋起來就是锋勺,第一個(gè)括號(hào)先嘗試捕獲匹配<
, 編號(hào)為1,然后是第二個(gè)括號(hào)匹配“字符串@字符串”叨粘,比如說user@host,然后第三個(gè)括號(hào)表示不捕獲分組, 識(shí)別".com"這類晤揣,然后第四個(gè)括號(hào)就是看第一個(gè)括號(hào)有沒有捕獲到東西,如果有就去匹配>
纳击,沒有則是匹配行尾续扔。也就是你的郵箱地址要么為"user@host.com",要么為<user@host.com>
,其他都是不符合要求。
常用函數(shù)
一般用法都是用re.compile
構(gòu)建一個(gè)正則表達(dá)式對(duì)象焕数,這個(gè)正則表達(dá)式對(duì)象可以用在re.match
,re.search
,re.find
,re.findall
等函數(shù)里纱昧,同時(shí)該對(duì)象也有.match
,.search
方法。舉個(gè)例子堡赔,比如說你知道了一個(gè)形如GSExxx的GEO編號(hào)识脆,你需要提取這個(gè)編號(hào)下的所有GSMxxx編號(hào),然后根據(jù)這個(gè)GSMxxx編號(hào)去提取SRA編號(hào),以隨便找的GSE100566為例灼捂。
首先利用Python的requests庫抓取網(wǎng)頁信息
# Python
import re
import requests
base_url = "https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc="
acc = "GSE101571"
resp = requests.get(base_url + acc)
contents = resp.text()
然后構(gòu)建一個(gè)正則表達(dá)式离例,去捕獲所有的GSMxxx類型的編號(hào)
pattern = "GSM\d+"
GSM_acc = re.findall(pattern, contents)
或許你不滿足于此,你還希望捕獲到每個(gè)GSM編號(hào)后的描述纵东,也就是"GSM2686880 SET-2_STAT1-D1",這兩個(gè)部分你都需要粘招。通過檢查網(wǎng)頁元素,你發(fā)現(xiàn)了一個(gè)規(guī)律偎球,也就是這兩個(gè)元素是在一個(gè)tr
內(nèi)
你信心滿滿的構(gòu)建了一個(gè)匹配模式洒扎,結(jié)果啥都沒有匹配到
pattern = re.compile("<tr><td.*?><a.*?>(GSM\d+)</a></td><td.*?>(.*?)</td>")
re.search(pattern, contents)
你發(fā)現(xiàn)這似乎由于這個(gè)HTML里有很多神奇的空白和"\n",原本方便人類閱讀的記號(hào)卻阻礙了數(shù)據(jù)處理,你必須做點(diǎn)什么衰絮,你想到了可以用re.sub
進(jìn)行替換袍冷,所以你做了如下的事情
contents = re.sub(r"\n\s*","",contents)
最后你終于用原來的匹配模式得到了以元組數(shù)據(jù)結(jié)構(gòu)的結(jié)果
result = re.findall(pattern, contents)
下一步根據(jù)GSMxxx編號(hào)去提取SRX編碼。這一步的核心就是從元祖中提取元素猫牡,然后構(gòu)建一個(gè)url去爬取新的網(wǎng)頁胡诗,然后提取SRX編號(hào)即可以。先測(cè)試第一個(gè)淌友,
r1 = results[0][0]
r1_resp = requests.get(base_url + r1)
m = re.search("SRX\d+", r1_resp.text)
m.group(0)
然后開始遍歷,存儲(chǔ)到字典中煌恢。考慮到網(wǎng)絡(luò)延遲所耽誤的時(shí)間遠(yuǎn)遠(yuǎn)大于內(nèi)存分配的時(shí)間震庭,也就沒有必須要預(yù)先分配內(nèi)存空間瑰抵。
sra_dict = {}
for acc in results:
key = acc[0]
resp = requests.get(base_url + key)
value = re.search("SRX\d+",resp.text).group(0)
sra_dict[key] = value
事實(shí)證明網(wǎng)絡(luò)不好,這個(gè)簡單的程序是可以跑半天的器联。