正则表达式
python
import re
text = "今天天气真好 https://www.bilibili.com 和 https://github.com 都不错"
print(re.findall(r"https?://[^\s]+", text))
# 输出:['https://www.bilibili.com', 'https://github.com']
中括号 [] → "任选一个字符"
反斜杠 \d \s \w → 数字、空白、字母数字下划线
脱字符 ^ → 在 [] 里表示"非",在 [] 外表示"行首"
[^\s]就是"只要不是空白字符,谁都可以"括号 () → "我只要括号里的"
点星问号 .*? → 非贪婪,爬虫 80% 场景靠它
()
python
import re
s = '<span class="price">¥39.90</span>'
print(re.findall(r'¥(\d+\.\d{2})', s))
# 输出:['39.90']
\. 转义字符 \d{2}两位小数 + 必须"有数字",所以用
+而不用*
python
import re
html = '''
<img src="">
<img src="a.png">
'''
print(re.findall(r'src="([^"]*)"', html))
# 输出:['', 'a.png']
https?://[^\s]+ 有没有S都可以
python
import re
s = '文件: report.pdf 大小: 8M 文件: summary 大小: 4M'
print(re.findall(r'文件:\s*([^\s]+)\s+大小', s))
# 输出:['report.pdf', 'summary']
a.*b匹配整串:a123ba456b
a.*?b匹配两段:a123b和a456b(若用findall)
.仍是"任意一个字符"(换行除外,除非加re.S)
*仍是"0 次或多次"
?放在量词(*、+、?、{m,n})后面时,把默认的"贪婪"改成"非贪婪"(也叫"懒惰")
python
import re
html = '''<td class="title">肖申克的救赎</td>
<td class="rating_num" align="center">9.7</td>'''
lst = re.findall(r'<td class="title">(.*?)</td>.*?<td class="rating_num" align="center">(.*?)</td>', html)
print(lst)
# 屏幕打印:[('肖申克的救赎', '9.7')]
固定标签原样写
会变内容用
(.*?)包起来中间无关内容用
.*?跳过要几个内容就加几个括号
re.S(或者写全re.DOTALL)作用:让
.也能匹配换行符 。爬虫 HTML 经常换行,不加它
.*?遇到换行就刹车。
re.I(re.IGNORECASE)作用:忽略大小写。
有的网站写
src=有的写SRC=,一加re.I全算对。
re.M(re.MULTILINE)作用:让
^$能匹配每一行的行首/行尾,而不是整个字符串开头结尾。爬虫很少用,先知道即可。
python
import re
html = '''<title>
豆瓣电影 Top 250
</title>'''
# 不加 re.S → 什么都拿不到,因为 . 遇到 \n 停了
print(re.findall(r'<title>(.*?)</title>', html)) # []
# 加 re.S → 把换行当普通字符,成功
print(re.findall(r'<title>(.*?)</title>', html, re.S)) # ['\n 豆瓣电影 Top 250\n']
大小通吃
python
s = '<IMG SRC="a.png">'
print(re.findall(r'src="([^"]*)"', s, re.I)) # ['a.png']
python
import re
html = '''<div class="item">
<a href="https://movie.douban.com/subject/1292052/">
<span class="title">肖申克的救赎</span>
</a>
<div class="star">
<span class="rating_num">9.7</span>
</div>
</div>'''
print(re.findall(r'<span class="title">(.*?)</span>.*?<span class="rating_num">(.*?)</span>', html, re.S))
# 输出:[('肖申克的救赎', '9.7')]
这里
.*?只是"桥梁 "------
我们要的是左右两个<span>里的内容,中间不管有多少换行、空格、缩进、别的标签,都跳过 ,但别跳过火(不能一路吃到页面最底部)。 必须要加 re.S 不然.*?吃不了 换行符!
(?P<名字>...)给分组起别名
python
import re
pattern = re.compile(
r'<span class="title">(?P<title>.*?)</span>.*?'
r'<span class="rating_num">(?P<score>.*?)</span>',
re.S
)
html = '''<div class="item">
<span class="title">肖申克的救赎</span>
<span class="rating_num">9.7</span>
</div>'''
m = pattern.search(html)
if m:
print('电影:', m.group('title'))
print('评分:', m.group('score'))
# 输出
# 电影: 肖申克的救赎
# 评分: 9.7
python
import requests, re, pandas as pd, time
# 1. 预编译正则------匹配单个电影条目
item_pattern = re.compile(r'<div class="item">(.*?)</div>\s*</div>\s*</div>', re.S)
# 在条目内分别匹配链接、标题、评分
link_pattern = re.compile(r'<a href="https://movie\.douban\.com(/subject/\d+/)">')
title_pattern = re.compile(r'<span class="title">([^<]+)</span>') # 只匹配中文标题(第一个)
score_pattern = re.compile(r'<span class="rating_num"[^>]*>([^<]+)</span>')
# 2. 翻页函数
def one_page(start: int):
url = 'https://movie.douban.com/top250'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
params = {'start': start, 'filter': ''}
html = requests.get(url, headers=headers, params=params, timeout=10).text
# 找到所有电影条目
items = item_pattern.findall(html)
results = []
for item in items:
# 在每个条目中分别查找链接、标题、评分
link_match = link_pattern.search(item)
title_match = title_pattern.search(item)
score_match = score_pattern.search(item)
if link_match and title_match and score_match:
link = link_match.group(1)
title = title_match.group(1).strip()
score = score_match.group(1).strip()
results.append((link, title, score))
return results
# 3. 主循环:10 页
data = []
for s in range(0, 250, 25):
data.extend(one_page(s))
time.sleep(1) # 礼貌爬,别冲太快
# 4. 转 DataFrame 并写 CSV
df = pd.DataFrame(data, columns=['link', 'title', 'score'])
df.to_csv('douban_top250_regex.csv', index=False, encoding='utf-8-sig')
print('搞定!共', len(df), '条,已保存为 douban_top250_regex.csv')