python中如何使用正則表達(dá)式提取數(shù)據(jù)
正則表達(dá)式是一個特殊的字符序列,它能幫助你方便的檢查一個字符串是否與某種模式匹配。
re 模塊使 Python 語言擁有全部的正則表達(dá)式功能。
compile 函數(shù)可創(chuàng)建一個模式字符串和可選的標(biāo)志參數(shù)組成的一個正則表達(dá)式對象。該對象擁有一系列方法用于正則表達(dá)式匹配和替換。
re 模塊也提供了與這些方法功能完全一致的函數(shù),這些函數(shù)使用一個模式字符串做為它們的第一個參數(shù)。
模式 | 描述 |
---|---|
^ | 匹配字符串的開頭 |
$ | 匹配字符串的末尾。 |
. | 匹配任意字符,除了換行符,當(dāng)re.DOTALL標(biāo)記被指定時,則可以匹配包括換行符的任意字符。 |
[...] | 用來表示一組字符,單獨列出:[amk] 匹配 'a','m'或'k' |
[^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
re* | *匹配0次或多次。貪婪方式,re代表正則表達(dá)式 |
re+ | +匹配1次或多次。 |
re? | ?匹配0次或1次,非貪婪方式,匹配0次指表達(dá)式后面為空的也匹配 |
re{ n} | 連續(xù)匹配 n 個前面表達(dá)式。例如, o{2},連續(xù)匹配兩次o, 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的兩個 o。 |
re{ n,} | 匹配 n 個前面表達(dá)式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等價于 "o+"。"o{0,}" 則等價于 "o*"。 |
re{ n, m} | 表示匹配 連續(xù)的 前面的表達(dá)式 至少n次,至多 m 次。表達(dá)式 油{3,4} 就表示匹配 連續(xù)的 油 字 至少3次,至多 4 次 |
a| b | 匹配a或b |
(re) | 對正則表達(dá)式分組并記住匹配的文本 |
常用正則表達(dá)式實例
字符匹配
實例 | 描述 |
---|---|
python | 匹配 "python". |
字符類
實例 | 描述 |
---|---|
[Pp]ython | 匹配 "Python" 或 "python" |
rub[ye] | 匹配 "ruby" 或 "rube" |
[aeiou] | 匹配中括號內(nèi)的任意一個字母 |
[0-9] | 匹配任何數(shù)字。類似于 [0123456789] |
[a-z] | 匹配任何小寫字母 |
[A-Z] | 匹配任何大寫字母 |
[a-zA-Z0-9] | 匹配任何字母及數(shù)字 |
[^aeiou] | 除了aeiou字母以外的所有字符 |
[^0-9] | 匹配除了數(shù)字外的字符 |
特殊字符類
實例 | 描述 |
---|---|
. | 匹配除 "\n" 之外的任何單個字符。要匹配包括 '\n' 在內(nèi)的任何字符,請使用象 '[.\n]' 的模式。 |
\d | 匹配一個數(shù)字字符。等價于 [0-9]。 |
\D | 匹配一個非數(shù)字字符。等價于 [^0-9]。 |
\s | 匹配任何空白字符,包括空格、制表符、換頁符等等。等價于 [ \f\n\r\t\v]。 |
\S | 匹配任何非空白字符。等價于 [^ \f\n\r\t\v]。 |
\w | 匹配包括下劃線的任何單詞字符。等價于'[A-Za-z0-9_]'。 |
\W | 匹配任何非單詞字符。等價于 '[^A-Za-z0-9_]'。 |
re.match只匹配字符串的開始,如果字符串開始不符合正則表達(dá)式,則匹配失敗,函數(shù)返回None;而re.search匹配整個字符串,直到找到第一個匹配。
# re.match()函數(shù)只能從起始的位置匹配,否則返回None import re matchObj = re.match('www', 'www.runoob.com') print(re.match('www', 'www.runoob.com').span()) # 在起始位置匹配 print(re.match('com', 'www.runoob.com')) # 不在起始位置匹配 # (0, 3) # None # 使用group(num) 或 groups() 函數(shù)來獲取用來匹配的正在表達(dá)式提取的值。 if matchObj: print("matchObj.group() : ", matchObj.group()) # 執(zhí)行結(jié)果是matchObj.group() : www # matchObj.group() 等同于 matchObj.group(0),表示匹配到的完整文本字符 # print ("matchObj.group(1) : ", matchObj.group(1)) # print ("matchObj.group(2) : ", matchObj.group(2)) # re.search 掃描整個字符串并返回第一個成功的匹配。 import re line = "Cats are smarter than dogs"; searchObj = re.search(r'(.*) are (.*?) ', line, re.M | re.I) if searchObj: print("searchObj.group() : ", searchObj.group()) print("searchObj.group(1) : ", searchObj.group(1)) print("searchObj.group(2) : ", searchObj.group(2)) # 執(zhí)行結(jié)果 # searchObj.group() : Cats are smarter # searchObj.group(1) : Cats # searchObj.group(2) : smarter # re.sub()用于替換字符串中的匹配項 import re phone = "2004-959-559 # 這是一個國外電話號碼" # 刪除字符串中的 Python注釋,$匹配字符串的末尾。 # 把匹配到的字符串替換為空字符串 num = re.sub(r'#.*$', "", phone) print("電話號碼是: ", num) # 電話號碼是: 2004-959-559 # 刪除非數(shù)字(-)的字符串,\D 匹配任意非數(shù)字 num = re.sub(r'\D', "", phone) print("電話號碼是 : ", num) # 電話號碼是 : 2004959559 """ findall在字符串中找到正則表達(dá)式所匹配的所有子串,并返回一個列表,如果沒有找到匹配的,則返回空列表。 注意: match 和 search 是匹配一次 findall 匹配所有。 """ import re pattern = re.compile(r'\d+') # 創(chuàng)建個正著表達(dá)式對象,查找數(shù)字 result1 = pattern.findall('runoob 123 google 456') result2 = pattern.findall('run88oob123google456', 0, 10) result3 = pattern.search('runoob 123 google 456') # 123 只匹配了一次,匹配首個符合要求的字符串 print(result1) print(result2) print(result3.group()) # 執(zhí)行結(jié)果: # ['123', '456'] # ['88', '12'] # 123
re.compile 函數(shù)
compile 函數(shù)用于編譯正則表達(dá)式,生成一個正則表達(dá)式( Pattern )對象,供 match() 和 search() 這兩個函數(shù)使用。
語法格式為:
import re pattern = re.compile(r'\d+') # 用于匹配至少一個數(shù)字 m = pattern.match('one12twothree34four') # 查找頭部,沒有匹配 print(m) # None m = pattern.match('one12twothree34four', 2, 10) # 從'e'的位置開始匹配,沒有匹配 print(m) # None m = pattern.match('one12twothree34four', 3, 10) # 從'1'的位置開始匹配,正好匹配 print(m) # 返回一個 Match 對象 # <re.Match object; span=(3, 5), match='12'> print(m.group(0)) # 可省略 0,獲得整個匹配的子串時,可直接使用 group() 或 group(0); # '12' print(m.start(0)) # 可省略 0,獲取分組匹配的子串在整個字符串中的起始位置(子串第一個字符的索引),參數(shù)默認(rèn)值為 0; # 3 print(m.end(0)) # 可省略 0,獲取分組匹配的子串在整個字符串中的結(jié)束位置(子串最后一個字符的索引+1),參數(shù)默認(rèn)值為 0; 5 print(m.span(0)) # 可省略 0,返回 (start(group), end(group))。 # (3, 5)
執(zhí)行結(jié)果:
None
None
<re.Match object; span=(3, 5), match='12'>
12
3
5
(3, 5)
括號()-分組
括號稱之為 正則表達(dá)式的 組選擇。
組 就是把 正則表達(dá)式 匹配的內(nèi)容 里面 其中的某些部分 標(biāo)記為某個組。
我們可以在 正則表達(dá)式中 標(biāo)記 多個 組
為什么要有組的概念呢?因為我們往往需要提取已經(jīng)匹配的 內(nèi)容里面的 某些部分的信息。
前面,我們有個例子,從下面的文本中,選擇每行逗號前面的字符串,也 包括逗號本身 。
蘋果,蘋果是綠色的
橙子,橙子是橙色的
香蕉,香蕉是黃色的
就可以這樣寫正則表達(dá)式 ^.*,
。
但是,如果我們要求 不要包括逗號 呢?
當(dāng)然不能直接 這樣寫 ^.*
因為最后的逗號 是 特征 所在, 如果去掉它,就沒法找 逗號前面的了。
但是把逗號放在正則表達(dá)式中,又會包含逗號。
解決問題的方法就是使用 組選擇符 : 括號。
我們這樣寫 ^(.*),
,結(jié)果如下
大家可以發(fā)現(xiàn),我們把要從整個表達(dá)式中提取的部分放在括號中,這樣 水果 的名字 就被單獨的放在 組 group 中了。
對應(yīng)的Python代碼如下
content = '''蘋果,蘋果是綠色的 橙子,橙子是橙色的 香蕉,香蕉是黃色的''' import re p = re.compile(r'^(.*),', re.MULTILINE) for one in p.findall(content): print(one)
多個分組時,怎么取每個分組的值。
比如,我們要從下面的文本中,提取出每個人的 名字 和對應(yīng)的 手機(jī)號
張三,手機(jī)號碼15945678901
李四,手機(jī)號碼13945677701
王二,手機(jī)號碼13845666901
可以使用這樣的正則表達(dá)式 ^(.+),.+(\d{11})
可以寫出如下的代碼
content = '''張三,手機(jī)號碼15945678901 李四,手機(jī)號碼13945677701 王二,手機(jī)號碼13845666901''' import re p = re.compile(r'^(.+),.+(\d{11})', re.MULTILINE) print(p.findall(content)) #findall()方法返回的是列表 m = p.search(content) #列表不能調(diào)用group,因此需使用search()方法,但search方法只能匹配第一個符合的 print(m.group(1)) print(m.group(2)) for one in p.findall(content): print(one) print(type(one)) #執(zhí)行結(jié)果 # [('張三', '15945678901'), ('李四', '13945677701'), ('王二', '13845666901')] # 張三 # 15945678901 # ('張三', '15945678901') # <class 'tuple'> # ('李四', '13945677701') # <class 'tuple'> # ('王二', '13845666901') # <class 'tuple'>
當(dāng)有多個分組的時候,我們可以使用 (?P<分組名>...)
這樣的格式,給每個分組命名。
這樣做的好處是,更方便后續(xù)的代碼提取每個分組里面的內(nèi)容
比如
import re p = re.compile(r'^(?P<name>.+),.+(?P<phone>\d{11})', re.MULTILINE) print(p.finditer(content)) # 返回string中所有與pattern相匹配的全部字串,返回形式為迭代器。 for match in p.finditer(content): print(match.group('name')) print(match.group('phone')) # 執(zhí)行結(jié)果 # <callable_iterator object at 0x00000000027C2518> # 張三 # 15945678901 # 李四 # 13945677701 # 王二 # 13845666901
總結(jié):正則若匹配成功,match()/search()返回的是Match對象,finditer()返回的是Match對象的迭代器,獲取匹配結(jié)果需要調(diào)用Match對象的group()、groups或group(index)方法。
group()
:母串中與模式pattern匹配的子串;group(0)
:結(jié)果與group()一樣;groups()
:所有g(shù)roup組成的一個元組,group(1)是字符串中第一個匹配成功的子串分組,group(2)是第二個,依次類推,如果index超了邊界,拋出IndexError;findall()
:返回的就是所有匹配的子串?dāng)?shù)組,就是子串元組組成的列表,例如上面的例子,母串中的第一行組成一個元組,第二行組成一個元組,這些元組共同構(gòu)成一個list,就是findall()的返回結(jié)果。
方括號-匹配幾個字符之一
方括號表示要匹配 指定的幾個字符之一 。
比如
[abc]
可以匹配 a, b, 或者 c 里面的任意一個字符。等價于 [a-c]
。
[a-c]
中間的 - 表示一個范圍從a 到 c。
如果你想匹配所有的小寫字母,可以使用 [a-z]
一些 元字符 在 方括號內(nèi) 失去了魔法, 變得和普通字符一樣了。
比如
[akm.]
匹配 a k m .
里面任意一個字符
這里 .
在括號里面不在表示 匹配任意字符了,而就是表示匹配 .
這個 字符
如果在方括號中使用 ^
, 表示 非
方括號里面的字符集合。
比如
content = 'a1b2c3d4e5' import re p = re.compile(r'[^\d]' ) for one in p.findall(content): print(one)
[^\d]
表示,選擇非數(shù)字的字符
輸出結(jié)果為:
a
b
c
d
e
切割字符串
字符串 對象的 split
方法只適用于 簡單的字符串分割。 有時,你需要更加靈活的字符串切割。
比如,我們需要從下面字符串中提取武將的名字。
names = '關(guān)羽; 張飛, 趙云,馬超, 黃忠 李逵'
我們發(fā)現(xiàn)這些名字之間, 有的是分號隔開,有的是逗號隔開,有的是空格隔開, 而且分割符號周圍還有不定數(shù)量的空格
這時,可以使用正則表達(dá)式里面的 split 方法:
import re names = '關(guān)羽; 張飛, 趙云, 馬超, 黃忠 李逵' namelist = re.split(r'[;,\s]\s*', names) print(namelist)
正則表達(dá)式 [;,\s]\s*
指定了,分割符為 分號、逗號、空格 里面的任意一種均可,并且 該符號周圍可以有不定數(shù)量的空格。
字符串替換
匹配模式替換
字符串 對象的 replace
方法只適應(yīng)于 簡單的 替換。 有時,你需要更加靈活的字符串替換。
比如,我們需要在下面這段文本中 所有的 鏈接中 找到所以 /avxxxxxx/
這種 以 /av
開頭,后面接一串?dāng)?shù)字, 這種模式的字符串。
然后,這些字符串全部 替換為 /cn345677/
。
names = ''' 下面是這學(xué)期要學(xué)習(xí)的課程: <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是牛頓第2運動定律 <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是畢達(dá)哥拉斯公式 <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是切割磁力線 '''
被替換的內(nèi)容不是固定的,所以沒法用 字符串的replace方法。
這時,可以使用正則表達(dá)式里面的 sub 方法:
import re names = ''' 下面是這學(xué)期要學(xué)習(xí)的課程: <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是牛頓第2運動定律 <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是畢達(dá)哥拉斯公式 <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是切割磁力線 ''' newStr = re.sub(r'/av\d+?/', '/cn345677/' , names) print(newStr)
sub 方法就是也是替換 字符串, 但是被替換的內(nèi)容 用 正則表達(dá)式來表示 符合特征的所有字符串。
比如,這里就是第一個參數(shù) /av\d+?/
這個正則表達(dá)式,表示以 /av
開頭,后面是一串?dāng)?shù)字,再以 /
結(jié)尾的 這種特征的字符串 ,是需要被替換的。
第二個參數(shù),這里 是 '/cn345677/'
這個字符串,表示用什么來替換。
第三個參數(shù)是 源字符串。
指定替換函數(shù)
剛才的例子中,我們用來替換的是一個固定的字符串 /cn345677/
。
如果,我們要求,替換后的內(nèi)容 的是原來的數(shù)字+6, 比如 /av66771949/
替換為 /av66771955/
。
怎么辦?
這種更加復(fù)雜的替換,我們可以把 sub的第2個參數(shù) 指定為一個函數(shù) ,該函數(shù)的返回值,就是用來替換的字符串。
如下
import re names = ''' 下面是這學(xué)期要學(xué)習(xí)的課程: <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是牛頓第2運動定律 <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是畢達(dá)哥拉斯公式 <a target='_blank'>點擊這里,邊看視頻講解,邊學(xué)習(xí)以下內(nèi)容</a> 這節(jié)講的是切割磁力線 ''' # 替換函數(shù),參數(shù)是 Match對象 def subFunc(match): # Match對象 的 group(0) 返回的是整個匹配上的字符串 src = match.group(0) # Match對象 的 group(1) 返回的是第一個group分組的內(nèi)容 number = int(match.group(1)) + 6 dest = f'/av{number}/' print(f'{src} 替換為 {dest}') # 返回值就是最終替換的字符串 return dest newStr = re.sub(r'/av(\d+?)/', subFunc , names) print(newStr)
# 正則表達(dá)式提取 import re content = ''' Python3 高級開發(fā)工程師 上?;ソ探逃萍加邢薰旧虾?浦東新區(qū)2萬/月02-18滿員 測試開發(fā)工程師(C++/python) 上海墨鹍數(shù)碼科技有限公司上海-浦東新區(qū)2.5萬/每月02-18未滿員 Python3 開發(fā)工程師 上海德拓信息技術(shù)股份有限公司上海-徐匯區(qū)1.3萬/每月02-18剩余11人 測試開發(fā)工程師(Python) 赫里普(上海)信息科技有限公司上海-浦東新區(qū)1.1萬/每月02-18剩余5人 Python高級開發(fā)工程師 上海行動教育科技股份有限公司上海-閔行區(qū)2.8萬/月02-18剩余255人 python開發(fā)工程師 上海優(yōu)似騰軟件開發(fā)有限公司上海-浦東新區(qū)2.5萬/每月02-18滿員 ''' for one in re.findall(r'([\d.]+)萬/每{0,1}月', content): print(one)
#非正則表達(dá)式提取 content = ''' Python3 高級開發(fā)工程師 上海互教教育科技有限公司上海-浦東新區(qū)2萬/月02-18滿員 測試開發(fā)工程師(C++/python) 上海墨鹍數(shù)碼科技有限公司上海-浦東新區(qū)2.5萬/每月02-18未滿員 Python3 開發(fā)工程師 上海德拓信息技術(shù)股份有限公司上海-徐匯區(qū)1.3萬/每月02-18剩余11人 測試開發(fā)工程師(Python) 赫里普(上海)信息科技有限公司上海-浦東新區(qū)1.1萬/每月02-18剩余5人 Python高級開發(fā)工程師 上海行動教育科技股份有限公司上海-閔行區(qū)2.8萬/月02-18剩余255人 python開發(fā)工程師 上海優(yōu)似騰軟件開發(fā)有限公司上海-浦東新區(qū)2.5萬/每月02-18滿員 ''' # 將文本內(nèi)容按行分割,放入列表,按\r\n,\r(回車),\n(換行)分割 lines = content.splitlines() # print(lines) for line in lines: # 查找'萬/月' 在 字符串中什么地方 # find() 方法檢測字符串中是否包含子字符串 str ,如果包含的話,返回子字符串開始的索引, # 不包含的話返回-1 pos2 = line.find('萬/月') # print(pos2) if pos2 < 0: # 查找'萬/每月' 在 字符串中什么地方 pos2 = line.find('萬/每月') # 都找不到,滿足條件,觸發(fā)continue,不執(zhí)行后面的代碼,跳到循環(huán)開頭進(jìn)入下一輪循環(huán) if pos2 < 0: continue # 執(zhí)行到這里,說明可以找到薪資關(guān)鍵字 # 接下來分析 薪資 數(shù)字的起始位置 # 方法是 找到 pos2 前面薪資數(shù)字開始的位置 idx = pos2 - 1 # 只要是數(shù)字或者小數(shù)點,就繼續(xù)往前面找 # isdigit()方法檢測字符串是否只由數(shù)字組成,如果字符串只包含數(shù)字則返回 True 否則返回 False while line[idx].isdigit() or line[idx] == '.': idx -= 1 # 現(xiàn)在 idx 指向 薪資數(shù)字前面的那個字, # 所以薪資開始的 索引 就是 idx+1 pos1 = idx + 1 print(line[pos1:pos2])
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
python3整數(shù)反轉(zhuǎn)的實現(xiàn)方法
這篇文章主要介紹了python3整數(shù)反轉(zhuǎn)的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04