今天咱們就說一個模塊,那就是re,不過想要了解re模塊,咱們得先了解一下什麼是正則表達式,有助於咱們更好的學習re模塊html
一.正則表達式python
首先, 咱們在網頁上進行註冊或者登錄的時候常常能看到一些格式上的錯誤提示,好比:你在註冊百度帳號的時候https://passport.baidu.com/v2/?reg®Type=1&tpl=wk 輸入用戶名隨意的輸入系統會提示你,你的帳號過長或者不容許使用中文等等操做,那這種操做若是使用咱們現有的知識點是能夠完成的,可是完成的效果並很差,寫起來也不容易,尤爲是對郵箱的匹配,電話號碼的匹配,那正則表達式就是專門來處理相似問題的一種表達式,英文全稱: Regular Expression,簡稱 regex或者re. 但你要知道咱們在使用python的re模塊以前,咱們首先要對正則有必定的瞭解和認識,就像咱們使用time模塊以前,咱們已經對時間有了必定的認識.正則表達式
正則表達式是對字符串操做的一種邏輯公式,咱們通常使用正則表達式對字符串進行匹配和過濾,使用正則的優缺點:編輯器
優勢:靈活,功能性強,邏輯性強工具
缺點:上手難,一旦上手,會愛上這個東西學習
工具:各大文本編輯器通常都有正則匹配功能,咱們也能夠去http://tool.chinaz.com/regex/進行在線測試. 測試
正則表達式有普通字符和元字符組成,普通字符包含大小寫字母,數字,在匹配普通字符的時候咱們直接寫就能夠了,好比'abc',咱們若是用python也能夠實現相同的效果,因此普通字符沒什麼好說的,重點在元字符上url
元字符:元字符纔是正則表達式的靈魂,元字符中的內容太多了,在這裏咱們只介紹一些經常使用的.spa
1.字符組code
字符組很簡單,用[]括起來,在[]中出現的內容會被匹配,例如:[abc]匹配a或b或c
若是字符組中的內容過多還可使用-,例如:[a-z]匹配a到z之間的全部字母,[0-9]匹配全部阿拉伯數字
思考:[a-zA-Z0-9]匹配的是什麼?
2.簡單元字符
基本的元字符,這個東西網上一搜一大堆,可是經常使用的就那麼幾個:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
. 匹配除換行符之外的任意字符
\w 匹配字母或數字或下劃線
\s 匹配任意的空白符
\d 匹配數字
\n 匹配一個換行符
\t 匹配一個製表符
\b 匹配一個單詞的結尾
^ 匹配字符串的開始
$ 匹配字符串的結尾
\W 匹配非字母或數字或下劃線
\D 匹配非數字
\S 匹配非空白符
a|b 匹配字符a或字符b
() 匹配括號內的表達式,也表示一個組
[...] 匹配字符組中的字符
[^...] 匹配除了字符組中字符的全部字符
|
3.量詞
咱們到目前匹配的全部內容都是單一文字符號,那如何一次性匹配不少個字符呢,咱們要用到量詞
1
2
3
4
5
6
|
*
重複零次或更屢次
+
重複一次或更屢次
? 重複零次或一次
{n} 重複n次
{n,} 重複n次或更屢次
{n,m} 重複n次到m次
|
4.惰性匹配和貪婪匹配
在量詞中的*,+,{}都屬於貪婪匹配就是儘量多的匹配到結果.
1
2
3
4
|
str
: 麻花藤昨天讓英雄聯盟關服了
reg:麻花藤.
*
此時匹配的是整句話
|
在使用.*後面若是加了?則是儘量的少匹配,表示惰性匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
str
:麻花藤昨天讓英雄聯盟關服了
reg:麻花藤.
*
?
此時匹配的是 麻花藤
str
:<div>胡辣湯<
/
div>
reg:<.
*
>
結果:<div>胡辣湯<
/
dir
>
str
:<div>胡辣湯<
/
div>
reg:<.
*
?>
結果:
<div>
<
/
div>
str
:<div>胡辣湯<
/
div>
reg:<(div|
/
div
*
)?>
結果:
<div>
<
/
div>
|
.*?X的特殊含義 找到下一個x爲止
1
2
3
|
str
: abcdefgxhijklmn
reg: .
*
?x
結果:abcdefgx
|
5.分組
在正則中使用()進行分組,好比咱們要匹配一個相對複雜的身份證號,身份證號分紅兩種,老的身份證號有15位,新的身份證號有18位,而且新的身份證號結尾有多是x.
1
2
3
4
|
給出如下正則:
^[
1
-
9
]\d{
13
,
16
}[
0
-
9x
]$
^[
1
-
9
]\d{
14
}(\d{
2
}[
0
-
9x
])?$
^([
1
-
9
]\d{
16
}[
0
-
9x
]|[
1
-
9
]\d{
14
})$
|
6.轉義
在正則表達式中,有不少有特殊意義的是元字符, 好比\n和\s等,若是要在正則中匹配正常的"\n"而不是"換行符"就須要對"\"進行轉義, 變成'\\'.在python中, 不管是正則表達式, 仍是待匹配的內容, 都是以字符串的形式出現的, 在字符串中\也有特殊的含義, 自己還須要轉義,因此若是匹配一次"\n", 字符串中要寫成'\\n', 那麼正則裏就要寫成"\\\\n",這樣就太麻煩了,這個時候咱們就用到了r'\n'這個概念, 此時的正則是r'\\n'就能夠了.
練習:
1.匹配郵箱
2.匹配手機號
3.匹配生日.日期格式(xxxx-mm-dd)
二.re模塊
re模塊是python提供的一套關於處理正則表達式的模塊,核心功能有四個:
1.findall 查找全部,返回list
1
2
3
4
5
|
lst
=
re.findall(
"m"
,
"mai le fo len, mai ni mei!"
)
print
(lst)
# ['m', 'm', 'm']
lst
=
re.findall(r
"\d+"
,
"5點以前,你要給我5000萬"
)
print
(lst)
# ['5', '5000']
|
2.finditer 和findall差很少. 只不過這時返回的是迭代器
1
2
3
4
|
it
=
re.finditer(
"m"
,
"mai le fo len, mai ni mei!"
)
for
el
in
it:
print
(el.group())
# 依然須要分組
|
3. search 會進行匹配,可是若是匹配到了第一個結果,就會返回這個結果,若是匹配不上search返回的則是None
1
2
|
ret
=
re.search(r
'\d'
,
'5點以前. 你要給我5000萬'
).group()
print
(ret)
# 5
|
4.match 只能從字符串的開頭進行匹配
1
2
|
ret
=
re.match(
'a'
,
'abc'
).group()
print
(ret)
# a
|
5.其餘操做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
ret
=
re.split(
'[ab]'
,
'qwerafjbcd'
)
# 先按'a'分割獲得'qwer'和'fjbcd',在
對
'qwer'
和
'fjbcd'
分別按
'b'
分割
print
(ret)
# ['qwer', 'fj', 'cd']
ret
=
re.sub(r
"\d+"
,
"_sb_"
,
"alex250taibai250wusir250ritian38"
)
# 把字符串中
的數字換成__sb__
print
(ret)
# alex_sb_taibai_sb_wusir_sb_ritian_sb_
ret
=
re.subn(r
"\d+"
,
"_sb_"
,
"alex250taibai250wusir250ritian38"
)
# 將數字替
換成
'__sb__'
,返回元組(替換的結果,替換了多少次)
print
(ret)
# ('alex_sb_taibai_sb_wusir_sb_ritian_sb_', 4)
obj
=
re.
compile
(r
'\d{3}'
)
# 將正則表達式編譯成爲一個正則表達式對象, 規則要匹配的
是
3
個數字
ret
=
obj.search(
'abc123eeee'
)
# 正則表達式對象調用search, 參數爲待匹配的字符串
print
(ret.group())
# 結果: 123
爬蟲重點:
obj
=
re.
compile
(r
'(?P<id>\d+)(?P<name>e+)'
)
# 從正則表達式匹配的內容每一個組起名字
ret
=
obj.search(
'abc123eeee'
)
# 搜索
print
(ret.group())
# 結果: 123eeee
print
(ret.group(
"id"
))
# 結果: 123 # 獲取id組的內容
print
(ret.group(
"name"
))
# 結果: eeee # 獲取name組的內容
|
6.兩個坑
注意: 在re模塊中和咱們在線測試工具中的結果多是不同的.
1
2
3
4
5
6
|
ret
=
re.findall(
'www.(baidu|oldboy).com'
,
'www.oldboy.com'
)
print
(ret)
# ['oldboy'] 這是由於findall會優先把匹配結果組裏內容返回,若是想要匹
配結果,取消權限便可
ret
=
re.findall(
'www.(?:baidu|oldboy).com'
,
'www.oldboy.com'
)
print
(ret)
# ['www.oldboy.com']
|
split裏也有一個坑
1
2
3
4
5
6
7
8
9
|
ret
=
re.split(
"\d+"
,
"eva3egon4yuan"
)
print
(ret)
#結果 : ['eva', 'egon', 'yuan']
ret
=
re.split(
"(\d+)"
,
"eva3egon4yuan"
)
print
(ret)
#結果 : ['eva', '3', 'egon', '4', 'yuan']
#在匹配部分加上()以後所切出的結果是不一樣的,
#沒有()的沒有保留所匹配的項,可是有()的卻可以保留了匹配的項,
#這個在某些須要保留匹配部分的使用過程是很是重要的。
|
這種優先級的問題有時候會幫咱們完成不少功能,咱們來看一個比較複雜的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import
re
from
urllib.request
import
urlopen
import
ssl
# 幹掉數字簽名證書
ssl._create_default_https_context
=
ssl._create_unverified_context
def
getPage(url):
response
=
urlopen(url)
return
response.read().decode(
'utf-8'
)
def
parsePage(s):
ret
=
re.findall(
'<div
class
=
"item"
>.
*
?<div
class
=
"pic"
>.
*
?<em .
*
?>(?P<
id
>\d
+
).
*
?
<span
class
=
"title"
>(?P<title>.
*
?)<
/
span>'
'.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)評價</span>'
, s, re.S)
return
ret
def
main(num):
url
=
'https://movie.douban.com/top250?start=%s&filter='
%
num
response_html
=
getPage(url)
ret
=
parsePage(response_html)
print
(ret)
count
=
0
for
i
in
range
(
10
):
# 10頁
main(count)
count
+
=
25
|
此時利用的就是分組以後,匹配成功後獲取到的是分組後的結果,(?P<id>\d+) 此時當前組所匹配的數據就會被分組到id組內,此時程序能夠改寫成:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
import
ssl
import
re
from
urllib.request
import
urlopen
# 幹掉數字簽名證書
ssl._create_default_https_context
=
ssl._create_unverified_context
def
getPage(url):
response
=
urlopen(url)
return
response.read().decode(
'utf-8'
)
def
parsePage(s):
com
=
re.
compile
(
'<div
class
=
"item"
>.
*
?<div
class
=
"pic"
>.
*
?<em .
*
?>(?P<
id
>\d
+
).
*
?
<span
class
=
"title"
>(?P<title>.
*
?)<
/
span>'
'.
*
?<span
class
=
"rating_num"
.
*
?>(?P<rating_num>.
*
?)<
/
span>.
*
?
<span>(?P<comment_num>.
*
?)評價<
/
span>', re.S)
ret
=
com.finditer(s)
for
i
in
ret:
yield
{
"id"
: i.group(
"id"
),
"title"
: i.group(
"title"
),
"rating_num"
: i.group(
"rating_num"
),
"comment_num"
: i.group(
"comment_num"
),
}
def
main(num):
url
=
'https://movie.douban.com/top250?start=%s&filter='
%
num
response_html
=
getPage(url)
ret
=
parsePage(response_html)
# print(ret)
f
=
open
(
"move_info7"
,
"a"
, encoding
=
"utf8)
for
obj
in
ret:
print
(obj)
data
=
str
(obj)
f.write(data
+
"\n"
)
count
=
0
for
i
in
range
(
10
):
main(count)
count
+
=
25
|
正則表達式和re模塊就說這麼多,若是要把正則全部的內容所有講清楚講明白, 至少要一週以上的時間,對於咱們平常使用而言,上述知識點已經夠用了,若是碰到一些極端狀況建議想辦法分部處理,先對字符串進行拆分,而後再考慮用正則.