轻量级爬虫实战:用Python标准库抓取Scrape Center电影数据

每次看到那些复杂的爬虫框架配置就头疼?其实对于结构规整的静态网页,用Python自带的requests和re模块就能快速搞定数据抓取。今天我们就以Scrape Center的SSR1电影网站为例,教你如何用不到100行代码实现一个轻量级但功能完备的爬虫。

1. 为什么选择requests+re组合

在Python爬虫领域,Scrapy和BeautifulSoup确实功能强大,但它们的学习曲线和项目配置成本往往让初学者望而却步。相比之下,requests+re组合有以下优势:

  • 零依赖 :仅需Python标准库,无需安装额外包
  • 学习成本低 :requests的API设计极其简单直观
  • 执行效率高 :正则表达式处理静态页面速度飞快
  • 调试方便 :可以逐步测试每个正则匹配环节

提示:这种方案最适合结构固定、不需要处理JavaScript渲染的静态网页。对于动态加载的现代网站,可能需要考虑Selenium等工具。

2. 环境准备与基础配置

开始前,确保你的Python环境已经就绪。我们只需要最基本的库:

import re
import time
import requests
from requests.exceptions import RequestException

虽然可以添加fake_useragent来伪装浏览器,但为了保持极简,我们先用固定User-Agent:

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}

3. 网页结构分析与正则设计

打开Scrape Center SSR1的任意页面(如 https://ssr1.scrape.center/page/1 ),查看源代码会发现电影信息都包裹在 <div class="el-col-md-4"> 的容器中。每个电影包含:

  1. 封面图片URL(img标签的src属性)
  2. 电影名称(h2标签内文本)
  3. 分类标签(多个button下的span文本)
  4. 详细信息(国家、时长、上映时间等)
  5. 评分(score类下的p标签文本)

对应的正则表达式可以这样设计:

PATTERN = re.compile(
    r'<div.*?el-col-md-4.*?'
    r'src="(.*?)".*?'  # 图片URL
    r'<h2.*?>(.*?)</h2>.*?'  # 电影名称
    r'button.*?<span>(.*?)</span>.*?'  # 分类标签1
    r'button.*?<span>(.*?)</span>.*?'  # 分类标签2 
    r'button.*?<span>(.*?)</span>.*?'  # 分类标签3
    r'info.*?<span.*?>(.*?)</span>.*?'  # 国家
    r'<span.*?>(.*?)</span>.*?'  # 类型
    r'<span.*?>(.*?)</span>.*?'  # 时长
    r'info.*?<span.*?>(.*?)</span>.*?'  # 上映时间
    r'score.*?>(.*?)</p>',  # 评分
    re.S)

4. 核心功能实现

4.1 页面获取函数

def fetch_page(url):
    try:
        resp = requests.get(url, headers=HEADERS, timeout=10)
        resp.encoding = resp.apparent_encoding
        return resp.text if resp.status_code == 200 else None
    except RequestException as e:
        print(f"请求失败: {e}")
        return None

4.2 数据解析函数

def parse_movies(html):
    items = re.findall(PATTERN, html)
    movies = []
    for item in items:
        movie = {
            'cover': item[0],
            'title': item[1],
            'categories': list(set(item[2:5])),  # 去重
            'country': item[5],
            'duration': item[7],
            'release': item[8],
            'score': item[9].strip()
        }
        movies.append(movie)
    return movies

4.3 数据存储函数

虽然原始示例使用了Excel存储,但JSON格式更适合这种结构化数据:

import json

def save_to_json(data, filename='movies.json'):
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

5. 完整爬虫实现与优化

将所有组件组合起来,并添加一些优化措施:

def scrape_ssr1(max_page=10):
    base_url = "https://ssr1.scrape.center/page/{}"
    all_movies = []
    
    for page in range(1, max_page + 1):
        url = base_url.format(page)
        html = fetch_page(url)
        if not html:
            continue
            
        movies = parse_movies(html)
        all_movies.extend(movies)
        print(f"已抓取第{page}页,共{len(movies)}部电影")
        
        time.sleep(1)  # 礼貌性延迟
    
    save_to_json(all_movies)
    print(f"全部完成,共抓取{len(all_movies)}部电影数据")

6. 正则表达式调试技巧

编写复杂的正则表达式时,可以分步验证:

  1. 逐步构建 :先匹配大框架,再逐步添加细节
  2. 在线测试 :使用regex101.com等工具实时调试
  3. 可视化工具 :如Debuggex帮助理解模式结构
  4. 单元测试 :对样本HTML片段进行针对性测试

遇到匹配问题时,可以:

  • 检查 re.S 标志是否正确使用(处理换行)
  • 确认贪婪 .*? 和非贪婪 .* 的区别
  • 使用 re.DEBUG 标志查看解析过程

7. 扩展与进阶

虽然这个爬虫已经很实用,但还可以进一步优化:

  • 异常处理 :增加重试机制应对网络波动
  • 并发请求 :使用 concurrent.futures 提升速度
  • 增量爬取 :记录已抓取URL避免重复
  • 数据清洗 :对抓取结果进行规范化处理
# 带重试机制的请求函数示例
def fetch_with_retry(url, retries=3):
    for i in range(retries):
        try:
            return fetch_page(url)
        except Exception as e:
            if i == retries - 1:
                raise
            time.sleep(2 ** i)  # 指数退避

在实际项目中,我发现正则表达式虽然强大,但对于特别复杂的HTML结构,维护成本会显著上升。这时候可以考虑用 lxml 这样的轻量级解析库作为补充,它比BeautifulSoup更快,又比纯正则更易维护。

更多推荐