什么是正则表达式
- 定义:一套规则,可以在字符串文本中进行搜查替换等
- 使用步骤:
- 1.使用
compile()
函数将正则表达式的字符串编译成一个pattern
对象 - 2.通过
pattern
对象的一些方法对文本进行匹配,匹配结果是一个match
对象 - 3.用
match
对象的方法,对结果进行操作
- 1.使用
- 常用方法:
- match:从开始位置开始查找,一次匹配,即1次匹配成功则退出
一般使用形式:
match(string[,pos[,endpos]])
- search:从任何位置开始查找,一次匹配
search[string[, pos[, endpos]]]
例子:
- match:从开始位置开始查找,一次匹配,即1次匹配成功则退出
>>>import re
>>>pattern = re.compile(r'\d+') #用于匹配至少一个数字
>>>m = pattern.match('one12twothree34four') #查找头部,没有匹配
>>>print(m) #输出None
>>>m = pattern.match('one12twothree34four', 2, 10) #从'e'的位置开始匹配,没有匹配到
>>>print(m)
>>>m = pattern.match('one12twothree34four', 3, 10) #从'1' 的位置开始匹配,正好匹配上
>>>print(m)
<_sre.SRE_Match object at 0x10a42aac0>
>>>m.group(0) #可忽略0
'12'
>>>m.start(0) #可忽略0
3
>>>m.end(0) #可忽略0
5
>>>m.span(0) #可忽略0
(3, 5)
>>>m = pattern.search('one12twothree34four') #这里如果使用match方法则不匹配
>>>m
<_sre.SRE_Match object at 0x10cc03ac0>
>>>m.group()
'12'
注:match方法只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而search匹配整个字符串,直到找到一个匹配。match和search都是一次匹配,只要找到一个匹配结果就返回,而不是查找所有匹配结果
- findall:全部匹配,返回列表
findall(string[, pos[, endpos]])
- finditer:全部匹配,返回迭代器
例子:
import re
pattern = re.compile(r'\d+') #查找数字
result1 = pattern.findall('hello 123456 789')
result2 = pattern.findall('one1two2three3four4', 0, 3)
print(result1) #输出:['123456', '789']
print(result2) #输出:[]
r1 = pattern.finditer('hello 123456 789')
r2 = pattern.finditer('one1two2three3four4', 0, 10)
print(r1)
print(r2)
print('r1....')
for m1 in r1:
print("matching string:{} position:{}".format(m1.group(), m1.span()))
""" 输出结果:matching string:123456 position:(6, 12)
matching string:789 position:(13, 16)"""
print('r2....')
for m2 in r2:
print("matching string:{} position:{}".format(m2.group(), m2.span()))
""" 输出结果:matching string:1 position:(3, 4)
matching string:2 position:(7, 8)"""
findall是以列表形式返回全部能匹配到的子串,如果没有匹配,则返回一个空列表。
finditer方法的行为跟findall的行为类似,也是搜索整个字符串,获得所有匹配的结果。但它返回一个顺序访问每一个匹配结果(Match对象)的迭代器
-
split:分割字符串,返回列表
split(string[, maxsplit])
其中maxsplit用于说明最大的分割次数,默认为全部分割
例子:import re p = re.compile(r'[\s\,;]+') print(p.split('a,b;;c d')) #输出结果:['a', 'b', 'c', 'd']
-
sub:替换
re.sub(pattern, repl, string, count=0)
其中,repl可以是字符串也可以是一函数:- 如果repl是字符串,则会使用repl去替换字符串每一个匹配的子串,并返回替换后的字符串,repl还可以使用id的形式来引用过分组,但不能使用编号0;
- 如果repl是函数,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
- count用于指导最多替换次数,不指定时全部替换
- pattern:正则中的模式字符串
例子:
import re
p = re.compile(r'(\w+) (\w+)') #\w=[A-Za-z0-9]
s = 'hello 123, hello 456'
print(p.sub(r'hello world', s)) #使用'hello world'替换'hello 123'和'hello 456'
print(p.sub(r'\2 \1', s))
""" hello world, hello world
123 hello, 456 hello"""
def func(m):
return 'hi' + ' ' + m.group(2)
print(p.sub(func, s))
print(p.sub(func, s, 1))
"""hi 123, hi 456
hi 123, hello 456"""
- 匹配中文
- 中文是Unicode编码(utf-8也是Unicode编码),范围:主要在[u4e00-u9fa5]
- 中文全角逗号一类的不在[u4e00-u9fa5]范围内
- 贪婪与非贪婪模式
- 贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
- 非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
- python里面数量词默认是贪婪模式
例子:
import re
s = "abbbbbbc"
pattern1 = re.compile("ab+")
pattern2 = re.compile("ab*?")
m1 = pattern1.match(s)
m2 = pattern2.match(s)
s1 = m1.group()
s2 = m2.group()
print(s1) #abbbbbb
print(s2) #a 尽可能少匹配b所以没有b
案例
- 结合requests、re两者的内容爬取豆瓣电影 Top 250里的内容
- 要求抓取名次、影片名称、国家、导演等字段。
思路分析:
- 首先由于豆瓣网页默认只有25部电影信息,所以URL是变化的。观察首页的URL为
https://movie.douban.com/top250
,下一页的URL为https://movie.douban.com/top250?start=25&filter=
,下下页为https://movie.douban.com/top250?start=50&filter=
所以可以使用一个while循环来遍历网页
while page <= 225:
url = "https://movie.douban.com/top250?start=%s&filter=" % page
- 分析网页信息,鼠标右键点击网页选择查看网页源代码即可查看页面信息。
- 首先找到我们需要爬取的字段:名次,影片名称,国家,导演。
名次:
影片名称:这里观察到名称有中英文两种title,这里只选择第一个中文title
国家和导演在同一个标签下:
相应的正则表达式:
名次:'<em class="">(.*?)</em>
名称:'<span class="title">(.*?)</span>
导演:'.*?<p class="">.*?导演:(.*?) '
国家:'<br>.*?/ (.*?) /.*?</p>'
- 最后在代码中加上请求头防止爬虫被阻止
完整代码:
import requests
import re
import json
class Movie(object):
def __init__(self, rnd, n):
# rnd: 名次,名字,导演
# n: 国家
self.info = {
'排名': rnd[0],
'电影名': rnd[1],
'导演': rnd[2],
'国家': n
}
def __repr__(self):
return str(self.info)
def parse_html(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 SE 2.X MetaSr 1.0"}
response = requests.get(url, headers=headers)
text = response.text
regix = '<em class="">(.*?)</em>.*?<span class="title">(.*?)</span>''.*?<p class="">.*?导演:(.*?) '
nation = '<br>.*?/ (.*?) /.*?</p>'
rank_name_dire = re.findall(regix, text, re.S)
r3 = re.findall(nation,text,re.S)
info = [Movie(i[0], i[1]) for i in zip(rank_name_dire, r3)]
for i in info:
print(i)
def main():
for offset in range(0, 250, 25):
url = 'https://movie.douban.com/top250?start=' + str(offset) +'&filter='
for item in parse_html(url):
print(item)
write_movies_file(item)
def write_movies_file(str):
with open('douban_film.txt','a',encoding='utf-8') as f:
f.write(json.dumps(str,ensure_ascii=False) + '\n')
if __name__ == '__main__':
main()