python: re模块
在Python中可以使用正则表达式, Python提供re
模块,包含所有正则表达式的功能。由于Python的字符串本身也用\
转义,所以要特别注意:在字符串的前面加上 r 的前缀, 就不用考虑转义的问题了.
s1 = 'djioi\ndjj' # \n 表示换行 print(s1) # djioi # djj s2 = r'djioi\ndjj' # 字符串前加个r 就不用考虑转义的问题了. print(s2) # djio
python的re模块提供了很多种有关正则表达式的方法
一 . 匹配
1. findall
语法 : re.findall('正则表达式', '字符串')
import re ret = re.findall('\d+', '19874ashfk0248') # 匹配数字,数字的长度至少为1 print(ret) # 返回值为列表 # ['19874', '0248']
当正则表达式里有分组()时, 会优先显示分组里匹配的字符串,无论分组有没有匹配到,没有匹配到则为空字符串' ', 可以在分组里的第一个位置加上 '?:' ,就会取消分组的优先显示特权.
ret2 = re.findall('\d+(\w+)', '19874ashfk0248') print(ret2) # ['ashfk0248'] ret3 = re.findall('(-)?\d+', '78543') print(ret3) # ['']
ret2 = re.findall('\d+(?:\w+)', '19874ashfk0248') # 在分组里加入 ?: print(ret2) # ['19874ashfk0248'] ret3 = re.findall('(?:-)?\d+', '78543') print(ret3) # ['78543']
2. search
语法 : search('正则表达式', '字符串')
ret1 = re.search('\d+', '19874ashfk0248') print(ret1) # <re.Match object; span=(0, 5), match='19874'> print(ret1.group()) # 19874
ret3 = re.search('\d', 'shuyuiiuus') print(ret3) # None 没有匹配上的字符时,会返回None # print(ret3.group()) # 报错: 'NoneType' object has no attribute 'group'
当正则表达式里有分组()时,group()可以传数字来显示第几个分组匹配的值, 数字为0或没有时,表示显示匹配所有的值.
ret2 = re.search('(\d+)(\w+)(\d+)', '19874ashfk0248') print(ret2.group()) # 19874ashfk0248 print(ret2.group(0)) # 19874ashfk0248 print(ret2.group(1)) # 19874 print(ret2.group(2)) # ashfk024 print(ret2.group(3)) # 8
3. match
语法 : re.match('正则表达式', '字符串')
ret1 = re.match('\d+', '786jhg032') print(ret1) # <re.Match object; span=(0, 3), match='786'> print(ret1.group()) # 786 ret2 = re.match('\d+', 'kt786jhg032') # 字符串的第一个不是数字,match里的正则表达式自带开始符: ^ ,所以字符串的第一个字符必须是数字才能匹配上. print(ret2) # None # print(ret2.group()) # 报错: 'NoneType' object has no attribute 'group'
4. finditer
语法 : re.finditer('正则表达式', '字符串')
ret1 = re.finditer('\d+', '786jhg032te654') # ret1是个迭代器 print(ret1) # <callable_iterator object at 0x000001208AF38AC8> print(ret1.__next__().group()) # 786 print(ret1.__next__().group()) # 032 print(ret1.__next__().group()) # 654
总结 findall, search, match, finditer 的区别 :
findall : 在string中查找所有 匹配成功的组, 即用括号括起来的部分, 返回list对象, 每个列表元素是由每个匹配的所有组组成的list.
search : 在string中进行搜索,成功返回object, 失败返回None, 只匹配一个。
match : 匹配string 开头,成功返回object, 失败返回None,只匹配一个。
finditer : 在string中查找所有 匹配成功的字符串, 返回iterator,每个元素是一个object。
二. 替换
1. sub
语法 : re.sub('正则表达式', '替换的元素', '字符串', 次数)
ret1 = re.sub('\d+', 'h', 'dguj23kk321jjjj222kkkk111', 2) # 把字符串里匹配到的的数字替换成 'h', 只替换2次 print(ret1) # dgujhkkhjjjj222kkkk111 ret2 = re.sub('\d+', 'h', 'dguj23kk321jjjj222kkkk111') # 没有替换次数,默认全部替换 print(ret2) # dgujhkkhjjjjhkkkkh
2. subn
语法 : re.subn('正则表达式', '替换的元素', '字符串', 次数)
ret3 = re.subn('\d+', 'd', 'hhd3567fg8er590ggg0mmm345') # subn会返回替换的次数 print(ret3) # ('hhddfgderdgggdmmmd', 5) ret4 = re.subn('\d+', 'd', 'hhd3567fg8er590ggg0mmm345', 3) print(ret4) # ('hhddfgderdggg0mmm345', 3)
三. 切割
split 语法 : re.split('正则表达式', '字符串')
s = 'qwerty' lst = s.split('qwerty') print(lst) # ['', ''] re1 = re.findall('\d+', 'dyudkd34hudslwio987hud22aokd') print(re1) # ['34', '987', '22'] ret1 = re.split('\d+','dyudkd34hudslwio987hud22aokd') print(ret1) # ['dyudkd', 'hudslwio', 'hud', 'aokd'] re2 = re.findall('\d+\w+', '19874ashfk0248') print(re2) # ['19874ashfk0248'] ret2 = re.split('\d+\w+', '19874ashfk0248') # 匹配到的字符串'19874ashfk0248' 对 源字符串'19874ashfk0248'进行切割,得到两个空字符串 print(ret2) # ['', ''] # # re3 = re.findall('\d+(\w+)', '19874ashfk0248') print(re3) # ['ashfk0248'] ret3 = re.split('\d+(\w+)', '19874ashfk0248') # 正则表达式里有分组时,把匹配的字符串对原字符串切割后,还要显示分组里匹配到的字符串 print(ret3) # ['', 'ashfk0248', ''] # # re4 = re.findall('\d+(?:\w+)', '19874ashfk0248') print(re4) # ['19874ashfk0248'] ret4 = re.split('\d+(?:\w+)', '19874ashfk0248') print(ret4) # ['', '']
四.进阶方法
1. finditer
re.finditre获得的是一个迭代器,取值时节省内存,提高空间效率
2. complie
使用re的一般步骤是先将正则表达式的字符串形式通过complie编译为一个实例对象,然后使用这个实例对象处理文本并获得匹配结果.我们在多次使用一个正则表达式时,complie会帮我们节省很多时间.
语法 : re.complie('正则表达式')
ret = re.compile('\d+') ret1 = ret.findall('1234') ret2 = ret.search('qwer') print(ret1) # ['1234'] print(ret2) # None 没有匹配到字符,返回None
五. 分组练习
# 把所有的数字给取出来 ret1 = re.findall('-?\d+(?:\.\d+)?', "1-2*(60+(-40.35/5)-(-4*3))") print(ret1) # ['1', '-2', '60', '-40.35', '5', '-4', '3'] # 把所有整数取出来 ret2 = re.findall('\d+(?:\.\d+)|(\d+)', '1-2*(60+(-40.35/5)-(-4*3))') print(ret2) # ['1', '2', '60', '', '5', '4', '3'] ret2.remove('') print(ret2) # ['1', '2', '60', '5', '4', '3'] # 把'<a>wahaha<\a>'里的wahaha取出来 ret3 = re.findall('>(\w+)<', r'<a>wahaha<\a>') print(ret3) # ['wahaha'] # r'<(\w+)>(\w+)</(\w+)>' r'<(a)>(\w+)</a>' ret4 = re.search(r'<(\w+)>(\w+)</(\w+)>', r'<a>wahaha</a>') print(ret4.group()) # <a>wahaha</a> # 分组命名 : ?P # # r'<(\w+)>(\w+)</(\w+)>' ret1 = re.search(r'<(?P<name>\w+)>(\w+)</(?P=name)>', r'<a>wahaha</a>') # 把第一个分组的名字命为name, 两对尖角符<>之间的名字必须一样 print(ret1.group()) # <a>wahaha</a> print(ret1.group('name')) # a 通过对group方法传分组名字的参数,来获取分组所匹配的字符 # r"<(\w+)>\w+</\1>" ret2 = re.search(r'<(\w+)>(\w+)</(\1)>', r'<a>wahaha</a>') # \1是默认为和第一个分组名字一样 print(ret2.group()) # <a>wahaha</a> print(ret2.group(1)) # a
六. 爬虫练习
# 爬取豆瓣评分前250名电影的信息 import re from urllib.request import urlopen # 内置的包 来获取网页的源代码 字符串 # res = urlopen('http://www.cnblogs.com/Eva-J/articles/7228075.html') # print(res.read().decode('utf-8')) def getPage(url): response = urlopen(url) return response.read().decode('utf-8') def parsePage(s): # s 网页源码 ret = com.finditer(s) for i in ret: ret = { "id": i.group("id"), "title": i.group("title"), "rating_num": i.group("rating_num"), "comment_num": i.group("comment_num") } yield ret def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num # 0 response_html = getPage(url) # response_html是这个网页的源码 str 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") 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) count = 0 for i in range(10): # 一共十页 main(count) # count = 0 count += 25