Python使用正則表示式實現爬蟲資料抽取
1. 為什麼要使用正則表示式?
首先,大家來看一個例子。一個文字檔案裡面儲存了一些市場職位資訊,格式如下所示:
Python3 高階開發工程師 上海互教教育科技有限公司上海-浦東新區2萬/月02-18滿員
測試開發工程師(C++/python) 上海墨鵾數碼科技有限公司上海-浦東新區2.5萬/每月02-18未滿員
Python3 開發工程師 上海德拓資訊科技股份有限公司上海-徐彙區1.3萬/每月02-18剩餘11人
測試開發工程師(Python) 赫裡普(上海)資訊科技有限公司上海-浦東新區1.1萬/每月02-18剩餘5人
Python高階開發工程師 上海行動教育科技股份有限公司上海-閔行區2.8萬/月02-18剩餘255人python開發工程師 上海優似騰軟體開發有限公司上海-浦東新區2.5萬/每月02-18滿員
現在,我們需要編寫一個程式,從這些文本里面抓取所有職位的薪資。獲取結果如下所示:
2
2.5
1.3
1.1
2.8
2.5
怎麼做?大家可以先自己思考一下。這是典型的字串處理。分析這裡面的規律,可以發現,薪資的數字後面都有關鍵字萬/月
或者萬/每月
。根據我們學過的知識,我們不難寫出下面的程式碼:
html_str = """ Python3 高階開發工程師 上海互教教育科技有限公司上海-浦東新區2萬/月02-18滿員 測試開發工程師(C++/python) 上海墨鵾數碼科技有限公司上海-浦東新區2.5萬/每月02-18未滿員 Python3 開發工程師 上海德拓資訊科技股份有限公司上海-徐彙區1.3萬/每月02-18剩餘11人 測試開發工程師(Python) 赫裡普(上海)資訊科技有限公司上海-浦東新區1.1萬/每月02-18剩餘5人 Python高階開發工程師 上海行動教育科技股份有限公司上海-閔行區2.8萬/月02-18剩餘255人 python開發工程師 上海優似騰軟體開發有限公司上海-浦東新區2.5萬/每月02-18滿員 """ # 將字串html_str中每一行的資料提取出來存入到一個列表中 position_info_list = html_str.splitlines() for position_info in position_info_list: # 遍歷 if position_info: # 判斷是否有資料 # 查詢萬/月或者是萬/每月的索引 idx = position_info.find("萬/月") if position_info.find("萬/月") != -1 else position_info.find("萬/每月") end_pos = idx # 記錄結束位置 if idx == -1: continue # 上面兩種都沒找到 find_start = idx - 1 # 記錄萬字前的位置 while position_info[find_start].isdigit() or position_info[find_start] == ".": find_start -= 1 start_pos = find_start + 1 # 開始位置 print(position_info[start_pos: end_pos]) # 切片獲取薪資
執行一下,發現完全可以。如圖所示:
在你高興完之後,我們再看看寫的程式碼。怎麼樣?太麻煩了,是不是。為了從每行獲取薪資對應的數字,我們可是寫了不少行程式碼。這種從字串
中搜索出某種特徵的子串
有沒有更簡單的方法呢?解決方案就是我們今天要介紹的正則表示式
。如果我們使用正則表示式,程式碼可以這樣:
import re html_str = """ Python3 高階開發工程師 上海互教教育科技有限公司上海-浦東新區2萬/月02-18滿員 測試開發工程師(C++/python) 上海墨鵾數碼科技有限公司上海-浦東新區2.5萬/每月02-18未滿員 Python3 開發工程師 上海德拓資訊科技股份有限公司上海-徐彙區1.3萬/每月02-18剩餘11人 測試開發工程師(Python) 赫裡普(上海)資訊科技有限公司上海-浦東新區1.1萬/每月02-18剩餘5人 Python高階開發工程師 上海行動教育科技股份有限公司上海-閔行區2.8萬/月02-18剩餘255人 python開發工程師 上海優似騰軟體開發有限公司上海-浦東新區2.5萬/每月02-18滿員 """ salary_list = re.findall(r"([\d.]+)萬/每?月",html_str) for salary in salary_list: print(salary)
執行一下看看,結果是一樣的。但是程式碼卻簡單多了。從上面的例子可以看出,用正則表示式關鍵的地方在於如何寫出正確的表示式語法
。正則表示式非常強大,語法非常複雜,如果你英文閱讀能力還可以,那太好了,點選這裡,參考Python官方文件裡面的描述 。具體的使用細節包括語法都在裡面。本文會給大家介紹一些常見的正則表示式語法。
2. 什麼是正則表示式?
在處理字串時,經常會有查詢符合某些複雜規則的字串的需求。正則表示式就是用於描述這些規則的工具。換句話說,正則表示式就是記錄文字規則的程式碼
。對於接觸過DOS/終端
的使用者來說,如果想匹配當前資料夾下所有的文字檔案,可以輸入dir *.txt/ls *.txt
命令,按<Enter>
鍵後,所有.txt
檔案將會被列出來。這裡的*.txt
即可理解為一個簡單的正則表示式。
在資料庫
中使用正則表示式,如圖所示:
3. re模組操作
Python提供了re模組
,用於實現正則表示式的操作。在實現時,可以使用re模組
提供的方法search()、 match()、findall()
等進行字串處理,也可以先使用re模組
的compile()方法
將模式字串轉換為正則表示式物件,然後再使用該正則表示式物件的相關方法來操作字串。re模組
在使用時,需要先應用import語句
引入,具體程式碼如下:
import re
這裡因為我們還沒有學習匹配的規則,所以先學習一下match
方法,其他的方法在本文末尾講解。match()方法
用於從字串的開始處進行匹配,如果在起始位置匹配成功,則返回Match物件
,否則返回None
,語法格式如下:
re.match(pattern,string,[flags] )
引數說明:
1. pattern:表示模式字串,由要匹配的正則表示式轉換而來。
2. string:表示要匹配的字串。
3. flags:可選引數,表示標誌位,用於控制匹配方式,如是否區分字母大小寫。
常用的flags
如下表所示:
標誌 | 說明 |
---|---|
A 或ASCII | 對於\w、\W、\b、\B、\d、\D、\s和\S只進行ASCII匹配(僅適用於Python 3.x) |
I或IGNORECASE | 執行不區分字母大小寫的匹配 |
M或MULTILINE | 將^和$用於包括整個字串的開始和結尾的每一行(預設情況下,僅適用於整個字串的開始和結尾處) |
S或DOTALL | 使用(.)字元匹配所有字元,包括換行符 |
X或VERBOSE | 忽略模式字串中未轉義的空格和註釋 |
例如,匹配字串是否以amo_
開頭,不區分字母大小寫,程式碼如下:
從上面的執行結果中可以看出,字串Amo_cool
是以amo_
開頭,所以返回一個Match物件
,而字串外貌描述 Amo_ cool
不是以amo_
開頭,將返回None
。這是因為match()方法
從字串的開始位置開始匹配,當第一個字母
不符合條件時,則不再進行匹配,直接返回None。Match物件中包含了匹配值的位置和匹配資料。其中,要獲取匹配值的起始位置可以使用Match物件的start() 方法
要獲取匹配值的結束位置可以使用end()方法
通過span()方法
可以返回匹配位置的元組 通過string屬性
可以獲取要匹配的字串。例如下面的程式碼:
import re pattern = r"amo_" # 模式字串 str1 = "Amo_cool amo_cool" # 要匹配的字串 match = re.match(pattern,str1,re.I) # 匹配字串 不區分大小寫 print(f"匹配值的起始位置: {match.start()}") print(f"匹配值的結束位置: {match.end()}") print(f"匹配位置的元組: {match.span()}") print(f"要匹配的字串: {match.string}") print(f"匹配資料: {match.group()}"
執行結果如圖所示:
Python中字串前面加上r
表示原生字串,與大多數程式語言相同,正則表示式裡使用\
作為轉義字元,這就可能造成反斜槓困擾。假如你需要匹配文字中的字元\
,那麼使用程式語言表示的正則表示式裡將需要4個反斜槓\
:前兩個和後兩個分別用於在程式語言裡轉義成反斜槓,轉換成兩個反斜槓後再在正則表示式裡轉義成一個反斜槓。Python裡的原生字串很好地解決了這個問題,有了原生字串,你再也不用擔心是不是漏寫了反斜槓,寫出來的表示式也更直觀。如圖所示:
4. 匹配單個字元
在上一小節中,瞭解到通過re模組
能夠完成使用正則表示式來匹配字串。本小節,將要講解正則表示式的單字元匹配,具體的規則,如下所示:
例項 | 描述 |
---|---|
. | 匹配除"\n"之外的任何單個字元。要匹配包括"\n"在內的任何字元,請使用"[.\n]"模式。 |
\d | 匹配一個數字字元。等價於 [0-9]。 |
\D | 匹配一個非數字字元。等價於 [^0-9]。 |
\s | 匹配任何空白字元,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。 |
\S | 匹配任何非空白字元。等價於 [^ \f\n\r\t\v]。 |
\w | 匹配包括下劃線的任何單詞字元。等價於"[A-Za-z0-9_]"。 |
\W | 匹配任何非單詞字元。等價於"[^A-Za-z0-9_]"。 |
[…] | 用來表示一組字元,單獨列出:[amk] 匹配 ‘a',‘m'或'k' |
[^…] | 不在[]中的字元:[^abc] 匹配除了a,b,c之外的字元。 |
^ | 匹配字串的開頭 |
$ | 匹配字串的結尾 |
例子如下:
5. 匹配多個字元
匹配多個字元的相關格式:
例項 | 描述 |
---|---|
re* | 匹配0個或多個的表示式 。 |
re+ | 匹配1個或多個的表示式。 |
re? | 匹配0個或1個由前面的正則表示式定義的片段,非貪婪方式。 |
re{n} | 匹配n個前面表示式。例如,o{2}不能匹配Bob中的o,但是能匹配food中的兩個o。 |
re{n,} | 精確匹配n個前面表示式。例如,o{2,}不能匹配Bob中的o,但能匹配foooood中的所有o。o{1,}等價於o+。o{0,}則等價於o*。 |
re{n,m} | 匹配 n 到 m 次由前面的正則表示式定義的片段,貪婪方式 |
例子如下:
6. 匹配分組
例項 | 描述 |
---|---|
a|b | 匹配a或b |
(re) | 匹配括號內的表示式,也表示一個組 |
\num | 引用分組num匹配到的字串 |
(?P<name>) | 分組起別名 |
(?P=name) | 引用別名為name分組匹配到的字串 |
練習1:匹配出0-100之間的數字
result = re.match(r"[1-9]?\d$|100","70").group()
練習2:匹配出163、126、qq、sina郵箱
要求:可使用英文小寫
數字
下劃線
,下劃線不能在首尾
且@符號之前有4到16位
字元
result = re.match(r"^[a-z0-9][a-z0-9_]{2,14}[a-z0-9]@(163|126|qq|sina)\.com$","[email protected]").group()
練習3:匹配出<html><body>amo666</body></html>
import re str1 = "<html><body>amo666</body></html>" pattern1 = r"<([a-zA-Z]*)><([a-zA-Z]*)>.*</\2></\1>" match_obj1 = re.match(pattern1,str1) print(match_obj1.group()) pattern2 = r"<(?P<name1>[a-zA-Z]*)><(?P<name2>[a-zA-Z]*)>.*</(?P=name2)></(?P=name1)>" match_obj2 = re.match(pattern2,str1) print(match_obj2.group())
執行結果如下:
<html><body>amo666</body></html>
<html><body>amo666</body></html>
7. re模組的高階用法
7.1 使用search()方法進行匹配
search()方法
用於在整個字串中搜索第一個匹配的值, 如果匹配成功,則返回Match物件,否則返回None,語法格式如下:
re. search(pattern,[flags])
引數說明:
- pattern:表示模式字串,由要匹配的正則表示式轉換而來。
- string:表示要匹配的字串。
- flags:可選引數,表示標誌位,用於控制匹配方式,如是否區分字母大小寫。
例如,搜尋第一個以amo_
開頭的字串,不區分字母大小寫,程式碼如下:
import re match_obj1 = re.search(r"amo_\w+","Amo_SHOP amo_shop",re.I) print(match_obj1) match_obj2 = re.search(r"amo_\w+","專案名稱Amo_SHOP amo_shop",re.I) print(match_obj2)
執行結果如下:
從上面的執行結果中可以看出,search()方法
不僅僅是在字串的起始位置搜尋,其他位置有符合的匹配也可以。
7.2 使用findall()方法進行匹配
findall()方法
用於在整個字串中搜索所有符合正則表示式的字串,並以列表的形式返回。如果匹配成功,則返回包含匹配結構的列表,否則返回空列表。其語法格式如下:
re. findall(pattern,[flags])
引數說明:
- pattern:表示模式字串,由要匹配的正則表示式轉換而來。
- string:表示要匹配的字串。
- flags:可選引數,表示標誌位,用於控制匹配方式,如是否區分字母大小寫。
例如,搜尋以amo_
開頭的字串,不區分字母大小寫,程式碼如下:
import re result1 = re.findall(r"amo_\w+",re.I) print(result1) result2 = re.findall(r"amo_\w+","專案名稱Amo_SHOP amo_shop") print(result2)
執行結果如下:
如果在指定的模式字串中,包含分組,則返回與分組匹配的文字列表。例如:
import re result1 = re.findall(r"[1-9]{1,3}(\.[0-9]{1,3}){3}","127.0.0.1 192.168.31.157") print(result1)
上面的程式碼的執行結果如下:
['.1','.157']
從上面的結果中可以看出,並沒有得到匹配的IP地址,這是因為在模式字串中出現了分組,所以得到的結果是根據分組進行匹配的結果,即(\.[0一9]{1,3})
匹配的結果。如果想獲取整個模式字串的匹配,可以將整個模式字串使用一對小括號進行分組,然後在獲取結果時,只取返回值列表的每個元素(是一個元組)的第1個元素。程式碼如下:
import re str1 = "127.0.0.1 192.168.31.157" result1 = re.findall(r"([1-9]{1,3}){3})",str1) for item in result1: print(item[0])
執行結果如下:
127.0.0.1
192.168.31.157
7.3 替換字串
sub()方法
用於實現字串替換,語法格式如下:
re. sub( pattern,repl,count,flags)
引數說明:
- pattern:表示模式字串,由要匹配的正則表示式轉換而來。
- repl: 表示替換的字串。
- string:表示要被查詢替換的原始字串。
- count:可選引數,表示模式匹配後替換的最大次數,預設值為0,表示替換所有的匹配。
- flags:可選引數,表示標誌位,用於控制匹配方式,如是否區分字母大小寫。
例如,隱藏中獎資訊中的手機號碼,程式碼如下:
import re pattern = r"1[34578]\d{9}" str1 = "中獎號碼為: 84978981 聯絡電話為: 13611111111" result = re.sub(pattern,"1XXXXXXXXXX",str1) print(result)
執行結果如下:
中獎號碼為: 84978981 聯絡電話為: 1XXXXXXXXXX
7.4 使用正則表示式分割字串
split()方法
用於實現根據正則表示式分割字串,並以列表的形式返回,其作用與字串物件的split()方法類似,所不同的就是分割字元由模式字串指定。語法格式如下:
re.split(pattern,[maxsplit],[flags])
引數說明:
- pattern:表示模式字串,由要匹配的正則表示式轉換而來。
- string:表示要匹配的字串。
- maxsplit:可選引數,表示最大的拆分次數。
- flags:可選引數,表示標誌位,用於控制匹配方式,如是否區分字母大小寫。
例如,從給定的URL地址中提取出請求地址和各個引數,程式碼如下:
import re pattern = r"[?|&]" url = "https://study.163.com/courses-search?keyword=python&username=amo" result = re.split(pattern,url) print(result)
執行結果如下:
['https://study.163.com/courses-search','keyword=python','username=amo']
關於正則表示式的貪婪
和非貪婪
可以點選這裡正則表示式的貪婪模式與非貪婪模式參考。
到此這篇關於Python使用正則表示式實現爬蟲資料抽取的文章就介紹到這了,更多相關Python 正則表示式資料抽取內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!