167 lines
4.4 KiB
Python
167 lines
4.4 KiB
Python
"""
|
|
福彩3D历史数据抓取脚本
|
|
数据来源:中国福利彩票官网 (cwl.gov.cn)
|
|
输出格式:JSON,字段 { period, date, hundreds, tens, ones }
|
|
"""
|
|
|
|
import requests
|
|
import json
|
|
import time
|
|
import os
|
|
import sys
|
|
|
|
# 输出路径
|
|
OUTPUT_FILE = os.path.join(os.path.dirname(__file__), '..', 'src', 'data', 'history.json')
|
|
|
|
# 福彩3D API
|
|
BASE_URL = 'https://www.cwl.gov.cn/cwl_admin/front/cwlkj/search/kjxx/findDrawNotice'
|
|
HEADERS = {
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
'Referer': 'https://www.cwl.gov.cn/',
|
|
}
|
|
|
|
|
|
def fetch_page(page_no, page_size=100):
|
|
"""获取一页数据"""
|
|
params = {
|
|
'name': '3d',
|
|
'issueCount': '',
|
|
'issueStart': '',
|
|
'issueEnd': '',
|
|
'dayStart': '',
|
|
'dayEnd': '',
|
|
'pageNo': page_no,
|
|
'pageSize': page_size,
|
|
'systemType': 'PC',
|
|
}
|
|
|
|
try:
|
|
resp = requests.get(BASE_URL, params=params, headers=HEADERS, timeout=30)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
return data
|
|
except requests.exceptions.RequestException as e:
|
|
print(f' [请求失败] 第 {page_no} 页: {e}')
|
|
return None
|
|
except json.JSONDecodeError as e:
|
|
print(f' [解析失败] 第 {page_no} 页: {e}')
|
|
return None
|
|
|
|
|
|
def parse_result(item):
|
|
"""解析单条开奖数据"""
|
|
# 开奖号码格式: "9,7,8" 或 "1 4 7"
|
|
code = item.get('red', '')
|
|
if not code:
|
|
return None
|
|
|
|
# 支持逗号或空格分隔
|
|
code = code.replace(',', ' ')
|
|
parts = code.strip().split()
|
|
if len(parts) != 3:
|
|
parts = list(code.replace(' ', '').strip())
|
|
|
|
try:
|
|
hundreds = int(parts[0])
|
|
tens = int(parts[1])
|
|
ones = int(parts[2])
|
|
except (ValueError, IndexError):
|
|
return None
|
|
|
|
# 日期格式 "2026-08-02(日)" -> "2026-08-02"
|
|
date_str = item.get('date', '')
|
|
date_str = date_str.split('(')[0]
|
|
|
|
return {
|
|
'period': item.get('code', ''), # 期号
|
|
'date': date_str, # 日期
|
|
'hundreds': hundreds,
|
|
'tens': tens,
|
|
'ones': ones,
|
|
}
|
|
|
|
|
|
def main():
|
|
print('福彩3D历史数据抓取')
|
|
print('=' * 50)
|
|
|
|
all_data = []
|
|
page_no = 1
|
|
max_consecutive_empty = 3 # 连续空页数上限
|
|
empty_count = 0
|
|
|
|
while True:
|
|
print(f'正在获取第 {page_no} 页...', end=' ')
|
|
|
|
data = fetch_page(page_no)
|
|
if not data:
|
|
empty_count += 1
|
|
if empty_count >= max_consecutive_empty:
|
|
print(f'\n连续 {max_consecutive_empty} 页失败,停止')
|
|
break
|
|
page_no += 1
|
|
time.sleep(1)
|
|
continue
|
|
|
|
empty_count = 0
|
|
items = data.get('result', [])
|
|
if not items:
|
|
print('无数据,抓取完成')
|
|
break
|
|
|
|
page_data = []
|
|
for item in items:
|
|
parsed = parse_result(item)
|
|
if parsed:
|
|
page_data.append(parsed)
|
|
|
|
all_data.extend(page_data)
|
|
print(f'获取 {len(page_data)} 条')
|
|
|
|
# 检查是否还有更多页
|
|
total_pages = data.get('pageNum', 0)
|
|
if page_no >= total_pages:
|
|
print(f'已获取全部 {total_pages} 页')
|
|
break
|
|
|
|
page_no += 1
|
|
time.sleep(0.5) # 避免请求过快
|
|
|
|
# 去重并排序(按日期升序)
|
|
seen = set()
|
|
unique_data = []
|
|
for d in all_data:
|
|
if d['period'] not in seen:
|
|
seen.add(d['period'])
|
|
unique_data.append(d)
|
|
|
|
unique_data.sort(key=lambda x: (x['date'], x['period']))
|
|
|
|
print(f'\n总计获取 {len(unique_data)} 条唯一数据')
|
|
|
|
if not unique_data:
|
|
print('未获取到任何数据,可能 API 有变动。')
|
|
print('可以尝试手动从 https://www.cwl.gov.cn/ 下载数据。')
|
|
return
|
|
|
|
# 写入文件
|
|
os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True)
|
|
json_str = json.dumps(unique_data, ensure_ascii=False, indent=2)
|
|
with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
|
|
f.write(json_str)
|
|
|
|
print(f'已保存到: {OUTPUT_FILE}')
|
|
|
|
# 摘要
|
|
first = unique_data[0]
|
|
last = unique_data[-1]
|
|
print(f'数据范围: {first["period"]} ({first["date"]}) ~ {last["period"]} ({last["date"]})')
|
|
|
|
# 构建后重新运行
|
|
print(f'\n数据已更新,重新构建项目即可生效:')
|
|
print(f' npm run build')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|