← 返回 Day 77 正文

Day 77 · 练习与清单

Day 077 — 练习清单与题目

✅ 今日完成清单


📝 基础练习题

练习 1:自定义去重过滤器

要求:编写一个去重过滤器,要求: - 正常基于 URL 去重 - 但忽略 URL 中的 utm_source、utm_medium、utm_campaign 三个追踪参数 - 测试以下 URL,验证去重效果:

  https://example.com/page?utm_source=google&page=1
  https://example.com/page?utm_source=facebook&page=1  # 应被过滤
  https://example.com/page?utm_source=google&page=2    # 应通过(不同 page)

预期输出:

✅ 通过: https://example.com/page?utm_source=google&page=1
⛔ 过滤: https://example.com/page?utm_source=facebook&page=1
✅ 通过: https://example.com/page?utm_source=google&page=2

练习 2:Pipeline 执行顺序

要求:给定三个 Pipeline,编写正确的 ITEM_PIPELINES 配置: - StorePipeline:存储到数据库 - CleanPipeline:清洗数据(去除空格、标准化价格) - NotifyPipeline:发送通知

要求输出:

1. 先清洗
2. 再存储
3. 最后通知(因为通知应该发送已清洗的数据)

写出 ITEM_PIPELINES 的配置代码。


练习 3:DropItem 练习

要求:编写一个 ValidatePipeline,丢弃以下不合规数据: - price 为负数 - name 长度小于 2 - url 不以 http 开头

测试数据:

python
test_items = [
    {'name': 'Good Product', 'price': 99.9, 'url': 'https://example.com/1'},
    {'name': 'X', 'price': 50, 'url': 'https://example.com/2'},      # 丢弃
    {'name': 'Bad Price', 'price': -10, 'url': 'https://example.com/3'},  # 丢弃
    {'name': 'No URL', 'price': 100, 'url': 'ftp://example.com/4'},  # 丢弃
]

🔥 进阶挑战题

挑战 1:带限速的调度器

要求:在 SimpleScheduler 基础上添加限速功能: - 每秒最多发送 N 个请求(可配置) - 超出限制时,请求进入等待队列 - 到达时间窗口后自动出队

提示:使用 time.time() 和滑动窗口算法。


挑战 2:Pipeline 链中断恢复

要求:设计一个支持断点续传的 Pipeline: - 处理到一半时模拟进程中断 - 记录已处理的 Item ID - 重启后跳过已处理的 Item

提示:用一个简单的 JSON 文件记录已处理 ID。


挑战 3:并发 Pipeline 性能测试

要求: - 创建 3 个不同的 Pipeline(清洗、验证、存储) - 分别测试:顺序执行 vs 模拟并发执行的性能差异 - 用 10000 条数据进行测试 - 输出执行时间对比


📋 参考答案提示

练习 1 提示

python
import hashlib
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse

IGNORE_PARAMS = {'utm_source', 'utm_medium', 'utm_campaign'}

def fingerprint(url):
    parsed = urlparse(url)
    params = parse_qs(parsed.query, keep_blank_values=True)
    for p in IGNORE_PARAMS:
        params.pop(p, None)
    sorted_query = urlencode(sorted(params.items()), doseq=True)
    clean = urlunparse(parsed._replace(query=sorted_query))
    return hashlib.sha1(clean.encode()).hexdigest()

练习 2 提示

python
ITEM_PIPELINES = {
    'myproject.pipelines.CleanPipeline': 100,
    'myproject.pipelines.StorePipeline': 200,
    'myproject.pipelines.NotifyPipeline': 300,
}

练习 3 提示

python
from scrapy.exceptions import DropItem

def validate(item):
    if item.get('price', 0) < 0:
        raise DropItem("价格为负数")
    if len(item.get('name', '')) < 2:
        raise DropItem("名称过短")
    if not item.get('url', '').startswith('http'):
        raise DropItem("URL 格式无效")
    return item