現在
你已經會使用 python 模擬瀏覽器
進行一些 Http 的請求了
那麼請求完之後
服務器返回給我們一堆源代碼
我們可不是啥都要的啊
我們是有原則的
我們想要的東西
怎麼能一股腦的啥都往自己兜裏塞呢?
使不得
使不得
所以
在服務器返回給我們的源碼之中
我們要過濾
拿到我們想要的就好
其它就丟一旁
那麼
我們就需要學會怎麼使用
正則表達式
通過它
我們才能過濾出我們想要的內容
...
接下來就是
真香警告
這篇文章不適合急性子的人看,要不然會把手機砸了的!但是,如果你能看完,那麼正則表達式對你來說,算個 p 的難度啊?
其實
正則表達式不僅僅適用於 python
很多編程語言
很多地方都會使用到正則
試想一下
如何從下面這段字符串中快速檢索所有的數字出來呢?
zui12shu234ai45der6en7sh88ixia7898os0huaib
簡單來說
正則表達式就是定義一些特殊的符號
來匹配不同的字符
比如
\d
就可以代表
一個數字,等價於 0-9 的任意一個
那麼你肯定想知道
其它的特殊符號表示的啥意思吧?
恩
就不告訴你
本篇完
再見
這是各種符號的解釋
字符 | 描述 |
---|---|
\ |
將下一個字符標記爲一個特殊字符(File Format Escape,清單見本表)、或一個原義字符(Identity Escape,有^$()*+?.[\{|共計12個)、或一個向後引用(backreferences)、或一個八進制轉義符。例如,“n ”匹配字符“n ”。“\n ”匹配一個換行符。序列“\\ ”匹配“\ ”而“\( ”則匹配“( ”。 |
^ |
匹配輸入字符串的開始位置。如果設置了RegExp對象的Multiline屬性,^也匹配“\n ”或“\r ”之後的位置。 |
$ |
匹配輸入字符串的結束位置。如果設置了RegExp對象的Multiline屬性,$也匹配“\n ”或“\r ”之前的位置。 |
* |
匹配前面的子表達式零次或多次。例如,zo*能匹配“z ”、“zo ”以及“zoo ”。*等價於{0,}。 |
+ |
匹配前面的子表達式一次或多次。例如,“zo+ ”能匹配“zo ”以及“zoo ”,但不能匹配“z ”。+等價於{1,}。 |
? |
匹配前面的子表達式零次或一次。例如,“do(es)? ”可以匹配“do ”或“does ”中的“do ”。?等價於{0,1}。 |
{n} |
n是一個非負整數。匹配確定的n次。例如,“o{2} ”不能匹配“Bob ”中的“o ”,但是能匹配“food ”中的兩個o。 |
{n,} |
n是一個非負整數。至少匹配n次。例如,“o{2,} ”不能匹配“Bob ”中的“o ”,但能匹配“foooood ”中的所有o。“o{1,} ”等價於“o+ ”。“o{0,} ”則等價於“o* ”。 |
{n,m} |
m和n均爲非負整數,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3} ”將匹配“fooooood ”中的前三個o。“o{0,1} ”等價於“o? ”。請注意在逗號和兩個數之間不能有空格。 |
? |
非貪心量化(Non-greedy quantifiers):當該字符緊跟在任何一個其他重複修飾符(*,+,?,{n},{n,},{n,m})後面時,匹配模式是非貪婪的。非貪婪模式儘可能少的匹配所搜索的字符串,而默認的貪婪模式則儘可能多的匹配所搜索的字符串。例如,對於字符串“oooo ”,“o+? ”將匹配單個“o ”,而“o+ ”將匹配所有“o ”。 |
. |
匹配除“\r ”“\n ”之外的任何單個字符。要匹配包括“\r ”“\n ”在內的任何字符,請使用像“(.|\r|\n) ”的模式。 |
(pattern) |
匹配pattern並獲取這一匹配的子字符串。該子字符串用於向後引用。所獲取的匹配可以從產生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中則使用$0…$9屬性。要匹配圓括號字符,請使用“\( ”或“\) ”。可帶數量後綴。 |
(?:pattern) |
匹配pattern但不獲取匹配的子字符串(shy groups),也就是說這是一個非獲取匹配,不存儲匹配的子字符串用於向後引用。這在使用或字符“(|) ”來組合一個模式的各個部分是很有用。例如“industr(?:y|ies) ”就是一個比“industry|industries ”更簡略的表達式。 |
(?=pattern) |
正向肯定預查(look ahead positive assert),在任何匹配pattern的字符串開始處匹配查找字符串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如,“Windows(?=95|98|NT|2000) ”能匹配“Windows2000 ”中的“Windows ”,但不能匹配“Windows3.1 ”中的“Windows ”。預查不消耗字符,也就是說,在一個匹配發生後,在最後一次匹配之後立即開始下一次匹配的搜索,而不是從包含預查的字符之後開始。 |
(?!pattern) |
正向否定預查(negative assert),在任何不匹配pattern的字符串開始處匹配查找字符串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如“Windows(?!95|98|NT|2000) ”能匹配“Windows3.1 ”中的“Windows ”,但不能匹配“Windows2000 ”中的“Windows ”。預查不消耗字符,也就是說,在一個匹配發生後,在最後一次匹配之後立即開始下一次匹配的搜索,而不是從包含預查的字符之後開始 |
(?<=pattern) |
反向(look behind)肯定預查,與正向肯定預查類似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows ”能匹配“2000Windows ”中的“Windows ”,但不能匹配“3.1Windows ”中的“Windows ”。 |
(?<!pattern) |
反向否定預查,與正向否定預查類似,只是方向相反。例如“(?<!95|98|NT|2000)Windows ”能匹配“3.1Windows ”中的“Windows ”,但不能匹配“2000Windows ”中的“Windows ”。 |
x|y |
沒有包圍在()裏,其範圍是整個正則表達式。例如,“z|food ”能匹配“z ”或“food ”。“(?:z|f)ood ”則匹配“zood ”或“food ”。 |
[xyz] |
字符集合(character class)。匹配所包含的任意一個字符。例如,“[abc] ”可以匹配“plain ”中的“a ”。特殊字符僅有反斜線\保持特殊含義,用於轉義字符。其它特殊字符如星號、加號、各種括號等均作爲普通字符。脫字符^如果出現在首位則表示負值字符集合;如果出現在字符串中間就僅作爲普通字符。連字符 - 如果出現在字符串中間表示字符範圍描述;如果如果出現在首位(或末尾)則僅作爲普通字符。右方括號應轉義出現,也可以作爲首位字符出現。 |
[^xyz] |
排除型字符集合(negated character classes)。匹配未列出的任意字符。例如,“[^abc] ”可以匹配“plain ”中的“plain ”。 |
[a-z] |
字符範圍。匹配指定範圍內的任意字符。例如,“[a-z] ”可以匹配“a ”到“z ”範圍內的任意小寫字母字符。 |
[^a-z] |
排除型的字符範圍。匹配任何不在指定範圍內的任意字符。例如,“[^a-z] ”可以匹配任何不在“a ”到“z ”範圍內的任意字符。 |
[:name:] |
增加命名字符類(named character class)[注 1]中的字符到表達式。只能用於方括號表達式。 |
[=elt=] |
增加當前locale下排序(collate)等價於字符“elt”的元素。例如,[=a=]可能會增加ä、á、à、ă、ắ、ằ、ẵ、ẳ、â、ấ、ầ、ẫ、ẩ、ǎ、å、ǻ、ä、ǟ、ã、ȧ、ǡ、ą、ā、ả、ȁ、ȃ、ạ、ặ、ậ、ḁ、ⱥ、ᶏ、ɐ、ɑ 。只能用於方括號表達式。 |
[.elt.] |
增加排序元素(collation element)elt到表達式中。這是因爲某些排序元素由多個字符組成。例如,29個字母表的西班牙語, "CH"作爲單個字母排在字母C之後,因此會產生如此排序“cinco, credo, chispa”。只能用於方括號表達式。 |
\b |
匹配一個單詞邊界,也就是指單詞和空格間的位置。例如,“er\b ”可以匹配“never ”中的“er ”,但不能匹配“verb ”中的“er ”。 |
\B |
匹配非單詞邊界。“er\B ”能匹配“verb ”中的“er ”,但不能匹配“never ”中的“er ”。 |
\cx |
匹配由x指明的控制字符。x的值必須爲A-Z 或a-z 之一。否則,將c視爲一個原義的“c ”字符。控制字符的值等於x的值最低5比特(即對3210進制的餘數)。例如,\cM匹配一個Control-M或回車符。\ca等效於\u0001, \cb等效於\u0002, 等等... |
\d |
匹配一個數字字符。等價於[0-9]。注意Unicode正則表達式會匹配全角數字字符。 |
\D |
匹配一個非數字字符。等價於[^0-9]。 |
\f |
匹配一個換頁符。等價於\x0c和\cL。 |
\n |
匹配一個換行符。等價於\x0a和\cJ。 |
\r |
匹配一個回車符。等價於\x0d和\cM。 |
\s |
匹配任何空白字符,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。注意Unicode正則表達式會匹配全角空格符。 |
\S |
匹配任何非空白字符。等價於[^ \f\n\r\t\v]。 |
\t |
匹配一個製表符。等價於\x09和\cI。 |
\v |
匹配一個垂直製表符。等價於\x0b和\cK。 |
\w |
匹配包括下劃線的任何單詞字符。等價於“[A-Za-z0-9_] ”。注意Unicode正則表達式會匹配中文字符。 |
\W |
匹配任何非單詞字符。等價於“[^A-Za-z0-9_] ”。 |
\xnn |
十六進制轉義字符序列。匹配兩個十六進制數字nn表示的字符。例如,“\x41 ”匹配“A ”。“\x041 ”則等價於“\x04&1 ”。正則表達式中可以使用ASCII編碼。. |
\num |
向後引用(back-reference)一個子字符串(substring),該子字符串與正則表達式的第num個用括號圍起來的捕捉羣(capture group)子表達式(subexpression)匹配。其中num是從1開始的十進制正整數,其上限可能是9[注 2]、31[注 3]、99甚至無限[注 4]。例如:“(.)\1 ”匹配兩個連續的相同字符。 |
\n |
標識一個八進制轉義值或一個向後引用。如果\n之前至少n個獲取的子表達式,則n爲向後引用。否則,如果n爲八進制數字(0-7),則n爲一個八進制轉義值。 |
\nm |
3位八進制數字,標識一個八進制轉義值或一個向後引用。如果\nm之前至少有nm個獲得子表達式,則nm爲向後引用。如果\nm之前至少有n個獲取,則n爲一個後跟文字m的向後引用。如果前面的條件都不滿足,若n和m均爲八進制數字(0-7),則\nm將匹配八進制轉義值nm。 |
\nml |
如果n爲八進制數字(0-3),且m和l均爲八進制數字(0-7),則匹配八進制轉義值nml。 |
\un |
Unicode轉義字符序列。其中n是一個用四個十六進制數字表示的Unicode字符。例如,\u00A9匹配版權符號(©)。 |
(來自維基百科)
你能看到這裏
也是
不知道你看懵逼了沒?
反正我是不想看了
接下來
纔是乾貨
小帥b就給你精簡一下
通俗的把最常用的匹配告訴你
字符 | 描述 |
---|---|
\d | 代表任意數字,就是阿拉伯數字 0-9 這些玩意。 |
\D |
大寫的就是和小寫的唱反調,\d 你代表的是任意數字是吧?那麼我 \D 就代表不是數字的。 |
\w |
代表字母,數字,下劃線。也就是 a-z、A-Z、0-9、_。 |
\W |
跟 \w 唱反調,代表不是字母,不是數字,不是下劃線的。 |
\n | 代表一個換行。 |
\r |
代表一個回車。 |
\f |
代表換頁。 |
\t |
代表一個 Tab 。 |
\s |
代表所有的空白字符,也就是上面這個:\n、\r、\t、\f。 |
\S |
跟 \s 唱反調,代表所有不是空白的字符。 |
\A |
代表字符串的開始。 |
\Z |
代表字符串的結束。 |
^ | 匹配字符串開始的位置。 |
$ | 匹配字符創結束的位置。 |
. | 代表所有的單個字符,除了 \n \r |
[...] |
代表在 [] 範圍內的字符,比如 [a-z] 就代表 a到z的字母 |
[^...] |
跟 [...] 唱反調,代表不在 [] 範圍內的字符 |
{n} | 匹配在 {n} 前面的東西,比如: o{2} 不能匹配 Bob 中的 o ,但是能匹配 food 中的兩個o。 |
{n,m} |
匹配在 {n,m} 前面的東西,比如:o{1,3} 將匹配“fooooood”中的前三個o。 |
{n,} |
匹配在 {n,} 前面的東西,比如:o{2,} 不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。 |
* |
和 {0,} 一個樣,匹配 * 前面的 0 次或多次。 比如 zo* 能匹配“z”、“zo”以及“zoo”。 |
+ |
和{1,} 一個樣,匹配 + 前面 1 次或多次。 比如 zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。 |
? |
和{0,1} 一個樣,匹配 ?前面 0 次或 1 次。 |
a|b | 匹配 a 或者 b。 |
() |
匹配括號裏面的內容。 |
ok
知道了這些之後
我們怎麼用 python 來進行判斷呢?
那就要使用到 python 的庫了
它就是
re
接下來我們就來使用 re 模塊
對其常用的方法
來使用正則表達式
re.match
使用這個方法
主要傳入兩個參數
第一個就是我們的匹配規則
第二個就是需要被過濾的內容
例如
我們想要從這
Xiaoshuaib has 100 bananas
拿到一個數字
那麼我們就可以這樣
import re content = 'Xiaoshuaib has 100 bananas' res = re.match('^Xi.*(\d+)\s.*s$',content) print(res.group(1))
通過我們剛剛說的匹配符號
可以定義出相應的匹配規則
在這裏我們將我們需要的目標內容用 () 括起來
此刻我們獲得結果是
0
那麼如果我們想要 100 這個數字呢?
可以這樣
import re content = 'Xiaoshuaib has 100 bananas' res = re.match('^Xi.*?(\d+)\s.*s$',content) print(res.group(1))
看出區別了麼
第二段代碼我們多了一個 ?符號
在這裏呢
涉及到兩個概念
一個是
貪婪匹配
另一個是
非貪婪匹配
所謂貪婪匹配
就是我們的第一段代碼
一個數一個數都要去匹配
而非貪婪呢
我們是直接把 100 給匹配出來了
剛剛我們用到的
.*?
是我們在匹配過程中最常使用到的
表示的就是匹配任意字符
但是
.*?的 . 代表所有的單個字符,除了 \n \r
如果我們的字符串有換行了
怎麼辦呢?
比如這樣
content = """Xiaoshuaib has 100 bananas"""
那麼我們就需要用到 re 的匹配模式了
說來也簡單
直接用 re.S 就可以了
import re content = """Xiaoshuaib has 100 bananas""" res = re.match('^Xi.*?(\d+)\s.*s$',content,re.S) print(res.group(1))
可能有些朋友會覺得
匹配一個東西還要寫開頭結尾
有點麻煩
那麼就可以使用 re 的另一個方法了
re.search
它會直接去掃描字符串
然後把匹配成功的第一個結果的返回給你
import re content = """Xiaoshuaib has 100 bananas""" res = re.search('Xi.*?(\d+)\s.*s',content,re.S) print(res.group(1))
這樣子也是可以獲取 100 的
但是如果我們的內容是這樣的
content = """Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas;"""
想要獲取所有的 100 呢?
這時候就要用到 re 的另一個方法了
re.findall
通過它我們就能輕鬆的獲取所有匹配的內容了
import re content = """Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas;""" res = re.findall('Xi.*?(\d+)\s.*?s;',content,re.S) print(res)
這裏的結果是
['100', '100', '100', '100']
又有朋友覺得
如果我們想直接替換匹配的內容呢
就比如剛剛的字符串
可不可以把 100 直接替換成 250 呢?
那就要用到 re 的另一個方法了
re.sub
可以這樣
import re content = """Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas; Xiaoshuaib has 100 bananas;""" content = re.sub('\d+','250',content) print(content)
那麼結果就變成了
Xiaoshuaib has 250 bananas;
Xiaoshuaib has 250 bananas;
Xiaoshuaib has 250 bananas;
Xiaoshuaib has 250 bananas;
250 個香蕉
吃....得完麼??
再來說說 re 的另一個常用到的方法吧
re.compile
這個主要就是把我們的匹配符封裝一下
import re content = "Xiaoshuaib has 100 bananas" pattern = re.compile('Xi.*?(\d+)\s.*s',re.S) res = re.match(pattern,content) print(res.group(1))
其實和我們之前寫的一樣的
res = re.match('^Xi.*?(\d+)\s.*s$',content,re.S)
只不過 compile 一下
便於以後複用
好了
關於 re 模塊和正則表達式就介紹完啦
知道了怎麼請求數據
也知道了將返回的數據如何正則過濾
那麼
爬蟲對我們來說還難麼?
這次本篇真的完啦
再見
掃一掃
學習 Python 沒煩惱
近期文章
python爬蟲03:那個叫做 Urllib 的庫讓我們的 python 假裝是瀏覽器
python爬蟲04|長江後浪推前浪,Requests 庫把 urllib 庫拍在沙灘上
也不知道爲什麼
你點了好看之後
你變得更好看了