主要练习Python正则基础知识,包含3个函数的使用:

re.match()
re.search()
re.findall()

放上常用正则模式,方便对照。

1 re.match 函数

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

re.match(pattern, string, flags=0)
# pattern     匹配的正则表达式
# string      要匹配的字符串
# flags	      标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等

1.1.取出字符串中的数字

import re

content = 'Hello 1234567 is a number. Regex String'
result = re.match('^Hello (\d+).*String$', content)
# "^Hello " 匹配字符串开头; (\d+) 匹配任意个数字; .* 匹配任意字符(换行符除外); 
# String$ 匹配字符串结尾
if result:
    print(result.group(1)) # 取出第一个括号的内容, 即(\d+)中的数字

# 输出结果
  1234567   

 1.2 改进1.1的匹配模式

import re

content = 'Hello 1234567 is a number. Regex String'
result = re.match('.*(\d+).*', content)
# 使用.*匹配其他所有字符, (\d+)匹配我们想要的数字
if result:
    print(result.group(1))

# 输出结果
  7    

结果并不如我们想要的那样,只输出了最后一个数字 7 。原因是正则贪婪模式导致的,贪婪模式会匹配尽可能多的字符。" .* "可以匹配所的字符,自然也包括了我们要的数字,所以它很贪婪的只给我们留一个 7 ,正好满足 (\d+) 的匹配,真的很贪啊。

1.3 改进1.2的匹配模式(非贪婪模式)

import re

content = 'Hello 1234567 is a number. Regex String'
result = re.match('.*?(\d+).*', content)
# 在 .* 后面加 ? 就可以使用非贪婪模式
if result:
    print(result.group(1))

# 输出结果
  1234567    

1.4 多行匹配

import re

content = '''Hello is a number. 
           Regex String 1234567'''  # 把数字换到第二行
result = re.match('.*?(\d+).*', content)
# 在 .* 后面加 ? 就可以使用非贪婪模式
if result:
    print(result.group(1))

# 没有输出结果

result = re.match('.*?(\d+).*', content, flags=re.S)
# flags设置是否区分大小写,多行匹配等:re.S设置'.'可以匹配换行符, re.I忽略大小写...
if result:
    print(result.group(1))

# 输出结果
  1234567    

2 re.search 函数

re.search扫描整个字符串并返回第一个成功的匹配。

re.search(pattern, string, flags=0) # 参数和match()一样

2.1 match和search的对比

import re

content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings'
result = re.match('Hello.*?(\d+).*?Demo', content)
print(result)
# match没有匹配, 字符串不是Extra开头

result = re.search('Hello.*?(\d+).*?Demo', content)
print(result.group(1))

1234567   # 匹配成功

# re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None
# 而re.search匹配整个字符串,直到找到一个匹配

3. re.findall 函数

findall() 会搜索字符串,以列表形式返回全部能匹配的子串。

findall(pattern, string, flags=0)

3.1 找出html中的歌手和歌名

import re

# 取出html中的歌手名和歌名
html = '''<div id="songs-list">
    <h2 class="title">经典老歌</h2>
    <p class="introduction">
        经典老歌列表
    </p>
    <ul id="list" class="list-group">
        <li data-view="2">一路上有你</li>
        <li data-view="7">
            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
        </li>
        <li data-view="4" class="active">
            <a href="/3.mp3" singer="齐秦">往事随风</a>
        </li>
        <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
        <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
        <li data-view="5">
            <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
        </li>
    </ul>
</div>'''

# 使用search
result = re.search('<a.*?singer="(.*?)">(.*?)</a>', html)
# 歌手名和歌名都在<a>标签中, 从 <a 开始匹配
if result:
    print(result.group(1), result.group(2))

# 输出结果
任贤齐 沧海一声笑   
# search只能返回匹配到的第一个结果

# 使用findall
 result = re.findall('<a.*?singer="(.*?)">(.*?)</a>', html)
if result:
    print(result)

# 输出结果
[('任贤齐', '沧海一声笑'), ('齐秦', '往事随风'), ('beyond', '光辉岁月'), 
('陈慧琳', '记事本'), ('邓丽君', '但愿人长久')]
# findall会返回所有匹配的字符串的list

爬虫实战练习

import requests
import re
content = requests.get('https://movie.douban.com/chart').text
# 豆瓣电影排行榜
pattern = re.compile('class="pl2".*?<.*?="(.*?)".*?>(.*?)<span.*?>(.*?)</span>.*?"rating_nums">(.*?)</span>.*?"pl">(.*?)</span>', re.S)
# compile可以在多次使用中提高效率,这里影响不大
results = re.findall(pattern, content)
for result in results:
    url, name1, name2, nums, pl = result
    print(url, name1.replace("/","").strip(), name2.replace("/","").strip(), nums, pl)

总结

尽量多使用search函数,而不是match,需要匹配的值很多时就用findall。使用正则表达式就是把一个问题变成两个问题,所以应该优先使用比如字符串自带的一些处理方法,其他的解析库BeautifulSoup等。