1. 项目概述:Python爬取同花顺股票数据的技术实现
去年帮私募朋友做量化策略时,需要批量获取技术指标数据。传统手工导出效率太低,最终用Python实现了自动化采集方案。这个项目核心是通过爬虫技术从同花顺获取结构化股票数据,并提取MACD、KDJ等关键指标,为量化分析提供数据支持。
同花顺作为主流金融数据平台,其网页版包含丰富的技术指标数据,但缺乏批量导出功能。通过分析页面请求,我们发现其数据接口采用Ajax动态加载,需要模拟浏览器行为才能获取完整数据。本文将详解从环境搭建到指标提取的全流程实现,包含三个关键阶段:数据采集层(请求模拟与反爬绕过)、数据处理层(字段清洗与计算)、数据应用层(指标可视化与策略回测)。
提示:本项目仅用于技术学习,实际使用需遵守同花顺的用户协议。高频访问可能导致IP被封禁,建议控制请求频率在5秒/次以上。
2. 技术方案设计与环境准备
2.1 核心工具选型
经过对比测试,最终技术栈组合如下:
- 请求库:Requests + selenium(主备双方案)
- Requests处理静态接口(效率高)
- selenium应对动态渲染(兼容性好)
- 解析库:BeautifulSoup4 + pyquery
- BS4用于HTML结构解析
- pyquery处理类jQuery选择器
- 数据处理:pandas + TA-Lib
- pandas进行数据清洗
- TA-Lib计算专业指标
- 反爬方案:随机UserAgent + 代理IP池
# 基础依赖安装 pip install requests selenium beautifulsoup4 pyquery pandas pip install TA-Lib # 需先安装Windows版二进制包2.2 同花顺接口分析
通过Chrome开发者工具(F12)分析网络请求,发现关键数据接口:
- 实时行情:
https://data.10jqka.com.cn/ - 历史K线:
https://d.10jqka.com.cn/v2/line/ - 指标数据:
https://basic.10jqka.com.cn/
接口特点:
- 需要携带
Referer和Cookie鉴权 - 分页参数采用
page和size控制 - 返回数据为JSON格式(需处理Unicode编码)
3. 核心爬取流程实现
3.1 请求头模拟实战
成功请求的关键在于头部信息配置。通过实际抓包,我们整理出必须包含的字段:
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Referer": "https://stock.10jqka.com.cn/", "Cookie": "v=xxxxxx;", # 需登录后获取 "X-Requested-With": "XMLHttpRequest" }实测发现同花顺对以下特征进行检测:
- 缺少
Referer直接返回403 - 非常规UserAgent会触发验证码
- 单IP高频访问会限流(建议配合代理)
3.2 数据解析与清洗
获取到的原始数据需要多层处理:
- HTML解编码:处理
&#x开头的Unicode字符 - 字段提取:用CSS选择器定位关键元素
- 类型转换:字符串转数值/日期类型
def parse_stock_data(html): soup = BeautifulSoup(html, 'lxml') data = [] for tr in soup.select('tbody tr'): item = { 'code': tr.select_one('.stockCode').text, 'name': tr.select_one('.stockName').text, 'price': float(tr.select_one('.price').text), 'change': tr.select_one('.change').text } data.append(item) return pd.DataFrame(data)3.3 技术指标计算方案
使用TA-Lib库计算专业指标时需注意参数设置:
| 指标类型 | 函数名 | 典型参数 | 计算要点 |
|---|---|---|---|
| MACD | talib.MACD | fastperiod=12 | 需输入close价格序列 |
| KDJ | talib.STOCH | [9,3,3] | 需要high/low/close |
| RSI | talib.RSI | timeperiod=14 | 单价格序列输入 |
| BOLL | talib.BBANDS | 20 | 返回上中下三条轨道线 |
踩坑记录:TA-Lib要求输入值为numpy数组,需提前用
df['close'].values转换
4. 完整实现代码解析
4.1 主爬虫类设计
class ThsSpider: def __init__(self): self.session = requests.Session() self.session.headers.update(base_headers) def get_stock_list(self, page=1): url = f"https://data.10jqka.com.cn/financial/yjyg/order/desc/page/{page}/" try: resp = self.session.get(url, timeout=10) resp.raise_for_status() return self.parse_list(resp.text) except Exception as e: print(f"请求失败: {str(e)}") return None def parse_list(self, html): # 实现解析逻辑 pass4.2 数据持久化方案
推荐两种存储方式:
- CSV文件(适合小型数据集)
df.to_csv('stock_data.csv', index=False, encoding='utf_8_sig') - MySQL数据库(推荐生产环境使用)
from sqlalchemy import create_engine engine = create_engine('mysql://user:pass@host/db') df.to_sql('stock_data', engine, if_exists='append')
4.3 定时任务配置
使用APScheduler实现自动化采集:
from apscheduler.schedulers.blocking import BlockingScheduler sched = BlockingScheduler() @sched.scheduled_job('cron', hour=15, minute=30) # 收盘后运行 def daily_job(): spider = ThsSpider() spider.get_stock_list() sched.start()5. 常见问题与优化方案
5.1 反爬应对策略
根据实测经验整理的反爬解决方案:
| 现象 | 解决方案 | 实现示例 |
|---|---|---|
| 返回验证码 | 降低频率+更换UA | headers['User-Agent'] = random.choice(ua_list) |
| IP被封禁 | 使用代理IP轮询 | proxies = {"https": "http://ip:port"} |
| 数据加载不全 | 改用selenium渲染 | driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") |
| 接口返回空数据 | 检查Cookie有效期 | 重新登录更新Cookie |
5.2 性能优化技巧
- 异步请求加速:改用aiohttp实现并发
async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return await resp.text() - 缓存复用机制:对基础数据本地缓存
- 增量采集策略:记录最后更新时间戳
5.3 数据质量校验
建议增加的校验规则:
def validate_data(df): # 检查空值率 if df.isnull().mean().max() > 0.3: raise ValueError("数据缺失严重") # 检查价格合理性 if (df['price'] <= 0).any(): raise ValueError("存在异常价格")6. 技术指标应用实例
6.1 MACD策略信号生成
def generate_signals(df): df['macd'], df['signal'], _ = talib.MACD(df['close']) df['position'] = np.where(df['macd'] > df['signal'], 1, -1) return df6.2 可视化分析
使用matplotlib绘制指标曲线:
plt.figure(figsize=(12,6)) plt.plot(df['date'], df['close'], label='Close') plt.plot(df['date'], df['upper'], 'r--', label='Upper Band') plt.legend() plt.show()在项目落地过程中,发现同花顺的移动平均线计算方式与TA-Lib存在差异。经过比对,确认是同花顺使用了前复权价格计算,而我们的原始数据未复权。解决方案是在计算前先对价格序列进行复权处理,保证指标一致性。