今日内容
1. 正则表达式
- """
- 补充: 爬虫基础 -- 正则:
- 正则单独的一本内容, 推荐书: <<正则指引>>
- """"""
- re 模块与正则之间的关系:
- 正则表达式:
- 他不是 Python 独有的, 它是一门独立的计算机技术, 属于计算机科学的一个概念.
- re 模块:
- 但是如果你想在 Python 中使用, 你就必须依赖于 re 模块
- 他是 Python 中封装好的正则表达式
- 学习计划: 1. 正则表达式 2.re 模块
- """
- # 1. 先学正则
- """
- ### 一. 正则表达式:
- 是对字符串操作的一种逻辑公式,
- 1. 就是用事先定义好的一些特定字符, 及这些特定字符的组合, 组成一个 "规则字符串", 这个 "规则字符串" 用来表达对字符串的一种过滤逻辑.
- 2. 正则表达式是一种文本模式, 该模式描述在搜索文本时要匹配的一个或多个字符串.
- """
- # 需求, 测试手机号是否属于合法字符?
- # # 1. 用 Python 正常代码书写
- # 需求, 测试手机号是否属于合法字符?
- # 1. 用 Python 正常代码书写
- while True:
- # 输入手机号码
- phone_number = input('please input your phone number :')
- # isdigit() 方法检测字符串是否只由数字组成
- # startswith() 方法用于检查字符串是否是以指定子字符串开头, 如果是则返回 True, 否则返回 False.
- # \ 换行
- # 设置手机号位数, 以及以什么开头, 符合即为合法号码
- 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')):
- print('是合法的手机号码')
- else:
- print('不是合法的手机号码')
- """
- # 值:
- please input your phone number : 1
- 不是合法的手机号码
- please input your phone number : 127
- 不是合法的手机号码
- please input your phone number : 157
- 不是合法的手机号码
- please input your phone number : 17891131234
- 是合法的手机号码
- please input your phone number : 12115735464
- 不是合法的手机号码
- """
用 Python 代码
# 2. 正则表达式
- # 需求, 测试手机号是否属于合法字符?
- # 1. 用正则代码书写
- import re
- while True:
- python_num = input('please input your python number>>>:')
- # ^ 匹配以什么把开头,$ 以什么结尾 ^...$ 中间框架固定, | 或者, () 分组, {n} 指重复次数
- # match 只会匹配字符串的开头部分
- if re.match('^(13|14|15|16|17|18)[0-9]{9}$',python_num):
- print('是合法的手机号码')
- else:
- print('不是合法的手机号码')
- """
- # 值
- please input your python number>>>:1111111111
- 不是合法的手机号码
- please input your python number>>>:15735157435
- 是合法的手机号码
- please input your python number>>>:12735447584
- 不是合法的手机号码
- """
正则表达式
"""
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. 字符组
代码:
"""
2. 字符组 [] 概念: 一个字符组针对一个字符
1.[0-9]表示[0,1,2,3,4,5,6,7,8,9]
# 也可以用 - 表示范围,[0-9]就和 [0123456789] 是一个意思
匹配数字规则: 按从小到大
- # A-Z 65 90
- # 中间 91 - 96 6 个字符
- # a-z 97 122
- [a-z]
- [A-z]
[A-Za-z]一个字符组里边的表达式都是 or(或)
2. 正则字符组匹配:
0. 正则 带匹配字符 结果
- # 说明
- 1. 8 True
- # 在一个字符组里枚举合法的所有字符, 字符组里的任意一个字符
和 "待匹配字符" 相同都视为可以匹配
2.[0-9] a False
3.[a-z] 同理
4.[A-Z] 同理
- 5.[0-9a-fA-F] e True
- """
- 字符组
- 2. 元字符
- """
. 匹配除换行以外的任意字符
#
\w 匹配字母或数字或下划线
\s 匹配任意的空白符
\d 匹配数字
#
\n 匹配换行符
\t 匹配制表符 # Tab 制表符
#
\b 匹配一个单词的结尾
^ 匹配字符串的开始
$ 匹配字符串的结尾
#
\W 匹配非字母或数字或下划线
\D 匹配非数字
\S 匹配非空白符
#
a|b 匹配字符 a 或字符 b
() 匹配括号内的表达式
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中字符的所有字符
- """
- 元字符
- 3. 量词:* ?
- # 3. 量词
- """
* 重复零次或者更多次
+ 重复一次或者更多次
? 重复零次或一次
{n} 重复 n 次
{n,} 重复 n 次 或者更多次
{n,m} 重复 n 到 m 次
- """
- 量词
- ps:
- 1. 正则表达式默认都是贪婪匹配
- 2. 通过在量词后面加? 就是非贪婪匹配, 或者称呼惰性匹配
- ps: ## 注意: 前面的 *,+,? 等都是贪婪匹配, 也就是尽可能多匹配, 后面加? 号使其变成惰性匹配
- 正则 待匹配字符 匹配结果 说明
- 李.*? 李杰和李莲英和李二棍子 李 \ 李 \ 李 惰性匹配
- 3. 量词必须根在正则表达式的后边
- 4. 大量正则组合案例 :
- """
- ### 案例 1
正则 带匹配字符 匹配结果 说明
海. 海燕海娇海东 海燕海娇海东 匹配所有 "海." 的字符
^ 海. 海燕海娇海东 海燕 只从开头匹配 "海."
海.$ 海燕海娇海东 海东 只匹配结尾的 "海.$"
### 案例 2
正则 带匹配字符 匹配结果 说明
李.? 李杰和李莲英和李二棍子 李杰 \ 李莲 \ 李二 ? 表示重复零次或一次, 即只匹配 "李" 后面一个任意字符
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子 * 表示重复零次或多次, 即匹配 "李" 后面 0 或多个任意字符
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子 + 表示重复一次或多次, 即只匹配 "李" 后面 1 个或多个任意字符
李.{1,2} 李杰和李莲英和李二棍子 李杰和 \ 李莲英 \ 李二棍 {1,2}匹配 1 到 2 次任意字符
- ## 注意: 前面的 *,+,? 等都是贪婪匹配, 也就是尽可能多匹配, 后面加? 号使其变成惰性匹配
正则 待匹配字符 匹配结果 说明
李.*? 李杰和李莲英和李二棍子 李 \ 李 \ 李 惰性匹配
- ## 案例 3
- ### 字符集[][^]
正则 待匹配字符 匹配结果 说明
李 [杰莲英二棍子]* 李杰和李莲英和李二棍子 李杰 \ 李莲英 \ 李二棍子 表示匹配 "李" 字后面[杰莲英二棍子] 的字符任意次
李[^ 和]* 李杰和李莲英和李二棍子 李杰 \ 李莲英 \ 李二棍子 表示匹配一个不是 "和" 的字符任意次
[\d] 456bdha3 4\5\6\3 表示匹配任意一个数字, 匹配到 4 个结果
[\d]+ 456bdha3 456\3 表示匹配任意个数字, 匹配到 2 个结果
- """
- 正则案例
- 5. 分组
- """
- ## 分组 ()与 或 |[^]
要求: 身份证号码是一个长度为 15 或 18 个字符的字符串, 如果是 15 位则全部由数字组成, 首位不能为 0; 如果是 18 位, 则前 17 位全部是数字, 末位可能是数字或 x, 下面我们尝试用正则来表示:
正则 待匹配字符 匹配结果 说明
^[1-9]\d{13,16}[0-9x]$ 110101198001017032 110101198001017032 表示可以匹配一个正确的身份证号
^[1-9]\d{13,16}[0-9x]$ 1101011980010170 1101011980010170 表示也可以匹配这串数字, 但这并不是一个正确的身份证号码, 它是一个 16 位的数字
^[1-9]\d{14}(\d{2}[0-9x])?$ 1101011980010170 False 现在不会匹配错误的身份证号了 \()表示分组, 将 \ d{2}[0-9x]分成一组, 就可以整体约束他们出现的次数为 0-1 次
^([1-9]\d{16}[0-9x]|[1-9]\d{14})$ 110105199812067023 110105199812067023 表示先匹配 [1-9]\d{16}[0-9x] 如果没有匹配上就匹配[1-9]\d{14}
- """
- 分组
- 6. 转义字符
- """ ## 转义符 3 在正则表达式中, 有很多有特殊意义的是元字符, 比如 \ n 和 \ s 等, 如果要在正则中匹配正常的"\n"而不是" 换行符 "就需要对"\"进行转义, 变成'\\'.
1. 在 python 中, 无论是正则表达式, 还是待匹配的内容, 都是以字符串的形式出现的, 在字符串中 \ 也有特殊的含义, 本身还需要转义. 所以如果匹配一次 "\n", 字符串中要写成'\\n', 那么正则里就要写成 "\\\\n", 这样就太麻烦了.
2. 这个时候我们就用到了 r'\n'这个概念, 此时的正则是 r'\\n'就可以了.
# 案例
正则 待匹配字符 匹配结果 说明
\n \n False 因为在正则表达式中 \ 是有特殊意义的字符, 所以要匹配 \ n 本身, 用表达式 \ n 无法匹配
\\n \n True 转义 \ 之后变成 \\, 即可匹配
"\\\\n" '\\n' True 如果在 python 中, 字符串中的'\'也需要转义, 所以每一个字符串'\'又需要转义一次
r'\\n' r'\n' True 在字符串之前加 r, 让整个字符串不转义
- """
- 转义字符
- """
- # 2. 在学 re 模块
- import re
- """
- findall 找出字符串中符合正则表达式的全部内容
- search 依据正则查一次, 只要有结果, 就不会往后查找, 当查找的结果不存在时, 就直接报错
- match 只会匹配字符串的开头部分
- """
2.re 模块
- # 2. 在学 re 模块
- import re
- """
- findall 找出字符串中符合正则表达式的全部内容
- search 依据正则查一次, 只要有结果, 就不会往后查找, 当查找的结果不存在时, 就直接报错
- match 只会匹配字符串的开头部分
- """
- """
- # 1.findall
- 找出符合正则表达式的全部内容
- # 找出字符串中符合正则表达式全部内容 并且返回的是一个列表, 列表中的元素就是正则匹配到的结果
- # 格式
- findall('正则表达式','带匹配的字符串')
- """ res = re.findall('[a-z]+','eva egon jason') # 自己计算
- print(res) # ['eva', 'egon', 'jason']
- print('=================================')
- """
- # 2.search 不会给你直接返回匹配的结果, 而是给你返回一个对象
- # 必须调用 group 才能看到匹配到的结果
- ps:
- 1.search 只会依据正则查一次, 只要查到了结果, 就不会往后查找
- 2. 当查找结果不存在情况下, 调用 group 直接报错
- # 格式:
- search('正则表达式','带匹配的字符串')
- """ res1 = re.search('a','eva egon jason')
- print(res1) # <_sre.SRE_Match object; span=(2, 3), match='a'>
- # search 不会给你直接返回匹配到的结果 而是给你返回一个对象
- # 必须调用 group 才能看到匹配到的结果
- print(res1.group()) # a
- print('=================================')
- """
- # 3.match
- ps:
- match 只会匹配字符串的开头部分, 如果没有, 直接报错
- 2. 当字符串的开头不符合正则表达式的情况下. 返回的也是 None, 调用 group 也会报错
- 格式:
- match('正则表达式','带匹配的字符串')
- """ res2 = re.match('a','age abc asd')
- print(res2)
- print(res2.group())
- print('=================================')
- """
- # 知识点 1
- # split 分割
- """ ret = re.split('[ab]','abcd') # 先按'a'分割得到''和'bcd', 在对''和'bcd'分别按'b'分割
- print(ret) # ['','', 'cd'] 返回的还是列表
- print('=================================')
- """
- # 知识点 2
- sub 替换
- # 先按照正则表达式查找所有符合该表达式的内容 统一替换成'新的内容' 还可以通过 n 来控制替换的个数
- # sub('正则表达式','新的内容','待替换的字符串',n)
- """ # 将数字替换成'H', 参数 3 表示只替换 3 个
- ret1 = re.sub('\d','H','fdsjalkdfsad15dfasgf445',3)
- print(ret1) # fdsjalkdfsadHHdfasgfH45
- print('=================================')
- # 将数字替换成'H', 返回元组(替换的结果, 替换了多少次)
- # print(ret) # 返回的是一个元组 元组的第二个元素 (5) 代表的是替换的个数
- ret2 = re.subn('\d','H','fdsjalkdfsad15dfasgf445')
- print(ret2) # ('fdsjalkdfsadHHdfasgfHHH', 5)
- print('=================================')
- """
- # 知识点 3
- compile:
- # 将正表达式编译为正则表达式对象
- """
- obj = re.compile('\d{3}') #将正则表达式编译成为一个 正则表达式对象, 规则要匹配的是 3 个数字
- rep1 = obj.search('abc123eeee') #正则表达式对象调用 search, 参数为待匹配的字符串
- print(rep1) # <_sre.SRE_Match object; span=(3, 6), match='123'>
- rep2 = obj.findall('347982734729349827384')
- print(rep2) #结果 : ['347', '982', '734', '729', '349', '827', '384']
- print('=================================')
- """
- # 知识点 4
- ### 迭代器
- #finditer 返回一个存放匹配结果的迭代器
- """ ret3 = re.finditer('\d','ds3sy4784a') #finditer 返回一个存放匹配结果的迭代器
- print(ret3) # <callable_iterator object at 0x10195f940>
- # print(next(ret3).group()) # 3 # 等价于 ret.__next__()
- # print(next(ret3).group()) # 4 # 等价于 ret.__next__()
- # print(next(ret3).group()) # 7 # 等价于 ret.__next__()
- # print(next(ret3).group()) # 8 # 等价于 ret.__next__()
- # print(next(ret3).group()) # 4 # 等价于 ret.__next__()
- # print(next(ret3).group()) # StopIteration # 等价于 ret.__next__() 查出迭代取值的范围 直接报错
- print(next(ret3).group()) # 3 #查看第一个结果
- print(next(ret3).group()) # 4 #查看第二个结果
- print([i.group() for i in ret3]) # ['7', '8', '4'] #查看剩余的左右结果
- print('=================================')
- """
- # 知识点 5
- ### 起别名
- """ res4 = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','110105199812067023')
- # 还可以给某一个正则表达式起别名
- # 格式:?p < 别名>
- res5 = re.search('^[1-9](?P<password>\d{14})(?P<username>\d{2}[0-9x])?$','110105199812067023')
- print(res5.group()) # 110105199812067023
- print(res5.group('password')) # 10105199812067
- print(res5.group(1)) # 10105199812067
- print(res5.group('username')) # 023
- print(res5.group(2)) #023
- print('=================================')
- """
- # 知识点 6
- # 忽略分组优先机制:?:
- """ ret6 = re.findall('www.(baidu|oldboy).com','www.oldboy.com') # 值:['oldboy']
- ret7 = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') # 忽略分组优先的机制 ,
- # ['www.oldboy.com']
- print(ret6,ret7) # ['oldboy'] 这是因为 findall 会优先把匹配结果组里内容返回, 如果想要匹配结果, 取消权限即可
- print('=======================================')
- ret8=re.split("\d+","eva3egon4yuan")
- print(ret8) #结果 : ['eva', 'egon', 'yuan']
- ret9=re.split("(\d+)","eva3egon4yuan")
- print(ret9) #结果 : ['eva', '3', 'egon', '4', 'yuan']
re 模块
3. 拓展
- import re
- import JSON
- from urllib.request import urlopen
- """
- https://movie.douban.com/top250?start=0&filter=
- https://movie.douban.com/top250?start=25&filter=
- https://movie.douban.com/top250?start=50&filter=
- https://movie.douban.com/top250?start=75&filter=
- <li>
- <div class="item">
- <div class="pic">
- <em class="">1</em>
- <a href="https://movie.douban.com/subject/1292052/">
- <img width="100" alt="肖申克的救赎" src="https://img3.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp" class="">
- </a>
- </div>
- <div class="info">
- <div class="hd">
- <a href="https://movie.douban.com/subject/1292052/" class="">
- <span class="title">肖申克的救赎</span>
- <span class="title"> / The Shawshank Redemption</span>
- <span class="other"> / 月黑高飞(港) / 刺激 1995(台)</span>
- </a>
- <span class="playable">[可播放]</span>
- </div>
- <div class="bd">
- <p class="">
- 导演: 弗兰克. 德拉邦特 Frank Darabont 主演: 蒂姆. 罗宾斯 Tim Robbins /...<br>
- 1994 / 美国 & nbsp;/ 犯罪 剧情
- </p>
- <div class="star">
- <span class="rating5-t"></span>
- <span class="rating_num" property="v:average">9.6</span>
- <span property="v:best" content="10.0"></span>
- <span>1489907 人评价</span>
- </div>
- <p class="quote">
- <span class="inq">希望让人自由.</span>
- </p>
- </div>
- </div>
- </div>
- </li>
- """
- 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
拓展 -- 正则在爬虫中的应用
4. 面试题:
1.. 贪婪与非贪婪
"""
贪婪匹配
贪婪匹配: 在满足匹配时, 匹配尽可能长的字符串, 默认情况下, 采用贪婪匹配
## 案例
正则 待匹配字符 匹配结果 说明
<.*> <script>...<script> <script>...<script> 默认为贪婪匹配模式, 会匹配尽量长的字符串
<.*?> r'\d' <script>\<script> 加上? 为将贪婪匹配模式转为非贪婪匹配模式, 会匹配尽量短的字符串
## 几个常用的非贪婪匹配 Pattern
*? 重复任意次, 但尽可能少重复
+? 重复 1 次或更多次, 但尽可能少重复
?? 重复 0 次或 1 次, 但尽可能少重复
{n,m}? 重复 n 到 m 次, 但尽可能少重复
{n,}? 重复 n 次以上, 但尽可能少重复
.*? 的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式.
合在一起就是 取尽量少的任意字符, 一般不会这么单独写, 他大多用在:
.*?x
就是取前面任意长度的字符, 直到一个 x 出现
"""
贪婪与非贪婪
2.search 只去一次 match 从开头取
- import re
- """
- # 2.search 不会给你直接返回匹配的结果, 而是给你返回一个对象
- # 必须调用 group 才能看到匹配到的结果
- ps:
- 1.search 只会依据正则查一次, 只要查到了结果, 就不会往后查找
- 2. 当查找结果不存在情况下, 调用 group 直接报错
- # 格式:
- search('正则表达式','带匹配的字符串')
- """ res1 = re.search('a','eva egon jason')
- print(res1) # <_sre.SRE_Match object; span=(2, 3), match='a'>
- # search 不会给你直接返回匹配到的结果 而是给你返回一个对象
- # 必须调用 group 才能看到匹配到的结果
- print(res1.group()) # a
- print('=================================')
- """
- # 3.match
- ps:
- match 只会匹配字符串的开头部分, 如果没有, 直接报错
- 2. 当字符串的开头不符合正则表达式的情况下. 返回的也是 None, 调用 group 也会报错
- 格式:
- match('正则表达式','带匹配的字符串')
- """ res2 = re.match('a','age abc asd')
- print(res2)
- print(res2.group())
- print('=================================')
search 与 match
来源: http://www.bubuko.com/infodetail-3126617.html