爬虫小知识

正则表达式

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']
  1. 中括号 [] → "任选一个字符"

  2. 反斜杠 \d \s \w → 数字、空白、字母数字下划线

  3. 脱字符 ^ → 在 [] 里表示"非",在 [] 外表示"行首"
    [^\s] 就是"只要不是空白字符,谁都可以"

  4. 括号 () → "我只要括号里的"

  5. 点星问号 .*? → 非贪婪,爬虫 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 匹配两段:a123ba456b(若用 findall

  1. . 仍是"任意一个字符"(换行除外,除非加 re.S

  2. * 仍是"0 次或多次"

  3. ? 放在量词(*+?{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')]
  • 固定标签原样写

  • 会变内容用 (.*?) 包起来

  • 中间无关内容用 .*? 跳过

  • 要几个内容就加几个括号

  1. re.S(或者写全 re.DOTALL

    作用:让 . 也能匹配换行符

    爬虫 HTML 经常换行,不加它 .*? 遇到换行就刹车。

  2. re.Ire.IGNORECASE

    作用:忽略大小写。

    有的网站写 src= 有的写 SRC=,一加 re.I 全算对。

  3. re.Mre.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')
相关推荐
培根芝士3 小时前
解决DBeaver对PostgresSQL备份数据库时报错
数据库
Hello World呀4 小时前
登录时,redis出现错误
数据库·redis·缓存
企鹅侠客4 小时前
第02章—先导基础篇:初识Redis
数据库·redis·缓存
哈哈老师啊4 小时前
Springboot新冠检测信息管理系统10m6v(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
7ioik4 小时前
MySQL默认事物隔离级别是什么?
数据库·mysql
程序边界4 小时前
金仓数据库MongoDB兼容深度体验:从协议到性能的硬核拆解
数据库·mongodb
Miqiuha4 小时前
软删除的好处
数据库
愚公搬代码4 小时前
【愚公系列】《扣子开发 AI Agent 智能体应用》020-扣子数据库实战(创建/使用扣子数据库)
数据库·人工智能
冰冰菜的扣jio4 小时前
InnoDB对于MVCC的实现
java·数据库·sql