boxmoe_header_banner_img

Hello! 欢迎来到豌豆高清!

文章导读

Python爬取双色球彩票开奖信息(二)


avatar
admin 2026 年 10 月 1 日 11


书接上回帖子:Python爬取双色球彩票开奖信息在上次帖子中,仅做了本次爬取(指定爬取条数),并未添加二次更新(爬取数据内容)时现有文件的追加,这次增加了本次爬取存储位置为当前目录下data的文件夹,本次修改内容足够后期使用中对该项目的终结,当然除非页面代码优化,无法获取实时数据,我会尝试更新代码

import requests
import json
import time
import random
import pandas as pd
import os
import re
from typing import Dict, List, Optional
from datetime import datetime, timedelta


class SSQSpider:
    def __init__(self):
        self.base_url = "https://www.cwl.gov.cn/cwl_admin/front/cwlkj/search/kjxx/findDrawNotice"
        self.headers = {
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "Accept-Encoding": "gzip, deflate, br",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Connection": "keep-alive",
            "Host": "www.cwl.gov.cn",
            "Referer": "https://www.cwl.gov.cn/ygkj/wqkjgg/ssq/",
            "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",
            "X-Requested-With": "XMLHttpRequest"
        }
        self.cookies = {
            "HMF_CI": "ccdeb225dfb17006dccc0241dfe329e21005024861f408cc58a70bd6fb877731381b1f33b9130951fda8c4cc26ba65dd51ce93586d3d79177bb66d4a320174ddcd",
            "21_vq": "46"
        }

    def get_ssq_data(self, page_no: int = 1, page_size: int = 30, **kwargs) -> Optional[Dict]:
        time.sleep(random.uniform(0.5, 2))
        params = {
            "name": "ssq",
            "pageNo": page_no,
            "pageSize": page_size,
            "systemType": "PC"
        }
        params.update({k: v for k, v in kwargs.items() if v is not None})
        try:
            response = requests.get(
                self.base_url,
                headers=self.headers,
                params=params,
                cookies=self.cookies,
                timeout=10
            )
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"第 {page_no} 页请求失败: {str(e)}")
            return None

    def parse_data(self, raw_data: Dict) -> List[Dict]:
        if not raw_data or not isinstance(raw_data.get("result"), list):
            return []
        structured_data = []
        for item in raw_data["result"]:
            try:
                date_str = item.get("date", "").split("(")[0].strip()
                try:
                    draw_date = datetime.strptime(date_str, "%Y-%m-%d").date()
                except ValueError:
                    continue
                red_balls = item.get("red", "").split(",")
                blue_ball = item.get("blue", "").strip()
                if len(red_balls) != 6 or not blue_ball:
                    continue
                first_prize = next(
                    (p for p in item.get("prizegrades", []) if p.get("type") == "1"),
                    {"bonus": "0", "num": "0"}
                )
                structured_data.append({
                    "期号": item.get("code", "").strip(),
                    "开奖日期": draw_date.strftime("%Y-%m-%d"),
                    "星期": item.get("week", "").strip(),
                    "红球1": red_balls[0],
                    "红球2": red_balls[1],
                    "红球3": red_balls[2],
                    "红球4": red_balls[3],
                    "红球5": red_balls[4],
                    "红球6": red_balls[5],
                    "蓝球": blue_ball,
                    "开奖号码": " ".join(red_balls) + " + " + blue_ball,
                    "奖池金额(元)": item.get("poolmoney", "0"),
                    "一等奖注数": first_prize.get("num", "0"),
                    "一等奖奖金(元)": first_prize.get("bonus", "0"),
                    "销售金额(元)": item.get("sales", "0")
                })
            except Exception as e:
                print(f"解析数据条目时出错: {str(e)}")
                continue
        return structured_data

    def save_to_excel(self, data: List[Dict], filename: str, overwrite: bool = False) -> bool:
        if not data:
            print("没有数据可保存")
            return False
        if not filename.endswith('.xlsx'):
            filename += '.xlsx'

        # 自动创建保存目录
        os.makedirs(os.path.dirname(filename) or '.', exist_ok=True)

        if os.path.exists(filename) and not overwrite:
            base_name, ext = os.path.splitext(filename)
            counter = 1
            while True:
                new_filename = f"{base_name}_{counter}{ext}"
                if not os.path.exists(new_filename):
                    filename = new_filename
                    print(f"将保存为新文件: {filename}")
                    break
                counter += 1
        try:
            df = pd.DataFrame(data)
            df["开奖日期"] = pd.to_datetime(df["开奖日期"], errors="coerce").dt.strftime("%Y-%m-%d")
            df = df.dropna(subset=["开奖日期"])
            # 确保期号为数值类型,排序安全
            if "期号" in df.columns:
                df["期号"] = df["期号"].astype(str).str.strip()
                df["期号"] = df["期号"].replace(['nan', 'None', ''], '0')
                df["期号"] = pd.to_numeric(df["期号"], errors='coerce').fillna(0).astype(int)
            df = df.sort_values("开奖日期", ascending=False)
            with pd.ExcelWriter(filename, engine="xlsxwriter") as writer:
                df.to_excel(writer, index=False, sheet_name="双色球开奖记录")
                # 简单格式化(可根据需要添加)
            print(f"成功保存 {len(df)} 条数据到 {filename}")
            return True
        except Exception as e:
            print(f"保存Excel失败: {str(e)}")
            return False

    # ========== 增量更新相关方法 ==========
    def fetch_by_date_range(self, start_date: str, end_date: str) -> List[Dict]:
        all_data = []
        page = 1
        page_size = 50
        max_pages = 100
        while page <= max_pages:
            print(f"  正在获取第 {page} 页...")
            raw = self.get_ssq_data(page_no=page, page_size=page_size,
                                    startDate=start_date, endDate=end_date)
            if raw is None:
                break
            parsed = self.parse_data(raw)
            if not parsed:
                break
            all_data.extend(parsed)
            if len(parsed) < page_size:
                break
            page += 1
            time.sleep(random.uniform(0.5, 1.5))
        filtered = [d for d in all_data if start_date <= d["开奖日期"] <= end_date]
        return filtered

    def incremental_update(self, filename: str = "ssq_results.xlsx") -> bool:
        if not os.path.exists(filename):
            print("本地数据文件不存在,请先执行全量爬取。")
            return False
        try:
            old_df = pd.read_excel(filename)
        except Exception as e:
            print(f"读取现有文件失败: {e}")
            return False
        if old_df.empty:
            print("现有文件为空,执行全量爬取...")
            return self.full_fetch_and_save(filename)
        old_df['开奖日期'] = pd.to_datetime(old_df['开奖日期'])
        latest_date = old_df['开奖日期'].max()
        start_date = (latest_date + timedelta(days=1)).strftime("%Y-%m-%d")
        end_date = datetime.now().strftime("%Y-%m-%d")
        if start_date > end_date:
            print("本地数据已是最新,无需更新。")
            return True
        print(f"🔄 正在获取 {start_date} 至 {end_date} 的新数据...")
        new_data = self.fetch_by_date_range(start_date, end_date)
        if not new_data:
            print("未获取到新数据。")
            return True
        new_df = pd.DataFrame(new_data)
        combined_df = pd.concat([old_df, new_df], ignore_index=True)
        combined_df.drop_duplicates(subset=["期号"], keep="last", inplace=True)
        # 统一期号类型
        if "期号" in combined_df.columns:
            combined_df["期号"] = combined_df["期号"].astype(str).str.strip()
            combined_df["期号"] = combined_df["期号"].replace(['nan', 'None', ''], '0')
            combined_df["期号"] = pd.to_numeric(combined_df["期号"], errors='coerce').fillna(0).astype(int)
        combined_df["开奖日期"] = pd.to_datetime(combined_df["开奖日期"]).dt.strftime("%Y-%m-%d")
        combined_df = combined_df.sort_values("开奖日期", ascending=False)
        with pd.ExcelWriter(filename, engine="xlsxwriter") as writer:
            combined_df.to_excel(writer, index=False, sheet_name="双色球开奖记录")
        print(f"✅ 增量更新完成,新增 {len(new_data)} 条,总计 {len(combined_df)} 条。")
        return True

    def full_fetch_and_save(self, filename: str, pages: int = 10) -> bool:
        print("执行全量爬取...")
        all_data = []
        for page in range(1, pages + 1):
            raw = self.get_ssq_data(page_no=page, page_size=50)
            if raw is None:
                continue
            parsed = self.parse_data(raw)
            if parsed:
                all_data.extend(parsed)
            time.sleep(random.uniform(1, 2))
        if all_data:
            return self.save_to_excel(all_data, filename, overwrite=True)
        return False


def get_user_input():
    print("\n双色球开奖数据工具")
    print("=" * 40)
    print("请选择操作模式:")
    print("1. 全量爬取(覆盖或新建)")
    print("2. 增量更新(仅追加新数据)")
    print("3. 退出")
    while True:
        choice = input("请输入选择(1/2/3): ").strip()
        if choice in ("1", "2", "3"):
            return choice
        print("无效输入,请重新选择!")


# def main():
#     spider = SSQSpider()
# 
#     # 确保 data 目录存在(基于脚本所在目录)
#     base_dir = os.path.dirname(os.path.abspath(__file__))
#     data_dir = os.path.join(base_dir, "data")
#     os.makedirs(data_dir, exist_ok=True)
# 
#     default_filename = os.path.join(data_dir, "ssq.xlsx")
#     while True:
#         choice = get_user_input()
#         if choice == "3":
#             print("退出程序。")
#             break
#         elif choice == "1":
#             confirm = input("确认全量爬取?将覆盖现有文件 (y/n): ").strip().lower()
#             if confirm != 'y':
#                 continue
#             pages = int(input("请输入要爬取的页数(每页50条,建议10页以上): ") or "10")
#             spider.full_fetch_and_save(default_filename, pages)
#             break
#         elif choice == "2":
#             spider.incremental_update(default_filename)
#             break
def main():
    spider = SSQSpider()

    base_dir = os.path.dirname(os.path.abspath(__file__))
    data_dir = os.path.join(base_dir, "data")
    os.makedirs(data_dir, exist_ok=True)

    default_filename = os.path.join(data_dir, "ssq.xlsx")

    while True:
        choice = get_user_input()
        if choice == "3":
            print("退出程序。")
            break
        elif choice == "1":
            confirm = input("确认全量爬取?将覆盖现有文件 (y/n): ").strip().lower()
            if confirm != 'y':
                continue
            try:
                pages = int(input("请输入要爬取的页数(每页50条,建议10页以上): ") or "10")
            except ValueError:
                pages = 10
            spider.full_fetch_and_save(default_filename, pages)
            break
        elif choice == "2":
            spider.incremental_update(default_filename)
            break

if __name__ == "__main__":
    main()



评论(0)

查看评论列表

暂无评论


发表评论

温馨提示

本站设有会员签到功能,若你有闲暇时间,记得前往会员中心。值得高兴的是签到能获得一定数额的豌豆,豌豆的作用可作为站内虚拟交易。
This site features a member check-in function. If you have some free time, remember to visit the member center. The good news is that you'll receive a certain amount of peas for checking in, which can be used for virtual transactions within the site.
注意注意注意: 本站仅支持谷歌,QQ,网易(126.com,163.com),QQ邮箱注册,暂不支持其他邮箱注册
Note: This site only supports registration with Google, QQ, NetEase (126.com, 163.com), and QQ email addresses. Registration with other email addresses is not currently supported.