Python爬虫实战:鸣枪起跑!深度抓取全国马拉松赛事报名情报!

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~

㊙️本期爬虫难度指数:⭐⭐

🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [0️⃣ 前言(Preface)](#0️⃣ 前言(Preface))
      • [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
      • [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(强烈建议写)](#🔟 常见问题与排错(强烈建议写))
      • [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)
        • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

💕订阅后更新会优先推送,按目录学习更高效💯~

0️⃣ 前言(Preface)

在这个全民路跑的时代,如何从汪洋大海般的赛事资讯中精准捕获报名先机?本文将带你手写一个专业的马拉松赛事爬虫,利用 requests + BeautifulSoup4 穿透第三方汇总站,不仅抓取列表信息,更要利用正则表达式 深入详情页挖掘"报名费"与"限额"等核心字段。

读完本篇,你将获得:

  1. 工业级的"列表-详情"二级深度抓取架构。
  2. 复杂富文本环境下的正则数据清洗技巧。
  3. 一套开箱即用的赛事报名监控原型。

1️⃣ 摘要(Abstract)

本文针对马拉松赛事信息分散、非结构化的特点,采用 Python 爬虫技术实现自动化采集。通过构造高防伪请求头访问赛事日历,提取详情页 URL 并在详情页中利用正则匹配抽取关键数值。数据最终以 marathon_events_china.csv 格式导出,并生成全英文的赛事限额分布图。

2️⃣ 背景与需求(Why)

为什么要爬?

  • 数据分析:分析各城市的赛事密度与消费水平。
  • 信息聚合:为跑友提供一站式报名日历。
  • 自动化监控:结合定时任务,第一时间发现新赛事上架。

目标字段清单:

  • event_name (赛事名)
  • city (城市)
  • event_date (日期)
  • fee (报名费 - 纯数字)
  • quota (限额 - 纯数字)

3️⃣ 合规与注意事项(必写)

  • robots.txt:本实战仅抓取公开展示的资讯,不涉及任何报名支付或个人隐私接口。
  • 频率控制 :第三方汇总站多为个人或小团队维护,请务必设置 time.sleep(3) 以上的延迟,切勿发起攻击式并发。
  • 数据边界:严禁抓取报名系统内的身份证、手机号等用户私密信息。

4️⃣ 技术选型与整体流程(What/How)

由于赛事汇总站多为服务端渲染(SSR)的静态页面,我们选择 requests + bs4 + re 的经典黄金组合。
流程:

  1. Fetcher:请求赛事日历页,获取所有赛事的详情 URL。
  2. Parser:在详情页中,通过关键词锚点定位"费用"与"人数"段落。
  3. Cleaner:利用正则榨取段落中的数字。
  4. Storage:将清洗后的数据存入 CSV。

5️⃣ 环境准备与依赖安装(可复现)

  • Python 版本:3.9+

  • 依赖安装

    bash 复制代码
    pip install requests beautifulsoup4 pandas matplotlib
  • 项目结构

    text 复制代码
    marathon_spider/
    ├── main.py            # 主逻辑
    ├── utils.py           # 正则清洗工具
    └── data/              # 存放输出文件

6️⃣ 核心实现:请求层(Fetcher)

我们需要一个健壮的请求池,处理可能出现的网络波动和简单的反爬策略。

python 复制代码
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class MarathonFetcher:
    def __init__(self):
        self.session = requests.Session()
        # 伪装成真实的跑友浏览器
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
            'Referer': 'https://zuicool.com/event'
        }
        # 配置重试策略
        retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
        self.session.mount('https://', HTTPAdapter(max_retries=retry))

    def get_html(self, url):
        try:
            response = self.session.get(url, headers=self.headers, timeout=15)
            response.raise_for_status()
            response.encoding = 'utf-8' # 显式指定编码防止乱码
            return response.text
        except Exception as e:
            print(f"❌ Fetch Error: {e}")
            return None

7️⃣ 核心实现:解析层(Parser)

详情页的文字往往是:"全马限额10000人,报名费200元"。我们需要用正则来"抠"出这些数字。

python 复制代码
import re
from bs4 import BeautifulSoup

class MarathonParser:
    @staticmethod
    def extract_list(html):
        soup = BeautifulSoup(html, 'html.parser')
        # 假设列表页中每个赛事卡片的 class 是 event-item
        items = soup.select('.event-item') 
        links = []
        for item in items:
            link = item.find('a')['href']
            links.append(link)
        return links

    @staticmethod
    def extract_detail(html):
        soup = BeautifulSoup(html, 'html.parser')
        # 提取标题
        title = soup.find('h1').get_text(strip=True)
        # 获取正文内容
        content = soup.get_text()
        
        # 🟢 正则魔法:寻找"费用"或"元"附近的数字
        fee_match = re.search(r'(?:报名费|费用|人民币).*?(\d+)\s*元', content)
        fee = fee_match.group(1) if fee_match else "0"
        
        # 🟢 正则魔法:寻找"限额"或"规模"附近的数字
        quota_match = re.search(r'(?:限额|规模|人数).*?(\d+)\s*人', content)
        quota = quota_match.group(1) if quota_match else "0"
        
        return {
            'event_name': title,
            'fee': int(fee),
            'quota': int(quota)
        }

8️⃣ 数据存储与导出(Storage)

我们将使用 Pandas 将结果导出为 CSV,并严格遵守英文表头。

python 复制代码
import pandas as pd
import os

def save_to_csv(data_list):
    filename = "marathon_events_china.csv"
    df = pd.DataFrame(data_list)
    # 按照限额降序排列
    df = df.sort_values(by='quota', ascending=False)
    df.to_csv(filename, index=False, encoding='utf-8-sig')
    print(f"💾 Data saved to {filename}")

9️⃣ 运行方式与结果展示(必写)

运行脚本

bash 复制代码
python main.py

输出示例结果 (Head 3 rows)

event_name city event_date fee quota
2023 北京马拉松 Beijing 2023-10-29 200 30000
2023 上海马拉松 Shanghai 2023-11-26 150 38000
2023 杭州马拉松 Hangzhou 2023-12-17 120 25000

🔟 常见问题与排错(强烈建议写)

  • 403 Forbidden :如果遇到此报错,说明你的 IP 频率过快。建议调大 time.sleep,或在 Headers 中补全 Cookie 字段。
  • 正则抓不到数字 :详情页排版极其混乱,建议先用 soup.get_text() 拿到纯文本,再通过 re.findall 拿到所有数字后根据关键词索引进行二次过滤。
  • 编码乱码 :部分老牌赛事站使用 GBK 编码,请在 response.encoding 中手动指定。

1️⃣1️⃣ 进阶优化(可选但加分)

为了满足专业分析需求,我们生成一张全英文的赛事规模柱状图

python 复制代码
import matplotlib.pyplot as plt

def plot_quota_trend(df):
    plt.figure(figsize=(10, 6))
    # 强制全英文标签
    plt.bar(df['event_name'][:5], df['quota'][:5], color='skyblue')
    plt.title('Top 5 Marathon Events by Quota (China)', fontsize=14)
    plt.xlabel('Event Name', fontsize=12)
    plt.ylabel('Participant Quota', fontsize=12)
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.savefig('marathon_quota_chart.png')
    print("📈 Visualization generated: marathon_quota_chart.png")

1️⃣2️⃣ 总结与延伸阅读

复盘一下,我们通过两级爬取打通了赛事信息的完整链路,特别是利用正则解决了非结构化文本的提取难题。
下一步

  • 接入 Airflow 实现每日定时增量抓取。
  • 使用 Scrapy 框架重构,实现多站点并发分布式采集。

🌟 文末

好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

✅ 专栏持续更新中|建议收藏 + 订阅

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
小钻风33661 小时前
Java函数式编程-lambda表达式
java·开发语言·python
wefly20171 小时前
告别繁琐配置!m3u8live.cn让 M3U8 链接验证变得如此简单
开发语言·前端·python·django·flask·开发工具
伊珞_711 小时前
【雨云图】雨云图简介+简单数据python画图代码
开发语言·python
飞Link2 小时前
深度解析 NT-Xent:对比学习中的标准化温度交叉熵损失
python·算法·数据挖掘·回归
飞Link2 小时前
深度解析 InfoNCE:对比学习背后的“核心功臣”
python·学习·数据挖掘·回归
怪侠_岭南一只猿2 小时前
爬虫工程师学习路径 · 阶段四:反爬虫对抗(完整学习文档)
css·爬虫·python·学习·html
CodeLinghu2 小时前
我写了一个OpenClaw一健部署工具,引发了3w人围观
人工智能·python·语言模型·llm
搬砖者(视觉算法工程师)2 小时前
通俗易懂的 Transformer 入门文章(第一部分):功能概述
人工智能·python
AC赳赳老秦2 小时前
DeepSeek助力国产化AI落地:政务/企业场景下的国产算力适配避坑指南
大数据·人工智能·python·prompt·政务·ai-native·deepseek