今天咱们就说一个模块,那就是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模块就说这么多,若是要把正则全部的内容所有讲清楚讲明白, 至少要一周以上的时间,对于咱们平常使用而言,上述知识点已经够用了,若是碰到一些极端状况建议想办法分部处理,先对字符串进行拆分,而后再考虑用正则.