利用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()
4526

被折叠的 条评论
为什么被折叠?



