今日内容
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 """
# 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']
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"> / The Shawshank Redemption</span> 26 <span class="other"> / 月黑高飞(港) / 刺激1995(台)</span> 27 </a> 28 29 30 <span class="playable">[可播放]</span> 31 </div> 32 <div class="bd"> 33 <p class=""> 34 导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim Robbins /...<br> 35 1994 / 美国 / 犯罪 剧情 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('=================================')