在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