175 lines
5.2 KiB
Python
175 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
批量获取板块历史K线数据
|
|
"""
|
|
|
|
import urllib.request
|
|
import json
|
|
import sqlite3
|
|
import os
|
|
import time
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
# 清除代理
|
|
for v in ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY']:
|
|
os.environ.pop(v, None)
|
|
|
|
SCRIPT_DIR = Path(__file__).parent
|
|
DATA_DIR = SCRIPT_DIR / "data"
|
|
DB_FILE = DATA_DIR / "board_history.db"
|
|
|
|
|
|
def get_board_codes():
|
|
"""从数据库获取已保存的板块代码"""
|
|
conn = sqlite3.connect(DB_FILE)
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('SELECT DISTINCT board_code, board_name FROM board_data WHERE board_type="industry"')
|
|
industry = cursor.fetchall()
|
|
|
|
cursor.execute('SELECT DISTINCT board_code, board_name FROM board_data WHERE board_type="concept"')
|
|
concept = cursor.fetchall()
|
|
|
|
conn.close()
|
|
return industry, concept
|
|
|
|
|
|
def get_kline(code: str, days: int = 25):
|
|
"""获取板块历史K线"""
|
|
secid = f"90.{code}"
|
|
url = f"http://push2his.eastmoney.com/api/qt/stock/kline/get?secid={secid}&fields1=f1,f2,f3,f4,f5,f6&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61,f62,f63&klt=101&fqt=1&end=20500101&lmt={days}"
|
|
|
|
try:
|
|
req = urllib.request.Request(url, headers={
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
'Referer': 'http://quote.eastmoney.com/'
|
|
})
|
|
|
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
data = json.loads(resp.read().decode())
|
|
|
|
if data.get('data') and data['data'].get('klines'):
|
|
klines = data['data']['klines']
|
|
result = []
|
|
|
|
for kline in klines:
|
|
parts = kline.split(',')
|
|
# 格式: 日期,开盘,收盘,最高,最低,成交量,成交额,振幅,涨跌幅,涨跌额,换手率
|
|
result.append({
|
|
'date': parts[0],
|
|
'pct_change': float(parts[8]) if parts[8] != '-' else 0,
|
|
'amount': float(parts[6]) if parts[6] != '-' else 0,
|
|
})
|
|
|
|
return result
|
|
except Exception as e:
|
|
pass
|
|
|
|
return None
|
|
|
|
|
|
def save_to_db(board_type: str, code: str, name: str, klines: list):
|
|
"""保存到数据库"""
|
|
conn = sqlite3.connect(DB_FILE)
|
|
cursor = conn.cursor()
|
|
|
|
count = 0
|
|
for kline in klines:
|
|
try:
|
|
cursor.execute('''
|
|
INSERT OR REPLACE INTO board_data
|
|
(date, board_type, board_code, board_name, pct_change, main_flow, leader_name)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
''', (
|
|
kline['date'],
|
|
board_type,
|
|
code,
|
|
name,
|
|
kline['pct_change'],
|
|
kline['amount'] / 1e8,
|
|
''
|
|
))
|
|
count += 1
|
|
except:
|
|
continue
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
return count
|
|
|
|
|
|
def main():
|
|
print("\n📊 批量获取板块历史数据")
|
|
print("=" * 50)
|
|
|
|
industry, concept = get_board_codes()
|
|
print(f"\n行业板块: {len(industry)} 个")
|
|
print(f"概念板块: {len(concept)} 个")
|
|
|
|
# 获取行业板块历史
|
|
print("\n📡 获取行业板块历史数据...")
|
|
success = 0
|
|
for i, (code, name) in enumerate(industry, 1):
|
|
print(f" [{i}/{len(industry)}] {name}...", end=' ', flush=True)
|
|
|
|
klines = get_kline(code, 25)
|
|
if klines:
|
|
count = save_to_db('industry', code, name, klines)
|
|
print(f"✅ {count}条")
|
|
success += 1
|
|
else:
|
|
print("❌")
|
|
|
|
time.sleep(0.15)
|
|
|
|
print(f"\n行业板块完成: {success}/{len(industry)}")
|
|
|
|
# 获取概念板块历史
|
|
print("\n📡 获取概念板块历史数据...")
|
|
success = 0
|
|
for i, (code, name) in enumerate(concept, 1):
|
|
print(f" [{i}/{len(concept)}] {name}...", end=' ', flush=True)
|
|
|
|
klines = get_kline(code, 25)
|
|
if klines:
|
|
count = save_to_db('concept', code, name, klines)
|
|
print(f"✅ {count}条")
|
|
success += 1
|
|
else:
|
|
print("❌")
|
|
|
|
time.sleep(0.15)
|
|
|
|
print(f"\n概念板块完成: {success}/{len(concept)}")
|
|
|
|
# 统计
|
|
conn = sqlite3.connect(DB_FILE)
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute('SELECT COUNT(DISTINCT date) FROM board_data')
|
|
date_count = cursor.fetchone()[0]
|
|
|
|
cursor.execute('SELECT MIN(date), MAX(date) FROM board_data')
|
|
min_date, max_date = cursor.fetchone()
|
|
|
|
cursor.execute('SELECT COUNT(*) FROM board_data')
|
|
total = cursor.fetchone()[0]
|
|
|
|
cursor.execute('SELECT date, COUNT(*) FROM board_data GROUP BY date ORDER BY date')
|
|
dates = cursor.fetchall()
|
|
|
|
conn.close()
|
|
|
|
print(f"\n✅ 完成!")
|
|
print(f" 数据日期: {date_count} 天")
|
|
print(f" 日期范围: {min_date} ~ {max_date}")
|
|
print(f" 总记录: {total} 条")
|
|
|
|
print(f"\n 日期分布:")
|
|
for date, cnt in dates[-10:]:
|
|
print(f" {date}: {cnt} 条")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |