爬取双色球历史中奖数据

部署运行你感兴趣的模型镜像

利用python和deepseek生成了一段代码,用于从某网站分年度爬取双色球的历史中奖记录,保存到excel文档中。

# -*- coding: utf-8 -*-
"""
Created on Tue Oct 14 10:34:32 2025

@author: oldhen

本代码用于在https://datachart.500.com/ 爬取双色球中奖数据
实测后发现爬取所有记录会出错,所以最终采用只爬取一年的记录,如果要爬取所有记录可以采用多次执行的方式
用户需要输入某年份开始期数和结束期数,形如25001的数字
爬取后会自动保存到“双色球历史数据”表格中,并会自动去重以及按照“期数”降序排列
"""

import requests
from lxml import html
import pandas as pd
import os
from datetime import datetime
import time

def analyze_existing_data():
    """分析已存在的Excel文件中的年份数据"""
    filename = "双色球历史数据.xlsx"
    
    if not os.path.exists(filename):
        print("未找到现有数据文件: 双色球历史数据.xlsx")
        return None
    
    try:
        # 读取现有数据,确保期数为字符串类型
        existing_df = pd.read_excel(filename, engine='openpyxl', dtype={'期数': str})
        
        if '期数' not in existing_df.columns:
            print("现有数据文件中未找到'期数'列")
            return None
            
        # 提取年份(期数的前两位)
        years = []
        for issue in existing_df['期数']:
            if len(str(issue)) >= 2:
                year = str(issue)[:2]
                if year.isdigit():
                    years.append(year)
        
        # 去重并排序
        unique_years = sorted(set(years))
        
        if unique_years:
            print(f"\n现有数据文件中包含以下年份的数据: {', '.join(unique_years)}")
            print(f"总共有 {len(existing_df)} 条历史记录")
        else:
            print("现有数据文件中未找到有效的年份数据")
            
        return unique_years
        
    except Exception as e:
        print(f"读取现有数据文件时出错: {e}")
        return None

def get_user_input():
    """获取用户输入的起始和结束期数"""
    print("\n温馨提示:请输入形如24001的期数,前两位是年份,后3位是期数,不超过160")
    
    while True:
        try:
            start_num = input("请输入开始期数(如:24001):").strip()
            end_num = input("请输入结束期数(如:24160):").strip()
            
            # 验证输入格式
            if len(start_num) != 5 or len(end_num) != 5:
                print("错误:期数必须是5位数字!")
                continue
                
            start_int = int(start_num)
            end_int = int(end_num)
            
            if start_int > end_int:
                print("错误:开始期数不能大于结束期数!")
                continue
            
            if end_int % 1000 > 160:
                print("错误:后三位最大不超过160")
                continue
                
            return start_num, end_num
            
        except ValueError:
            print("错误:请输入有效的数字!")
        except KeyboardInterrupt:
            print("\n程序已退出")
            exit()

def crawl_ssq_data(start_num, end_num):
    """爬取双色球数据"""
    # 构造URL
    url = f"https://datachart.500.com/ssq/history/newinc/history.php?start={start_num}&end={end_num}"
    
    print(f"开始爬取数据,URL: {url}")
    
    try:
        # 设置请求头,模拟浏览器访问
        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',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
            'Accept-Encoding': 'gzip, deflate',
            'Connection': 'keep-alive',
            'Upgrade-Insecure-Requests': '1',
        }
        
        # 发送请求
        response = requests.get(url, headers=headers, timeout=10)
        response.encoding = 'gb2312'  # 网站编码为gb2312
        
        print(f"响应状态码: {response.status_code}")
        
        if response.status_code != 200:
            print(f"请求失败,状态码: {response.status_code}")
            return None
            
        # 解析HTML
        tree = html.fromstring(response.text)
        
        # 尝试多种选择器定位数据表格
        selectors = [
            '//table[@class="tbb"]//tr',
            '//table[contains(@class, "tbl")]//tr',
            '//table//tr',
            '//*[@id="tdata"]//tr',  # 尝试使用ID选择器
            '//tbody//tr'
        ]
        
        rows = []
        for selector in selectors:
            rows = tree.xpath(selector)
            if len(rows) > 10:  # 如果找到足够多的行,认为找到了正确的选择器
                print(f"使用选择器 '{selector}' 找到了 {len(rows)} 行数据")
                break
        
        if not rows or len(rows) <= 10:
            print("未找到数据行,尝试查找所有表格")
            tables = tree.xpath('//table')
            print(f"页面中共有 {len(tables)} 个表格")
            for i, table in enumerate(tables):
                table_rows = table.xpath('.//tr')
                print(f"表格 {i+1} 有 {len(table_rows)} 行")
                if len(table_rows) > 10:
                    rows = table_rows
                    print(f"使用表格 {i+1} 作为数据源")
                    break
        
        if not rows or len(rows) <= 10:
            print("未找到足够的数据行,请检查网页结构")
            return None
            
        data = []
        for i, row in enumerate(rows):
            try:
                # 跳过表头行
                if i == 0:
                    continue
                    
                # 获取期数
                issue = row.xpath('.//td[1]//text()')
                # 获取红球号码(6个)
                red_balls = []
                for j in range(2, 8):  # td[2]到td[7]
                    ball = row.xpath(f'.//td[{j}]//text()')
                    if ball:
                        red_balls.append(ball[0].strip())
                
                # 获取蓝球号码
                blue_ball = row.xpath('.//td[8]//text()')
                
                # 验证数据完整性
                if issue and len(red_balls) == 6 and blue_ball:
                    issue_text = issue[0].strip()
                    # 验证期数格式
                    if len(issue_text) == 5 and issue_text.isdigit():
                        # 将红球号码合并为字符串
                        red_balls_str = ','.join(red_balls)
                        
                        data.append({
                            '期数': issue_text,
                            '红球': red_balls_str,
                            '蓝球': blue_ball[0].strip()
                        })
                    else:
                        print(f"跳过无效期数: {issue_text}")
                else:
                    # 如果不是数据行,跳过
                    if i < 5:  # 只打印前几行的调试信息
                        print(f"第{i}行数据不完整: 期数={issue}, 红球={len(red_balls)}个, 蓝球={blue_ball}")
                    
            except Exception as e:
                print(f"解析第{i}行数据时出错: {e}")
                continue
                
        if not data:
            print("未解析到任何有效数据")
            return None
            
        print(f"成功爬取 {len(data)} 条数据")
        return data
        
    except requests.exceptions.RequestException as e:
        print(f"网络请求错误: {e}")
        return None
    except Exception as e:
        print(f"爬取过程中出现错误: {e}")
        return None

def save_to_excel(data, start_num, end_num):
    """保存数据到Excel文件,以追加形式保存并按期数降序排列"""
    if not data:
        print("没有数据可保存")
        return False
        
    try:
        # 生成文件名
        filename = "双色球历史数据.xlsx"
        
        # 创建新数据的DataFrame,确保期数为字符串类型
        new_df = pd.DataFrame(data)
        new_df['期数'] = new_df['期数'].astype(str)  # 确保期数为字符串
        
        # 按期数降序排列新数据
        new_df = new_df.sort_values('期数', ascending=False)
        
        # 检查文件是否已存在
        if os.path.exists(filename):
            # 读取现有数据,确保期数为字符串类型
            existing_df = pd.read_excel(filename, engine='openpyxl', dtype={'期数': str})
            
            # 合并数据(排除重复期数)
            # 首先找出新数据中不在现有数据中的期数
            existing_issues = set(existing_df['期数'].astype(str))
            new_issues = set(new_df['期数'].astype(str))
            
            # 找出需要添加的新期数
            issues_to_add = new_issues - existing_issues
            
            if issues_to_add:
                # 筛选出需要添加的数据
                df_to_add = new_df[new_df['期数'].astype(str).isin(issues_to_add)]
                
                # 合并数据
                combined_df = pd.concat([existing_df, df_to_add], ignore_index=True)
                
                # 按期数降序排列 - 转换为整数排序以确保正确顺序
                # 先将期数转换为整数进行排序,然后再转换回字符串
                combined_df['期数_int'] = combined_df['期数'].astype(int)
                combined_df = combined_df.sort_values('期数_int', ascending=False)
                combined_df = combined_df.drop('期数_int', axis=1)
                
                # 保存合并后的数据
                combined_df.to_excel(filename, index=False, engine='openpyxl')
                print(f"成功追加 {len(df_to_add)} 条新数据到: {filename}")
                print(f"文件位置: {os.path.abspath(filename)}")
                
                # 显示数据预览
                #print("\n最新数据预览:")
                #print(combined_df.head(10))
            else:
                print("没有新数据需要追加,所有数据已存在")
                # 仍然显示现有数据的预览
                #print("\n现有数据预览:")
                #print(existing_df.head(10))
        else:
            # 文件不存在,直接保存新数据
            new_df.to_excel(filename, index=False, engine='openpyxl')
            print(f"数据已成功保存到: {filename}")
            print(f"文件位置: {os.path.abspath(filename)}")
            
            # 显示数据预览
            #print("\n数据预览:")
            #print(new_df.head(10))
        
        return True
        
    except Exception as e:
        print(f"保存Excel文件时出错: {e}")
        import traceback
        traceback.print_exc()  # 打印详细错误信息
        return False

def main():
    """主函数"""
    print("=" * 50)
    print("双色球历史数据爬取程序")
    print("=" * 50)
    
    # 分析现有数据中的年份
    existing_years = analyze_existing_data()
    
    # 获取用户输入
    start_num, end_num = get_user_input()
    
    # 爬取数据
    data = crawl_ssq_data(start_num, end_num)
    
    if data:
        # 保存数据
        save_to_excel(data, start_num, end_num)
    else:
        print("数据爬取失败,请检查网络连接或网址是否正确")

if __name__ == "__main__":
    main()

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

爬取双色球历史数据是一个涉及网络请求、HTML解析以及存储结果的过程。为了帮助你了解如何实现这一任务,我们将逐步讲解整个过程,并提供一些关键点和技术栈的选择。 ### 准备工具和库 首先你需要安装必要的Python库来进行网页抓取及解析操作: 1. **requests**: 用于发起HTTP GET/POST 请求获取页面内容。 2. **BeautifulSoup4 或 lxml**: 提供方便的 HTML/XML 文档解析功能。 3. **pandas** (可选): 方便对结构化数据如表格进行处理并保存到文件中。 4. **re** : 正则表达式模块,有时候可以帮助提取特定格式的数据。 你可以通过 pip 安装上述所需的包: ```bash pip install requests beautifulsoup4 pandas ``` ### 编码实践指南 下面是具体的步骤说明: #### 第一步 - 获取目标网站URL 找到一个可靠的发布双色球开奖记录的官方网站,例如中国福彩中心官网等。确定好之后记下其网址链接。 #### 第二步 - 发送 HTTP 请求获得响应文本 利用 `requests.get()` 方法向指定 URL 发起GET请求得到包含所有期数信息在内的完整 HTML 内容。 ```python import requests url = 'http://example.com/ssq/history.html' # 替换为实际地址 response = requests.get(url) html_content = response.text if response.status_code == 200 else None ``` 注意检查状态码是否成功(通常是200)后再继续下一步。 #### 第三步 - 分析页面结构选取合适的标签选择器 打开浏览器开发者工具(F12),定位到想要提取的信息所在的节点路径,比如日期、红蓝号码等等。然后根据实际情况调整CSS Selectors或者XPath规则以便于后续准确无误地筛选所需元素。 #### 第四步 - 解析并抽取有用字段值 结合所选用的解析引擎(这里推荐 BeautifulSoup),按照先前选定的选择器从DOM树中挑出感兴趣的部分。 ```python from bs4 import BeautifulSoup if html_content is not None: soup = BeautifulSoup(html_content,'lxml') # 示例:假设有这样的表格行<tr>, 每一行代表一期彩票的结果. for row in soup.select('table tr'): cells = [td.get_text(strip=True) for td in row.find_all(['th','td'])] date = cells[0] # 假定第1列是开奖日 red_balls = ','.join(cells[1:7]) # 将6个红色球合并成字符串 blue_ball = cells[-1] # 最后一列为蓝色球 print(f"{date},{red_balls},{blue_ball}") ``` #### 第五步 - 存储采集后的数据 最后可以将整理好的信息存入CSV文件或其他形式持久化媒介中。 ```python import csv with open("ssq_results.csv", mode='w', newline='', encoding='utf8') as fobj: writer = csv.writer(fobj) writer.writerow(["Date","Red Balls","Blue Ball"]) ... # 继续上面循环里的打印逻辑,改为writerow()方法即可 print("Data has been successfully saved.") ``` 以上就是简单的爬虫流程概述。当然,在真实环境中还需要考虑到更多因素,包括但不限于反爬机制规避策略、异常情况下的重试逻辑设计等高级话题。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值