一,正则表达式
Q1.什么是正则表达式?
只和字符串打交道,是一种规则来约束字符串的规则
正则表达式和python是两个语言
Q2.正则表达式的常见使用场景
判断某一个字符串是否符合规则,注册页-表单验证(例:爬虫,日志分析)
二,正则表达式的元字符
#例:[1234567890] 字符组 :是元字符中的一个 在字符组中所有的字符都可以匹配任意一个字符位置上能出现的内容 如果在字符串中有任意一个字符是字符组中的内容,那么就是匹配上的项 [0-9] [a-z] [A-Z] #注意:ascii编码小的值 指向一个大的值 [0-9a-zA-Z] [1-9][0-9]
\d 匹配一个数字
\D 匹配一个非数字 \w 匹配一个数字,字母,下划线
\W 匹配一个非数字或字符或下划线 \s 匹配一个空白符
\S 匹配一个非空白符 \n 匹配一个换行符 \t 匹配一个制表符 \b 匹配一个边界 #例:h\b 字符串的结尾,\bh 字符串开头 ^匹配开头 ^\d \d$ $匹配结尾 ^'...'$ 结合约束一个字符串格式 | 或 从左到右优先匹配,一般长的在左 [^...] 非字符组
. 除换行符外所有的字符
() 分组:对某些固定的内容做量词约束
| 或
# 记法: # \w \d \s \W \D \S \n \t 匹配特殊的字符 # ^ $ \b 匹配边界 # [...] [^...] 字符组相关 #\ :或 # .除了换行符外所有字符
#转意符:\ 在正则表达式中,有很多有特殊意义的是元字符,比如\n和\s等,如果要在正则中匹配正常的"\n"而不是"换行符"就需要对"\"进行转义,变成'\\'。 在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\n",字符串中要写成'\\n',那么正则里就要写成"\\\\n",这样就太麻烦了。这个时候我们就用到了r'\n'这个概念,此时的正则是r'\\n'就可以了。 #正则:r'\\n',待匹配字符串r'\n'
三,正则表达式的量词
一个量词必须跟在元字符后面,一般一个量词只能约束前面一个元字符
* 重复零次或多次 + 重复一次或多次 ? 零次或一次 {n} 重复多次 {n,} 重复n次以上 {n,m} 重复n-m次
四,贪婪匹配与惰性匹配
贪婪匹配:正则会尽量多的帮我们匹配
默认贪婪:回溯算法
回溯算法:
.*x:表示匹配任意长度任意字符,匹配到末尾后,回溯到最后一个x
惰性匹配:会尽量少为我们匹配
量词:?表示非贪婪,惰性匹配
.*?x :表示匹配任意长度任意字符遇到一个x就立即停止匹配
#例: # 身份证号码是一个长度为15或18个字符的字符串, # 如果是15位则全部 数字组成,首位不能为0; [1-9]\d{14} # 如果是18位,首位不能为0 前17位全部是数字,末位可能是数字或x [1-9]\d{16}[\dx] # [1-9]\d{16}[\dx]|[1-9]\d{14} # [1-9]\d{14}(\d{2}[\dx])? # 匹配网址 # www\.(baidu|oldboy|qq)\.com # www.baidu.com # www.oldboy.com # www.qq.com
五,re模块
1.findall ****半
参数:正则表达式,待匹配的字符串
返回值:是一个列表,所有匹配到的项
特点:有一个特点,会优先显示分组中的内容
#findall import re
ret = re.findall('\d(?:\.\d)?[*/]\d','1.5*5') #?:为取消分组优先 print(ret) ret1 = re.findall('(?:www)\.(?:baidu|qq)\.(?:com)','www.baidu.com') ret2 = re.findall('(www)\.(baidu|qq)\.(com)','www.baidu.com') print(ret1) #['www.baidu.com'] print(ret2) #[('www', 'baidu', 'com')]
2.search *****
参数:正则表达式,待匹配的字符串
返回值:返回一个SRE_Match对象
通过group去取值,且只包含第一个匹配到的项,后面有重复也不会匹配
特点:group()==group(0) 匹配到的完全字符串
group(1) 完成匹配,返回正则中第一个分组的字符串
#search
import re ret = re.search('(www)\.(baidu|qq)\.(com)','www.baidu.com') #search的正则有多少个分组都不影响默认值 print(ret.group(0)) #等于ret.group() 显示匹配到的全部 print(ret.group(1)) #www print(ret.group(2)) #baidu print(ret.group(3)) #com
ret = re.search('\d(\.\d)?[*/]\d','1.5*5')
print(ret.group())
3.match:验证用户输入内容
特点:只匹配开头
# 只匹配开头 ret = re.match('\d+','1231asda12342d') #等于:search('^\d+') print(ret) #<_sre.SRE_Match object; span=(0, 4), match='1231'> #(0,4)索引0-3 print(ret.group())
4.split:按照正则表达式匹配的内容进行切割
s2 = 'alex83egon20bossjin' ret = re.split('\d+',s2) #以数字切割字符串 ret2 = re.split('(\d+)',s2) #以数字切割字符串,并保留数字 ret3 = re.split('\d(\d)',s2) # 以数字切割字符串,并保留一位数字的 ret4 = re.split('(\d)+',s2) #(/d)+ == \d\d\d\d..(\d) print(ret)
print(ret2) print(ret3) print(ret4)
5.sub、subn 替换
#sub s1 = 'alex83egon20bossjin' ret = re.sub('\d+','|',s1) #将数字替换为'|' print(ret)
ret = re.sub('\d+','|',s1,1) #指定替换次数为1
print(ret)
#subn ret = re.subn('\d+','|',s1) #将数字替换为'|',并返回元组,包含字符串和替换次数 print(ret) #('alex|egon|bossjin', 2)
6.compile:编译正则规则,提高效率的方法 ****半
由于进行字符串匹配每次原先都需要编译,所以可以将常用的正则表达式进行编译,可以提高代码效率
正则执行流程:正则表达式-->re模块 翻译-->字符串操作-->编译-->字节码-->解释器执行代码
com = re.compile('\d+') #编译正则表达式后,重复使用不用重复编译 print(com) #<class '_sre.SRE_Pattern'> ==> re.compile('\\d+') ret = com.search('asdasd231asdasd') print(ret.group()) ret = com.findall('asdasd231asdasd') print(ret)
7.finditer :节省空间的方法
ret = re.finditer('\d+','abc1cde2fgh3skhfk') print(ret) #<generator object parsePage at 0x000002928E538FC0> 迭代器对象 for i in ret: print(i.group()) #yield i.group() #可以使用生成器函数取值
8.分组命名,分组约束
在前端中,<h1>函数</h1>,<h2>对象</h2>,带格式的的文字,实际上对于前端语言来说,都是把不同样式的字体放在不同的标签中,称为标签语言
pattern = '<(?P<tag>.*?)>.*?</(?P=tag)>' ret = re.search(pattern,'<h1>函数</h1>') print(ret) if ret: print(ret.group()) #<h1>函数</h1> print(ret.group(1)) #h1 print(ret.group('tag')) #h1
pattern = r'<(.*?)>.*?</\1>' #\1 复用第一个分组 ret = re.search(pattern,'<a>函数</a>') print(ret) if ret: print(ret.group()) print(ret.group(1))
#?: 与 ?P(?:正则表达式) 表示取消优先显示功能(?P<组名>正则表达式) 表示给这个组起一个名字 (?P=组名) 表示引用之前组的名字,引用部分匹配到的内容必须和之前那个组中的内容一模一样
9.正则表达式--爬虫实现
from urllib.request import urlopen import re import json def getPage(url): response = urlopen(url) content = response.read().decode('utf-8') return content def parsePage(s): # 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 = json.dumps(obj, ensure_ascii=False) f.write(data + "\n") f.close() if __name__ == '__main__': # 结构必须相似 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) #默认参数flag=re.S指点可以匹配任意字符,包括换行符 count = 0 for i in range(10): main(count) count += 25
#cpmpile的flags有很多可选值: re.I(IGNORECASE)忽略大小写,括号内是完整的写法 re.M(MULTILINE)多行模式,改变^和$的行为 re.S(DOTALL)点可以匹配任意字符,包括换行符 re.L(LOCALE)做本地化识别的匹配,表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境,不推荐使用 re.U(UNICODE) 使用\w \W \s \S \d \D使用取决于unicode定义的字符属性。在python3中默认使用该flag re.X(VERBOSE)冗长模式,该模式下pattern字符串可以是多行的,忽略空白字符,并可以添加注释