在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('')
# 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