Python爬虫实战:采集双色球(SSQ)历史开奖数据(期号、红球、蓝球、开奖日期)等信息,并进行结构化CSV存储(Requests + Pandas)!

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

㊗️爬虫难度指数:⭐⭐⭐

🚫声明:本数据&代码仅供学习交流,严禁用于商业用途、倒卖数据或违反目标站点的服务条款等,一切后果皆由使用者本人承担。公开榜单数据一般允许访问,但请务必遵守"君子协议",技术无罪,责任在人。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [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爬虫实战》👈

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

1️⃣ 摘要(Abstract)

项目代号: Lottery_History_Miner
核心目标: 自动化抓取双色球(SSQ)历史开奖数据(期号、红球、蓝球、开奖日期),并进行结构化存储。
工具栈: Requests (获取源码) + BeautifulSoup (定位表格) + Pandas (表格解析与清洗)。
最终产出: 包含近 20 年开奖记录的 lottery_history.csv 文件。

💡 读完这篇你将获得:

  1. 精通 HTML <table> 表格数据的批量提取技巧。
  2. 掌握 HTTP POST/GET 参数传递,学会如何"一次性请求所有数据"而无需翻页。
  3. 学会处理包含多个数据列(如红球1-6、蓝球)的复杂字段映射。

2️⃣ 背景与需求(Why)

  • 为什么要爬: 许多彩票官网只展示最近 30-50 期的数据。如果你想分析过去 10 年的号码分布规律,手动复制是不可能的。

  • 目标站点: datachart.500.com(著名的500彩票网历史数据页,静态且稳定)。

  • 目标字段清单:

    • issue_id (期号,如 23050)
    • draw_date (开奖日期,如 2023-05-01)
    • red_balls (红球号码,6位)
    • blue_ball (蓝球号码,1位)
    • sales (本期销量 - 可选)
    • pool (奖池金额 - 可选)

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

在涉足彩票数据时,必须保持理性与克制:

  • Robots.txt: 检查目标站点的 Robots 协议。通常公开的历史数据页面允许搜索引擎抓取,但禁止高频爬取。
  • 频率控制: 此类网站往往是老旧的 CMS 系统,并发能力弱。严禁 使用多线程高并发轰炸,务必设置 time.sleep
  • 用途声明: 爬虫产出的数据仅用于统计学研究编程练习不鼓励、不诱导任何形式的赌博行为。彩票有风险,购买需理性。

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

技术分析:

如果你直接访问目标网页,你会发现它是一个巨大的 HTML 表格。

  • Requests:既然是静态表格,直接请求即可。
  • Trick(小技巧): 很多历史数据页允许你通过 URL 参数(如 start=03001&end=23050)一次性拉取所有历史数据!这意味着我们不需要写翻页逻辑,一个请求就能拿到所有数据。

🛠️ 流程图:

构建参数(起止期号) → 发送请求 → 提取 <tr> 行数据 → 拆分 <td> 单元格 → 清洗合并号码 → 存入 CSV

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

Python 版本: 3.8+
依赖安装:

bash 复制代码
pip install requests beautifulsoup4 pandas lxml

项目结构:

text 复制代码
lottery_scraper/
├── ssq_history.py      # 主程序
└── lottery_history.csv # 结果文件

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

这里我们使用一个小技巧:直接构造 URL 参数请求"所有"数据。

python 复制代码
import requests
import time

def fetch_lottery_data(start_period, end_period):
    """
    抓取指定期号范围的数据
    """
    # 500彩票网的历史数据接口(这是一个公开的静态页面接口)
    target_url = "https://datachart.500.com/ssq/history/newinc/history.php"
    
    # 构造参数:start 和 end 是期号,例如 '03001' 到 '23150'
    params = {
        'start': start_period,
        'end': end_period
    }
    
    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'
    }
    
    try:
        print(f"🔄 Requesting data from {start_period} to {end_period}...")
        response = requests.get(target_url, params=params, headers=headers, timeout=15)
        response.raise_for_status()
        response.encoding = 'utf-8' # 显式指定编码,防止日期乱码
        return response.text
    except Exception as e:
        print(f"❌ Connection Failed: {e}")
        return None

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

这是本篇最精华的部分。我们需要从成百上千个 <tr> 标签中准确提取数据。

python 复制代码
from bs4 import BeautifulSoup

def parse_html_table(html_content):
    """
    解析 HTML 表格并返回结构化数据列表
    """
    soup = BeautifulSoup(html_content, 'lxml')
    data_rows = []
    
    # 定位核心表格:通常有一个特定的 ID 或 class
    # 在该网站中,数据行位于 id="tdata" 的 tbody 下
    tbody = soup.find('tbody', id='tdata')
    
    if not tbody:
        print("⚠️ Error: Could not find the data table. Structure might have changed.")
        return []
    
    # 遍历每一行 tr
    # class="t_tr1" 是数据行的特征,避免抓到表头
    rows = tbody.find_all('tr', class_='t_tr1')
    
    for row in rows:
        cells = row.find_all('td')
        
        # 容错检查:确保这一行有足够的数据格
        if len(cells) < 10:
            continue
            
        # 根据网页结构提取字段(需要对着网页源码数格子)
        # 0: 期号, 1-6: 红球, 7: 蓝球, 15: 开奖日期 (假设)
        try:
            issue = cells[0].text.strip()
            
            # 红球通常在第 2 到 第 7 列 (索引 1-6)
            red_balls = [cells[i].text.strip() for i in range(1, 7)]
            
            # 蓝球在第 8 列 (索引 7)
            blue_ball = cells[7].text.strip()
            
            # 开奖日期通常在最后一列或特定列,这里假设是第 16 列(索引 15)
            # 具体索引需根据实际网页调整,500网通常是最后一个td或指定位置
            date_str = cells[-1].text.strip() 
            
            data_rows.append({
                'Issue': issue,
                'Date': date_str,
                'Red_1': red_balls[0],
                'Red_2': red_balls[1],
                'Red_3': red_balls[2],
                'Red_4': red_balls[3],
                'Red_5': red_balls[4],
                'Red_6': red_balls[5],
                'Blue': blue_ball,
                'Full_Draw': f"{','.join(red_balls)}|{blue_ball}" # 方便直观查看
            })
            
        except IndexError as e:
            print(f"⚠️ Parsing Error on row: {issue if 'issue' in locals() else 'Unknown'} - {e}")
            continue
            
    return data_rows

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

对于表格类数据,Pandas 是绝对的王者。

python 复制代码
import pandas as pd
import os

def save_lottery_data(data_list):
    if not data_list:
        print("⚠️ No data to save.")
        return

    df = pd.DataFrame(data_list)
    
    # 数据清洗:按期号排序
    df.sort_values(by='Issue', ascending=False, inplace=True)
    
    filename = "lottery_history_ssq.csv"
    
    # 导出
    df.to_csv(filename, index=False, encoding='utf-8-sig')
    print(f"💾 Success! Saved {len(df)} records to {filename}")
    
    # 打印前 3 行预览
    print("\n📊 Data Preview:")
    print(df[['Issue', 'Date', 'Full_Draw']].head(3).to_markdown(index=False))

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

我们将期号范围设定得大一点,比如从 03001(2003年第一期)到 26001(未来预设),网站通常会自动返回有效范围内的所有数据。

python 复制代码
if __name__ == "__main__":
    # 设定一个足够大的范围,覆盖过去 20 年
    start_issue = "03001" 
    end_issue = "25150"  # 假设抓取到 2025 年某期
    
    print("🚀 Lottery Spider Initialized...")
    
    html = fetch_lottery_data(start_issue, end_issue)
    
    if html:
        print("✅ HTML Downloaded. Parsing...")
        parsed_data = parse_html_table(html)
        print(f"🔍 Parsed {len(parsed_data)} rows.")
        save_lottery_data(parsed_data)
    else:
        print("❌ Failed to retrieve data.")

📊 示例运行结果:

text 复制代码
🚀 Lottery Spider Initialized...
🔄 Requesting data from 03001 to 25150...
✅ HTML Downloaded. Parsing...
🔍 Parsed 3000+ rows.
💾 Success! Saved 3000 records to lottery_history_ssq.csv

📊 Data Preview:
| Issue | Date | Full_Draw |
| :--- | :--- | :--- |
| 23130 | 2023-11-12 | 01,08,12,19,25,31|10 |
| 23129 | 2023-11-09 | 05,09,15,22,26,30|06 |
| 23128 | 2023-11-07 | 03,11,14,20,28,32|12 |

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

  1. 表格错位(Column Mismatch):

    • 现象: 抓下来的日期变成了红球,或者蓝球消失了。
    • 原因: 网站改版了,或者某些特殊的历史期数增加了"快乐星期天"等额外列。
    • 解法: 使用 cells[-1] 这种相对索引来定位末尾的日期,或者通过表头(<thead>)动态计算索引。
  2. 编码乱码(Encoding Issues):

    • 现象: 中文日期变成 å¼
    • 解法: 必须显式设置 response.encoding = 'utf-8'。如果还不行,试着设为 gb2312(国内老站常见编码)。
  3. 数据缺失:

    • 现象: 某些期号(如春节休市)中间断开了。
    • 解法: 这是正常的业务逻辑。不要试图填补,保留断档即可,或者在 Pandas 中用 reindex 补全并标记为"休市"。

1️⃣1️⃣ 进阶优化(可选)

  • 数据可视化(Visualization): 拿到数据后,你可以用 Matplotlib 画一张**"蓝球号码出现频率直方图"**,看看哪个号最热。
  • 增量更新: 每次运行脚本时,先读取本地 CSV 的最新一期期号(比如 23130),然后请求参数 start=23131,只抓取更新的部分,追加(Append)到文件末尾。

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

复盘:

今天我们完成了一个**"一次性全量抓取"的经典案例。通过分析 URL 参数,我们避开了复杂的翻页逻辑,直接用最简单的方法拿到了最有价值的结构化数据。这就是爬虫的最高境界------"少写代码,多做分析"**。

下一步:

如果你想挑战更高难度的,试着去爬取**"足球竞彩赔率"**变化数据。那里的数据量是指数级增长的,你需要用到异步 IO (aiohttp) 和数据库 (MySQL/MongoDB) 才能扛得住。

🌟 文末

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

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

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

专栏 👉 《Python爬虫实战》,我会按照"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一篇都做到:

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

📣 想系统提升的小伙伴:强烈建议先订阅专栏,再按目录顺序学习,效率会高很多~

✅ 互动征集

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

评论区留言告诉我你的需求,我会优先安排更新 ✅


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

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

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


免责声明:本文仅用于学习与技术研究,请在合法合规、遵守站点规则与 Robots 协议的前提下使用相关技术。严禁将技术用于任何非法用途或侵害他人权益的行为。

相关推荐
2501_941652772 小时前
基于YOLO12-A2C2f-FRFN的电缆连接器类型识别与定位
python
氵文大师2 小时前
PyTorch 性能分析实战:像手术刀一样精准控制 Nsys Timeline(附自定义颜色教程)
人工智能·pytorch·python
梦幻精灵_cq2 小时前
正文标题党——正文标题也需要精致
python
YMWM_2 小时前
python3中类的__call__()方法介绍
开发语言·python
柠檬07112 小时前
cuda 安装记录
python
Monkey的自我迭代2 小时前
实战项目数据桥agent复盘
数据库·python·oracle
空空潍2 小时前
Python核心基础语法
开发语言·python
历程里程碑3 小时前
子串-----和为 K 的子数组
java·数据结构·c++·python·算法·leetcode·tornado