03-正则
什么是正则
正则(Regular Expression)是一套文本匹配规则
比如网页里一堆文字,你想一次性提取所有手机号、所有链接、所有数字,用正则一行代码搞定
正则表达式通过 re 内置模块实现
常用正则表达式符号
- \w 匹配字母数字及下划线
- \W 匹配非字母数字下划线
[^0-9a-zA-Z] - \s 匹配任意空白字符, 等价于 [\t\n\r\f]
- \S 匹配任意非空字符
- \d 匹配任意数字 [0-9]
- \D 匹配任意非数字
- \n 匹配一个换行符
- \t 匹配一个水平制表符: 通常用于在文本中插入固定间隔的空白
- [] 匹配集合, 示例[a-z]
- . 匹配除换行符(\n)以外的任意字符
元字符
^ 匹配字符串的开头或取反
import restr =re.match(r'^abc', 'abcdef') # 匹配成功(字符串以 'abc' 开头)str =re.match(r'^abc', '123abc') # 匹配失败(字符串开头不是 'abc')re.findall(r'[^0-9]', 'a1b2') # 匹配所有非数字字符,结果 ['a', 'b']re.search(r'[^aeiou]', 'apple') # 匹配第一个非元音字母 'p'$ 匹配字符串的末尾
import rere.search(r'abc$', '123abc') # 匹配成功(字符串以 'abc' 结尾)re.search(r'abc$', 'abc123') # 匹配失败(字符串结尾不是 'abc')管道符
|
import rere.findall(r'cat|dog', 'I have a cat and a dog.') # 匹配 ['cat', 'dog']量词 {}
# 控制前面的字符匹配重复的次数{m}:精确匹配 m次。{m,}:至少匹配 m次(无上限)。{m,n}:匹配 m到 n次(贪婪模式,尽可能多匹配)。特殊写法- {1,} 最少匹配一次,最多无限制 可通过+代替- {0,} 有就匹配,不存在也不会报错 可通过*代替- {0,1} 最少0次,最多1次 可通过?代替分组或捕获子串(提取数据)
match = re.search(r'(\d{3})-(\d{4})', 'Phone: 123-4567')print(match.group(1)) # 输出 '123'(第一个分组)print(match.group(2)) # 输出 '4567'(第二个分组)方法
Search匹配
用于查找整个字符串的任何位置, 只要匹配成功一个结果就返回,而不是查找所有匹配的结果,它的一般使用形式如下:
re.search(pattern, string[, flags=0])- pattern:匹配的正则表达式
- string: 要匹配的字符串
- flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等
re.search(r'a.b', '"a\nb', re.S)设置.可以匹配\nre.S. 匹配换行符re.M多行模式re.I忽略大小写
- 匹配成功 → 返回一个
Match对象(包含匹配信息)。 匹配失败 → 返回None。
示例: r 用于简化正则表达式的编写。它的作用是避免 Python 字符串的转义规则与正则表达式的转义规则冲突
添加上r之后: 以正则规则为主
import restr = "phone: 123-4567"exg = "23"newStr = re.search(exg, str)print(newStr.group()) # 获取匹配的数据 23str = 'file txt'# 不加r时会出现问题, 程序不确定你是否需要进行转义; 在python正常的字符串中\代表转义, 在正则中\s表示匹配空字符串# 程序无法判断, 设置r之后以正则规则为主体newStr = re.search(r'file\stxt', str) # 输出: 'file txt'print(newStr.group())print(newStr.span()) # 查看匹配长度match
用于查找字符串的头部,只要找到了一个匹配的结果就返回,而不是查找所有匹配的结果
re.match(pattern, string[, flags=0])
- pattern:匹配的正则表达式
- string: 要匹配的字符串
- flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等
- 匹配成功 → 返回一个
Match对象(包含匹配信息)。 匹配失败 → 返回None。
# 只检查字符串的开头,即使中间或结尾有匹配内容也会失败 newStr = re.match('file', 'file^txt') # 输出: fileprint(newStr.group())print(newStr) # 返回的是一个匹配对象print(newStr.group()) # .group()获取匹配内容print(newStr.span()) # 查看匹配长度# 只检查字符串的开头,即使中间或结尾有匹配内容也会失败 newStr = re.match('txt', 'file^txt') # 输出: file^txtprint(newStr.group())findall
用于查找整个字符串, 是一个高效提取所有匹配项的函数,它会扫描整个字符串并返回所有匹配的结果
re.findall(pattern, string[, flags=0])
- pattern:匹配的正则表达式
- string: 要匹配的字符串
- flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等
- 匹配成功 → 返回一个列表 。 匹配失败 → 返回
[]。
import retext = "苹果 12元, 香蕉 5元, 橙子 8元"prices = re.findall(r'\d+元', text)print(prices) # 输出: ['12元', '5元', '8元'] 匹配多个sub
用于替换字符串中所有匹配正则表达式的部分的函数
re.sub(pattern,replace,Sting)
pattern:要匹配的正则表达式(字符串或预编译对象)。repl:替换的内容(可以是字符串或函数)。string:被搜索的目标字符串。count(可选):最多替换次数(默认0表示全部替换)。flags(可选):控制匹配行为的标志(如re.I忽略大小写)
content = 'Extra stings Hello 1234567 World_This is 123 a Regex Demo Extra stings'content = re.sub(r'\d+', '666', content)print(content) # 匹配到的数字全部会被替换content = 'Extra stings Hello 1234567 World_This is 123 a Regex Demo Extra stings'content = re.sub(r'\d+', '666', content, count=1)print(content) # 只替换一次 贪婪匹配
.* 会尽可能多地匹配任意字符(除换行符) 必须满足正则的整体需求
import re# 匹配尽可能多的字符 content = 'Hello 1234567 World_This is a Regex Demo'result = re.match(r'^He.*(\d+)\s(.*)Demo$', content) print(result)print(result.group(1)) # 7print(result.group(2)) # World_This is a Regex # 匹配整个字符串中的最后一个 content = 'Hello 1234567 World_This 8 is a Regex Demo'result = re.match(r'^He.*(\d+)\s.*Demo$', content) print(result.group(1)) # 8# .* 匹配了 llo 1234567 World_This 因为后续还存在数字不影响整体非贪婪匹配
.*?非贪婪匹配任意字符,遇到后续能匹配的内容就停止
import re# 匹配尽可能少的字符content = 'Hello 1234567 World_This is a Regex Demo'result = re.match(r'^He.*?(\d+).*Demo$', content)print(result)print(result.group(1)) # 1234567案例
-
Scrape电影:
https://ssr1.scrape.center/ -
豆瓣读书:
https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8 -
安装
postman
工具
- 下载地址
https://www.postman.com/downloads/
- 下载地址
# 获取Scrape电影网页,分页信息,以及每一项的详细信息import reimport requestsimport timedef spider(): res = requests.get(url, headers=headers) if res.status_code == 200: reg = '<a data-v-7f856186="" href="(.*?)" class="name">.*?<h2 data-v-7f856186="" class="m-b-sm">(.*?)</h2>' data = re.findall(reg, res.text, re.S) for item in data: title_url = "https://ssr1.scrape.center/" + item[0] title = item[1] save_data(title_url, title) passdef save_data(title_url,title): r = requests.get(title_url, headers=headers) if r.status_code == 200: msg = re.findall( r'<div data-v-63864230="" class="drama"><h3 data-v-63864230="">剧情简介</h3>.*?<p data-v-63864230="">(.*?)</p></div>', r.text, re.S) result = re.sub(r'\s+', ' ', msg[0]) with open("./01.txt", "a", encoding="utf-8") as f: f.write(f'电影名称:{title}\n电影详情地址:{title_url}\n详情: {result}\n=================================\n\n') passif __name__ == '__main__': headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" } for i in range(1, 10): url = f"https://ssr1.scrape.center/page/{i}" spider() time.sleep(2)# 豆瓣读书设置翻页import reimport requestsdef spider(url_info): headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" } res = requests.get(url=url_info, headers=headers) return resdef parse_data(res): if res.status_code == 200: data = re.findall(r'<h2 class="clearfix">.*?<a class="fleft" href="(.*?)">(.*?)</a>', res.text, re.S) img = re.findall(r'<img class="subject-cover" align="left" src="(.*?)"/>', res.text, re.S) for title,msg_img in zip(data,img): title_msg = title[1] title_Ulr = title[0] saveData(title_msg,title_Ulr,msg_img) passdef saveData(title_msg,title_Ulr,msg_img): with open("./01.txt",'a',encoding='utf-8') as f: f.write(f"{title_msg} {title_Ulr} {msg_img} \n=================\n") # 存储图片 img_data = requests.get(msg_img, headers=headers).content with open(f"./data/{title_msg}.jpg",'wb') as f: f.write(img_data) passif __name__ == '__main__': headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" } for i in range(1,10): print(f"第{i}页") url_info = f"https://book.douban.com/latest?subcat=%E5%85%A8%E9%83%A8&p={i}&updated_at=" result = spider(url_info) parse_data(result)