# 正则表达式 -- re模块 爬虫基础 # 15

今日内容

1.正则表达式

"""
补充:爬虫基础 -- 正则:
正则单独的一本内容,推荐书: <<正则指引>>
"""
"""
re模块与正则之间的关系:
正则表达式:
他不是Python独有的,它是一门独立的计算机技术,属于计算机科学的一个概念。
re模块:
但是如果你想在Python中使用,你就必须依赖于re模块
他是Python中封装好的正则表达式

学习计划:1.正则表达式 2.re模块
"""
# 1.先学正则
"""
### 一.正则表达式:
是对字符串操作的一种逻辑公式,
1.就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
2.正则表达式是一种文本模式,该模式描述在搜索文本时要匹配的一个或多个字符串。

"""
# 需求,测试手机号是否属于合法字符?
# # 1.用Python正常代码书写
 1 # 需求,测试手机号是否属于合法字符?
 2 # 1.用Python正常代码书写
 3 while True:
 4     # 输入手机号码
 5     phone_number = input('please input your phone number : ')
 6     # isdigit() 方法检测字符串是否只由数字组成
 7     # startswith() 方法用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False。
 8     # \ 换行
 9     # 设置手机号位数,以及以什么开头,符合即为合法号码
10     if len(phone_number) == 11 \
11             and phone_number.isdigit()\
12             and (phone_number.startswith('13') \
13             or phone_number.startswith('14') \
14             or phone_number.startswith('15') \
15             or phone_number.startswith('16') \
16             or phone_number.startswith('17') \
17             or phone_number.startswith('18')):
18         print('是合法的手机号码')
19     else:
20         print('不是合法的手机号码')
21 
22 """
23 # 值:
24 please input your phone number : 1
25 不是合法的手机号码
26 please input your phone number : 127
27 不是合法的手机号码
28 please input your phone number : 157
29 不是合法的手机号码
30 please input your phone number : 17891131234
31 是合法的手机号码
32 please input your phone number : 12115735464
33 不是合法的手机号码
34 
35 """
用Python代码
# 2.正则表达式
 1 # 需求,测试手机号是否属于合法字符?
 2 # 1.用正则代码书写
 3 import re
 4 while True:
 5     python_num = input('please input your python number>>>:')
 6     # ^ 匹配以什么把开头,$ 以什么结尾 ^...$ 中间框架固定,  | 或者, () 分组, {n} 指重复次数
 7     # match 只会匹配字符串的开头部分
 8     if re.match('^(13|14|15|16|17|18)[0-9]{9}$',python_num):
 9         print('是合法的手机号码')
10     else:
11             print('不是合法的手机号码')
12 """
13 # 值
14 please input your python number>>>:1111111111
15 不是合法的手机号码
16 please input your python number>>>:15735157435
17 是合法的手机号码
18 please input your python number>>>:12735447584
19 不是合法的手机号码
20 """
正则表达式
"""
1.正则的作用:
正则就是用来筛选字符串特定内容的
2.正则的应用场景:(同样也是两个就业方向)
1.爬虫
2.数据分析
3.应用最多的四个符号:
1.\d 匹配数字
2.\w 匹配字母或者数字或下划线
3..* 匹配任意字符
.匹配除换行符以外的任意字符
*量词:重复零次或者更多次
4..*? 由贪婪变为非贪婪,取最小,重复0次,即任意字符都不匹配,或者都匹配空格
?量词:重复零次或者一次
5.在线验证正则表达式网址:
正则表达式在线测试
https://tool.chinaz.com/regex
网址只要是reg...结尾通常都是和正则有关系的

6.匹配方式:
1.想匹配什么内容可以直接写内容,不需要正则
2.字符组[]概念:一个字符组针对一个字
^ 与 $ 连用 匹配字符串就必须是什么
a|b 匹配a或者b
abc|ab 一定要将长的放在前面,防止有的数据匹配不到
^ 写在外边,限制开头
$ 限制字符串架结尾
[^a] 取反 除了a以外
1.字符组


代码:
 1 """
 2 2.字符组[]概念:一个字符组针对一个字符
 3     1.[0-9]表示[0,1,2,3,4,5,6,7,8,9]
 4     # 也可以用-表示范围,[0-9]就和[0123456789]是一个意思
 5         匹配数字规则:按从小到大
 6         # A-Z  65 90
 7         # 中间 91 - 96 6个字符
 8         # a-z  97 122
 9         [a-z]
10         [A-z]
11         [A-Za-z]一个字符组里边的表达式都是or(或)
12         2.正则字符组匹配:
13         0.正则     带匹配字符    结果
14         # 说明
15         1.  8  True
16         # 在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
17 和"待匹配字符"相同都视为可以匹配
18         2.[0-9]  a  False
19         3.[a-z]  同理
20         4.[A-Z]  同理
21         5.[0-9a-fA-F]  e  True
22 """
字符组

2.元字符

 1 """
 2 .   匹配除换行以外的任意字符
 3 #
 4 \w  匹配字母或数字或下划线
 5 \s  匹配任意的空白符
 6 \d  匹配数字
 7 #
 8 \n  匹配换行符
 9 \t  匹配制表符   # Tab  制表符
10 #
11 \b  匹配一个单词的结尾
12 ^   匹配字符串的开始
13 $   匹配字符串的结尾
14 #
15 \W  匹配非字母或数字或下划线
16 \D  匹配非数字
17 \S  匹配非空白符
18 #
19 a|b    匹配字符a或字符b
20 ()     匹配括号内的表达式
21 [...]  匹配字符组中的字符
22 [^...] 匹配除了字符组中字符的所有字符
23 
24 """
元字符


3.量词:* ?
 1 # 3.量词
 2 """
 3 *     重复零次或者更多次
 4 +     重复一次或者更多次
 5 ?     重复零次或一次
 6 {n}   重复n次
 7 {n,}  重复n次 或者更多次
 8 {n,m} 重复n到m次
 9 
10 """
量词


ps:
1.正则表达式默认都是贪婪匹配
2.通过在量词后面加?就是非贪婪匹配,或者称呼惰性匹配
ps: ## 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能多匹配,后面加?号使其变成惰性匹配
正则 待匹配字符 匹配结果 说明
李.*? 李杰和李莲英和李二棍子 李\李\李 惰性匹配
3.量词必须根在正则表达式的后边
4.大量正则组合案例 :
 1 """
 2 ###案例1
 3 正则    带匹配字符      匹配结果          说明
 4 海.        海燕海娇海东    海燕海娇海东      匹配所有"海."的字符
 5 ^海.    海燕海娇海东    海燕              只从开头匹配"海."
 6 海.$    海燕海娇海东    海东              只匹配结尾的"海.$"
 7 
 8 ###案例2
 9 正则       带匹配字符                       匹配结果                说明
10 李.?       李杰和李莲英和李二棍子            李杰 \李莲\ 李二        ?表示重复零次或一次,即只匹配"李"后面一个任意字符
11 李.*       李杰和李莲英和李二棍子            李杰和李莲英和李二棍子    *表示重复零次或多次,即匹配"李"后面0或多个任意字符
12 李.+       李杰和李莲英和李二棍子            李杰和李莲英和李二棍子    +表示重复一次或多次,即只匹配"李"后面1个或多个任意字符
13 李.{1,2}   李杰和李莲英和李二棍子            李杰和\李莲英\李二棍    {1,2}匹配1到2次任意字符
14 
15 
16  ## 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能多匹配,后面加?号使其变成惰性匹配
17 
18 正则    待匹配字符                匹配结果    说明
19 李.*?    李杰和李莲英和李二棍子    李\李\李    惰性匹配
20 
21 ## 案例3
22 ###字符集[][^]
23 正则                待匹配字符                         匹配结果                          说明
24 李[杰莲英二棍子]*    李杰和李莲英和李二棍子             李杰\李莲英\李二棍子             表示匹配"李"字后面[杰莲英二棍子]的字符任意次
25  李[^和]*            李杰和李莲英和李二棍子             李杰\李莲英\李二棍子             表示匹配一个不是"和"的字符任意次
26 [\d]                456bdha3                         4\5\6\3                          表示匹配任意一个数字,匹配到4个结果
27 [\d]+                 456bdha3                        456\3                             表示匹配任意个数字,匹配到2个结果
28 
29 
30 """
正则案例
    5.分组
 1 """
 2 ##分组 ()与 或 |[^]
 3  要求:身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部由数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:
 4 
 5 正则                          待匹配字符                   匹配结果                 说明
 6 ^[1-9]\d{13,16}[0-9x]$          110101198001017032           110101198001017032    表示可以匹配一个正确的身份证号
 7 ^[1-9]\d{13,16}[0-9x]$          1101011980010170             1101011980010170      表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字
 8 ^[1-9]\d{14}(\d{2}[0-9x])?$      1101011980010170             False                  现在不会匹配错误的身份证号了  \()表示分组,将\d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次
 9 ^([1-9]\d{16}[0-9x]|[1-9]\d{14})$    110105199812067023     110105199812067023     表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}
10 """
分组
    6.转义字符
 1 """
 2 ##转义符 \
 3 在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对"\"进行转义,变成'\\'。
 4 
 5     1.在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。
 6     2.这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了。
 7 # 案例
 8 正则    待匹配字符    匹配结果    说明
 9 \n        \n            False       因为在正则表达式中\是有特殊意义的字符,所以要匹配\n本身,用表达式\n无法匹配
10 \\n        \n            True        转义\之后变成\\,即可匹配
11 "\\\\n"    '\\n'        True        如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次
12 r'\\n'    r'\n'        True        在字符串之前加r,让整个字符串不转义
13 
14 """
转义字符
            
"""

# 2.在学re模块
import re
"""
findall 找出字符串中符合正则表达式的全部内容
search 依据正则查一次,只要有结果,就不会往后查找,当查找的结果不存在时,就直接报错
match 只会匹配字符串的开头部分
"""

2.re模块

  1 # 2.在学re模块
  2 import re
  3 """
  4 findall  找出字符串中符合正则表达式的全部内容
  5 search   依据正则查一次,只要有结果,就不会往后查找,当查找的结果不存在时,就直接报错
  6 match    只会匹配字符串的开头部分
  7 """
  8 
  9 """
 10 
 11 # 1.findall
 12 找出符合正则表达式的全部内容
 13 # 找出字符串中符合正则表达式全部内容 并且返回的是一个列表,列表中的元素就是正则匹配到的结果
 14     # 格式
 15     findall('正则表达式','带匹配的字符串')
 16 """
 17 res = re.findall('[a-z]+','eva egon jason')   # 自己计算
 18 print(res)   # ['eva', 'egon', 'jason']
 19 print('=================================')
 20 """
 21 # 2.search 不会给你直接返回匹配的结果,而是给你返回一个对象
 22     # 必须调用group才能看到匹配到的结果
 23     ps:
 24         1.search 只会依据正则查一次,只要查到了结果,就不会往后查找
 25         2.当查找结果不存在情况下,调用group直接报错
 26     # 格式:
 27 search('正则表达式','带匹配的字符串')
 28 """
 29 res1 = re.search('a','eva egon jason')
 30 print(res1)   # <_sre.SRE_Match object; span=(2, 3), match='a'>
 31 # search不会给你直接返回匹配到的结果 而是给你返回一个对象
 32 # 必须调用group才能看到匹配到的结果
 33 print(res1.group())  # a
 34 print('=================================')
 35 """
 36 # 3.match
 37 ps:
 38 match 只会匹配字符串的开头部分,如果没有,直接报错
 39 2.当字符串的开头不符合正则表达式的情况下.返回的也是None,调用group也会报错
 40 格式:
 41 match('正则表达式','带匹配的字符串')
 42 """
 43 res2 = re.match('a','age abc asd')
 44 print(res2)
 45 print(res2.group())
 46 print('=================================')
 47 """
 48 # 知识点1
 49 # split 分割
 50 """
 51 ret = re.split('[ab]', 'abcd')  # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
 52 print(ret)  # ['', '', 'cd'] 返回的还是列表
 53 
 54 print('=================================')
 55 """
 56 # 知识点2
 57 sub 替换
 58 # 先按照正则表达式查找所有符合该表达式的内容 统一替换成'新的内容'  还可以通过n来控制替换的个数
 59 # sub('正则表达式','新的内容','待替换的字符串',n)
 60 """
 61 # 将数字替换成'H',参数3表示只替换3个
 62 ret1 = re.sub('\d','H','fdsjalkdfsad15dfasgf445',3)
 63 print(ret1)  # fdsjalkdfsadHHdfasgfH45
 64 print('=================================')
 65 
 66 
 67 # 将数字替换成'H',返回元组(替换的结果,替换了多少次)
 68 # print(ret)  # 返回的是一个元组 元组的第二个元素(5)代表的是替换的个数
 69 ret2 = re.subn('\d','H','fdsjalkdfsad15dfasgf445')
 70 print(ret2)  # ('fdsjalkdfsadHHdfasgfHHH', 5)
 71 print('=================================')
 72 
 73 """
 74 # 知识点3
 75 compile: 
 76 # 将正表达式编译为正则表达式对象
 77 """
 78 
 79 obj = re.compile('\d{3}')  #将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
 80 rep1 = obj.search('abc123eeee') #正则表达式对象调用search,参数为待匹配的字符串
 81 print(rep1)  # <_sre.SRE_Match object; span=(3, 6), match='123'>
 82 rep2 = obj.findall('347982734729349827384')
 83 print(rep2)  #结果 : ['347', '982', '734', '729', '349', '827', '384']
 84 print('=================================')
 85 
 86 """
 87 # 知识点4
 88 ###迭代器
 89 #finditer返回一个存放匹配结果的迭代器
 90 """
 91 ret3 = re.finditer('\d', 'ds3sy4784a')   #finditer返回一个存放匹配结果的迭代器
 92 print(ret3)  # <callable_iterator object at 0x10195f940>
 93 # print(next(ret3).group())  # 3  # 等价于ret.__next__()
 94 # print(next(ret3).group())   # 4 # 等价于ret.__next__()
 95 # print(next(ret3).group())  # 7 # 等价于ret.__next__()
 96 # print(next(ret3).group())  # 8 # 等价于ret.__next__()
 97 # print(next(ret3).group())  # 4 # 等价于ret.__next__()
 98 # print(next(ret3).group())  # StopIteration # 等价于ret.__next__()   查出迭代取值的范围 直接报错
 99 print(next(ret3).group())   # 3 #查看第一个结果
100 print(next(ret3).group())  # 4  #查看第二个结果
101 print([i.group() for i in ret3])  # ['7', '8', '4'] #查看剩余的左右结果
102 print('=================================')
103 """
104 # 知识点5
105 ###起别名
106 """
107 res4 = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','110105199812067023')
108 # 还可以给某一个正则表达式起别名
109 # 格式:?p<别名>
110 res5 = re.search('^[1-9](?P<password>\d{14})(?P<username>\d{2}[0-9x])?$','110105199812067023')
111 print(res5.group())  # 110105199812067023
112 print(res5.group('password'))  # 10105199812067
113 print(res5.group(1))  # 10105199812067
114 print(res5.group('username'))  # 023
115 print(res5.group(2))  #023
116 print('=================================')
117 """
118 # 知识点6
119 # 忽略分组优先机制:?:
120 """
121 ret6 = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')  # 值:['oldboy']
122 ret7 = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')  # 忽略分组优先的机制 ,
123 # ['www.oldboy.com']
124 print(ret6,ret7)  # ['oldboy']   这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
125 
126 print('=======================================')
127 
128 ret8=re.split("\d+","eva3egon4yuan")
129 print(ret8) #结果 : ['eva', 'egon', 'yuan']
130 
131 ret9=re.split("(\d+)","eva3egon4yuan")
132 print(ret9) #结果 : ['eva', '3', 'egon', '4', 'yuan']
re模块

3.拓展

 1 import re
 2 import json
 3 from urllib.request import urlopen
 4 
 5 
 6 """
 7 https://movie.douban.com/top250?start=0&filter=
 8 https://movie.douban.com/top250?start=25&filter=
 9 https://movie.douban.com/top250?start=50&filter=
10 https://movie.douban.com/top250?start=75&filter=
11 
12 
13 <li>
14             <div class="item">
15                 <div class="pic">
16                     <em class="">1</em>
17                     <a href="https://movie.douban.com/subject/1292052/">
18                         <img width="100" alt="肖申克的救赎" src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp" class="">
19                     </a>
20                 </div>
21                 <div class="info">
22                     <div class="hd">
23                         <a href="https://movie.douban.com/subject/1292052/" class="">
24                             <span class="title">肖申克的救赎</span>
25                                     <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span>
26                                 <span class="other">&nbsp;/&nbsp;月黑高飞(港)  /  刺激1995(台)</span>
27                         </a>
28 
29 
30                             <span class="playable">[可播放]</span>
31                     </div>
32                     <div class="bd">
33                         <p class="">
34                             导演: 弗兰克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 Tim Robbins /...<br>
35                             1994&nbsp;/&nbsp;美国&nbsp;/&nbsp;犯罪 剧情
36                         </p>            
37                         <div class="star">
38                                 <span class="rating5-t"></span>
39                                 <span class="rating_num" property="v:average">9.6</span>
40                                 <span property="v:best" content="10.0"></span>
41                                 <span>1489907人评价</span>
42                         </div>
43 
44                             <p class="quote">
45                                 <span class="inq">希望让人自由。</span>
46                             </p>
47                     </div>
48                 </div>
49             </div>
50         </li>
51 """
52 
53 
54 def getPage(url):
55     response = urlopen(url)
56     return response.read().decode('utf-8')
57 
58 def parsePage(s):
59     com = re.compile(
60         '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
61         '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S)
62 
63     ret = com.finditer(s)
64     for i in ret:
65         yield {
66             "id": i.group("id"),
67             "title": i.group("title"),
68             "rating_num": i.group("rating_num"),
69             "comment_num": i.group("comment_num"),
70         }
71 
72 
73 def main(num):
74     url = 'https://movie.douban.com/top250?start=%s&filter=' % num
75     response_html = getPage(url)
76     ret = parsePage(response_html)
77     print(ret)
78     f = open("move_info7", "a", encoding="utf8")
79 
80     for obj in ret:
81         print(obj)
82         data = str(obj)
83         f.write(data + "\n")
84 
85 count = 0
86 for i in range(10):
87     main(count)
88     count += 25
拓展--正则在爬虫中的应用

4.面试题:

    1..贪婪与非贪婪

 1 """
 2 贪婪匹配
 3 贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
 4 
 5 ##案例
 6 正则    待匹配字符                匹配结果                说明
 7 <.*>    <script>...<script>     <script>...<script>     默认为贪婪匹配模式,会匹配尽量长的字符串
 8 <.*?>    r'\d'                   <script>\<script>       加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串
 9 
10 ## 几个常用的非贪婪匹配Pattern
11 *? 重复任意次,但尽可能少重复
12 +? 重复1次或更多次,但尽可能少重复
13 ?? 重复0次或1次,但尽可能少重复
14 {n,m}? 重复n到m次,但尽可能少重复
15 {n,}? 重复n次以上,但尽可能少重复
16 .*?的用法
17     . 是任意字符
18     * 是取 0 至 无限长度
19     ? 是非贪婪模式。
20     合在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
21 
22     .*?x
23     就是取前面任意长度的字符,直到一个x出现
24 """
贪婪与非贪婪

    2.search 只去一次  match 从开头取

 1 import re
 2 """
 3 # 2.search 不会给你直接返回匹配的结果,而是给你返回一个对象
 4     # 必须调用group才能看到匹配到的结果
 5     ps:
 6         1.search 只会依据正则查一次,只要查到了结果,就不会往后查找
 7         2.当查找结果不存在情况下,调用group直接报错
 8     # 格式:
 9 search('正则表达式','带匹配的字符串')
10 """
11 res1 = re.search('a','eva egon jason')
12 print(res1)   # <_sre.SRE_Match object; span=(2, 3), match='a'>
13 # search不会给你直接返回匹配到的结果 而是给你返回一个对象
14 # 必须调用group才能看到匹配到的结果
15 print(res1.group())  # a
16 print('=================================')
17 """
18 # 3.match
19 ps:
20 match 只会匹配字符串的开头部分,如果没有,直接报错
21 2.当字符串的开头不符合正则表达式的情况下.返回的也是None,调用group也会报错
22 格式:
23 match('正则表达式','带匹配的字符串')
24 """
25 res2 = re.match('a','age abc asd')
26 print(res2)
27 print(res2.group())
28 print('=================================')
search与match

猜你喜欢

转载自www.cnblogs.com/llx--20190411/p/11204302.html