re模块
一、re模块
首先说一下什么是模块,模块就是一组功能的集合。你要和某个东西打交道,而这个东西本身和python没关系,这个东西本身就存在,所以python提供了一个功能的集合专门负责和这个东西打交道。
模块的类型:
内置模块:不需要我们自己安装,解释器自带的;
第三方模块:需要我们自己安装的模块;
自定义模块:我们自己写的模块;
为什么要有模块?先举一个例子:我们都知道是操作系统把所有硬件管理起来的,文件是在硬盘上存储的,python想从硬盘上进行文件的操作和删除时就需要引入os模块,说白了,模块实际上就是给我们提供功能,这个要操作的内容本身就存在,模块只不过是python提供给我们去操作这个内容的方法。
注意:永远不要起一个和你已知的模块同名的py文件的名字!
今天我们就来学习一下re模块的几个基础方法,在python中使用正则表达式就要引入re模块。
1,查找
findall:表示匹配每一项都是列表中的一个元素。 (重要程度*****)(可用于从大段内容中找到匹配的项目)
语法规则:re.findall(‘正则表达式’, 待匹配字符串 , flag),具体示例如下:
import re ret = re.findall('\d+', 'sjkhk172按实际花费928') print(ret) # 结果为:['172', '928'] ret = re.findall('\d', 'sjkhk172按实际花费928') print(ret) # 结果为:['1', '7', '2', '9', '2', '8']
search:只匹配从左到右的第一个,得到的不是直接的结果,而是一个变量,通过这个变量的group方法来获取结果,如果没有匹配到,会返回None,使用group会报错。(重要程度*****)(可用于表单验证)
语法规则:re.search(‘正则表达式’, 待匹配字符串 , flag),具体示例如下:
ret = re.search('\d+', 'sjkhk172按实际花费928') print(ret) # 结果为:<_sre.SRE_Match object; span=(5, 8), match='172'> print(ret.group()) # 结果为:172 ret = re.search('\d','owghabDJLBNdgv') print(ret) # 结果为:None print(ret.group()) # 会报错 # 因为search方法查找的时候,若待匹配的字符串中没有能匹配到的结果时, # 用group会报错,所以通常我们写成下面这样 ret = re.search('\d+','sjkhk172按实际花费928') if ret : # 内存地址,这是一个正则匹配的结果 print(ret.group()) # 通过ret.group()获取真正的结果
match:从头开始匹配,相当于search中的正则表达式加上一个^。(重要程度**)
语法规则:re.match(‘正则表达式’, 待匹配字符串 , flag),具体示例如下:
ret = re.match('\d+$','sjkhk172按实际花费928') print(ret) # 结果为:None ret = re.match('\d+','172sjkhk按实际花费928') print(ret) # 结果为:<_sre.SRE_Match object; span=(0, 3), match='172'> print(ret.group()) # 结果为:172 ret = re.match('\d+$','172sjkhk按实际花费928') print(ret) # 结果为:None
2,字符串处理的扩展
切割:re.split(‘正则表达式’, 待匹配字符串),用正则表达式匹配出来的结果对待匹配字符串切割。如下:
s = 'alex83taibai40egon25' ret = re.split('\d+',s) print(ret) # 结果为:['alex', 'taibai', 'egon', '']
替换:sub和subn
re.sub(‘正则表达式’, ‘新内容’, 待匹配字符串 , 替换次数)
re.subn(‘正则表达式’, ‘新内容’, 待匹配字符串 , 替换次数) ,示例如下:
ret = re.sub('\d+','H','alex83taibai40egon25') print(ret) # 结果为:alexHtaibaiHegonH ret = re.sub('\d+','H','alex83taibai40egon25',1) print(ret) # 结果为:alexHtaibai40egon25 ret = re.subn('\d+','H','alex83taibai40egon25') print(ret) # 结果为:('alexHtaibaiHegonH', 3) ret = re.subn('\d+','H','alex83taibai40egon25', 2) print(ret) # 结果为:('alexHtaibaiHegonH', 2) # 我们可以看出sub和subn的区别是subn返回一个元组,元组第二个元素是替换次数。
3,re模块的进阶:时间和空间上更优化
compile:节省使用正则表达式解决问题的时间(编译,就是将正则表达式编译成字节码,这样在多次使用的过程中不会多次编译,可以用这个结果去直接search、match、findall、finditer),具体示例如下:
ret = re.compile('\d+') # 编译 print(ret) # 结果为:re.compile('\\d+') res = ret.findall('alex83taibai40egon25') # 编译后直接使用 print(res) res = ret.search('sjkhk172按实际花费928') # 编译后直接使用 print(res.group())
finditer:节省使用正则表达式解决问题的空间/内存(返回一个迭代器,所有的结果都在这个迭代器中,可以通过循环和group取出来),具体示例如下:
ret = re.finditer('\d+','alex83taibai40egon25') print(ret) # 结果为:<callable_iterator object at 0x00000000029EFDA0> for i in ret: print(i.group()) # 结果为: # 83 # 40 # 25
二、在python中使用正则表达式的特点和问题
1,分组在re模块中的使用
分析如下几段代码,理解并总结规则:
# 1 search方法中对正则分组,通过group(n)取出相应内容 s = '<a>wahaha</a>' ret = re.search('<(\w+)>(\w+)</(\w+)>',s) print(ret.group()) # 结果为:<a>wahaha</a>,默认取出所有匹配内容 print(ret.group(1)) # 数字参数代表的是取对应分组中的内容,结果为:a print(ret.group(2)) # 结果为:wahaha print(ret.group(3)) # 结果为:a # 2 findall也可以顺利取到分组中的内容,因为它有一个特殊的语法,就是优先显示分组中的内容 ret = re.findall('(\w+)',s) print(ret) # 结果为:['a', 'wahaha', 'a'] ret = re.findall('>(\w+)<',s) print(ret) # 结果为:['wahaha'] # 3 取消分组优先(?:正则表达式) ret = re.findall('>(?:\w+)<',s) print(ret) # 结果为:['>wahaha<'] ret = re.findall('\d+(?:\.\d+)?','1.234*4') print(ret) # 结果为:['1.234', '4'] # 当你想用()表示正则里的分组,而不想表示优先显示的时候可以用?:取消分组
综上: 对于正则表达式来说,有些时候我们需要进行分组,来整体约束某一组字符出现的次数;对于python语言来说,分组可以帮助你更好更精准的找到你真正需要的内容;具体想表示哪种分组,要看情况决定。
# 4 split方法中可以对正则加括号表示使切割的字符不丢失 ret = re.split('\d+','alex83taibai40egon25') print(ret) # 结果为:['alex', 'taibai', 'egon', ''] ret = re.split('(\d+)','alex83taibai40egon25') print(ret) # 结果为:['alex', '83', 'taibai', '40', 'egon', '25', ''] # 分组命名 (?P<组名>正则表达式) s = '<a>wahaha</a>' ret = re.search('>(?P<con>\w+)<',s) print(ret.group(1)) # 结果为:wahaha print(ret.group('con')) # 结果为:wahaha # 使用前面的分组 要求使用这个名字的分组和前面同名分组中的内容匹配的必须一致 pattern = '<(?P<tab>\w+)>(\w+)</(?P=tab)>' ret = re.search(pattern,s) print(ret.group()) # 结果为:<a>wahaha</a> # 可以练习下列的匹配: # 2018-12-06 # 2018.12.6 # 2018 12 06 # 12:30:30
三、使用正则表达式的技巧
当你要匹配的内容太没有特点,容易和你不想匹配的内容混在一起的时候,就可以把不想匹配的也取出来,然后通过python过滤掉,具体示例如下:
# 想只取整数,但是会带上小数,那么把整数和小数都取出来,然后再过滤掉小数 ret=re.findall(r"\d+\.\d+|\d+","1-2*(60+(-40.35/5)-(-4*3))") print(ret) # 结果为:['1', '2', '60', '40.35', '5', '4', '3'] ret=re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))") ret.remove('') print(ret) # 结果为:['1', '2', '60', '5', '4', '3']
四、爬虫的例子
预备知识:如何获取一个页面的源代码,如下可以做到:
from urllib import request ret = request.urlopen('https://movie.douban.com/top250?start=50&filter=') res = ret.read().decode('utf-8') print(res)
分析并掌握如下爬虫的小例子:
import re from urllib.request import urlopen def getPage(url): # 获取网页的字符串 response = urlopen(url) return response.read().decode('utf-8') def parsePage(s): ret = com.finditer(s) # 从s这个网页源码中 找到所有符合com正则表达式规则的内容 并且以迭代器的形式返回 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): # 0 25 50 # 这个函数执行10次,每次爬取一页的内容 url = 'https://movie.douban.com/top250?start=%s&filter=' % num response_html = getPage(url) # response_html就是这个url对应的html代码 就是 str ret = parsePage(response_html) # ret是一个生成器 print(ret) f = open("move_info7", "a", encoding="utf8") for obj in ret: print(obj) data = str(obj) f.write(data + "\n") f.close() 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)
# 这里re.S表示正则表达式中的.不只匹配非换行符,还匹配换行符,即匹配任意字符 count = 0 for i in range(10): main(count) count += 25
写在最后:
正则表达式到底重要到什么程度?
掌握作业中的所有内容
能够看懂常用的正则表达式
并且能够做出一些公司特异性要求的修改