Python基础学习(18)正则表达式
一、今日大纲
- 正则表达式
- re 模块
二、正则表达式
-
模块和实际工作时间的关系
-
time 模块和时间的关系
time 模块让我们更方便的完成和时间相关的操作,但是事件本身和 time 模块无关。
-
re 模块和正则表达式的关系
有了 re 模块就可以在 Python 语言中操作正则表达式了。
-
-
正则表达式的定义
正则表达式 Regular Expression 是一套用于匹配字符串的规则,它具有:
-
检测一个输入的字符串是否合法——web 开发项目、表单验证
-
用户输入一个内容的时候,我们提前做检测。
-
能够提高程序的效率并且减轻服务器的压力。
-
-
从一个大文件中找到所有符合规则的内容——日志分析、爬虫
- 能够高效地从一大段文字中快速找到符合规则的内容。
-
-
字符组 Character Class
描述的是一个位置上能出现的所有的可能性。
- 一个中括号只表示一个字符位置,如
[abc]
表示的是匹配单个的a
、b
或者c
字符。 - 可以根据ASCII码进行范围的比对,如
[0-9]
、[a-z]
等,也可以用[a-zA-Z]
等形式表示。 - 匹配两位数字可以利用
[1-9][0-9]
的形式,以此类推。
- 一个中括号只表示一个字符位置,如
-
元字符 Meta Character 和量词
Meta Character/ 量词 功能 [...] 匹配一位字符,以括号内容为准 [^...] 匹配一位“非”字符 d 匹配一位任意数字 w 匹配一位数字字母下划线 匹配一位 table 匹配一位换行符 s 匹配一位空白字符 D 匹配一位非数字 w 匹配一位非数字字母下划线 S 匹配一位非空白字符 | 或(把长的放在左边优先匹配) 转义字符 () 分组,约束描述的内容的范围 匹配后面是空白的字符 {n} 匹配n次 {n,} 匹配至少n次 {n,m} 匹配至少n次,至多m次 ? 匹配〇次或一次 + 匹配一次或多次 * 匹配〇次或多次 -
贪婪匹配和惰性匹配
- 贪婪匹配:在量词范围允许的情况下,尽可能地多匹配内容。
- 惰性匹配(非贪婪匹配):在量词范围允许的情况下,匹配到的内容相对最少,一般在量词后加
?
实现(元字符不可直接加?
)。
-
转义字符的使用
原本有特殊意义的字符,到了表达它本身的意义的时候,需要在前面加上转义字符;有一些有特殊意义的内容,放在字符组中,会取消它的特殊意义(
-
是特殊情况,可在[]
内表示范围)。# 一些小栗子 # 匹配任意长度的常数 d+ # 匹配小数:d+.d+ # 小数或整数: d+(.?d+)? # 手机号的表示 # ^1[3-9]%d{9}$ # 18/15位的身份证号码 # 以1-9开头,18位末尾有可能是X # ^[1-9]d{14}(d{2}[0-9x])?$
三、re 模块
主要介绍 re 模块的两个方法 findall()
和 search()
。
-
findall()
和search()
的基本用法findall()
会在目标中寻找到所有符合正则表达式的字符串,以列表形式返回;search()
会在目标中将第一个匹配到的内容以对象形式返回可以通过group()
方法返回匹配字符串,如果没有匹配字符串使用该方法会报错。ret = re.findall('d+', '19532asdas123812838adasda') # 找到全部 print(ret) ret = re.search('d+', '19532asdas123812838adasda') # 只找一个 if ret: print(ret.group()) # 找不到会报错
-
分组
如果在
findall()
和search()
的正则表达式参数中添加小括号分组,findall()
会按照正则表达式进行匹配,但是只会将括号所匹配内容加入列表,如果有多个括号,会将每组括号匹配内容放置于元组之中再依次加入列表;而search()
也会按照正则表达式进行匹配,且将第一个匹配到的内容以对象形式返回,但是我们可以通过在group(n)
方法中传入索引参数实现对多个括号分组的读取。# re.findall()还是按照完整的正则进行匹配,但是只显示括号里匹配到的内容,但是不能多个括号 ret = re.findall('9(d)d', '19532asdas123812838adasda') print(ret) # ['5'] # re.search()还是按照完整的正则进行匹配,显示也显示匹配到的第一个内容,我们可以通过给group方法传参数 ret = re.search('(9)(d)(d)', '19532asdas123812838adasda') print(ret) # <_sre.SRE_Match object; span=(1, 4), match='953'> if ret: print(ret.group(0)) # 953 print(ret.group(1)) # 9 print(ret.group(2)) # 5
-
如何取消分组优先级
在一些特殊场景中,如果因为正则表达式语法的原因,不得已将正则表达式的一部分放入分组,我们可以在该分组括号上以
(?:...)
形式取消分组的优先显示。# findall取消优先:?: ret = re.findall('1(d)(d)', '123124') print(ret) # [('2', '3'), ('2', '4')] ret = re.findall('1(?:d)(d)', '123124') print(ret) # ['3', '4']
-
应用场景
-
方便读取
# 为什么要用分组,以及findall的分组优先到底有什么好处 exp = '2-3*(5+6)' # a+b 或者是 a-b 并且计算它们的结果 ret = re.search('(d+)[+](d+)', exp) print(int(ret.group(1)) + int(ret.group(2)))
-
在爬虫中进行字段的精确匹配和读取
我们寻找了一个豆瓣网页,将它以HTML文件形式存储在项目目录中,我们现在读取它源码中的电影名字:
with open('douban.html', encoding='utf-8') as f: content = f.read() ret = re.findall('<span class="title">(.*?)</span>(?:s*<span class="title">.*?</span>)?', content) print(ret) # ['肖申克的救赎', '霸王别姬', '这个杀手不太冷', '阿甘正传', '美丽人生', '泰坦尼克号', '千与千寻', '辛德勒的名单', '盗梦空间', '忠犬八公的故事', '机器人总动员', '三傻大闹宝莱坞', '海上钢琴师', '放牛班的春天', '楚门的世界', '大话西游之大圣娶亲', '星际穿越', '龙猫', '教父', '熔炉', '无间道', '疯狂动物城', '当幸福来敲门', '怦然心动', '触不可及']
-
四、什么是爬虫
通过代码获取到一个网页的源码,要获取源码中嵌着的网页,并从网页中寻找到我们需要的内容,就需要依靠正则表达式来实现。
-
下载 requests 模块:依次寻找目录 File -> Settings->Project->Project Interpreter,然后点击加号,寻找并 Install requests 模块,这时我们就已经成功导入了第三方模块 requests,利用
requests.get()
即可获取网页的源码。import requests ret = requests.get('http://www.baidu.com') print(ret.content.decode('utf-8')) # <!DOCTYPE html> # <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道.......