量化交易backtrader实践(一)_数据获取篇(3)_爬取数据

embedded/2024/9/23 1:12:34/

这一节实践其实是在上一节之前进行的,背景原因是因为tushare.pro的积分不够高,当时还没有接触到使用akshare等其他接口,因此对于全股票列表用的是去网页上爬的方式获得的,也就借此机会,再复习了一遍爬虫的相关知识。

这一节主要是关于爬虫的一些实践的记录,也可以获取到可以交给backtrader进行回测的一系列的数据,但相对而言麻烦了一些,没有兴趣可以跳过,完全不影响backtrader的实践。

python爬虫基础

01_python爬虫基础内容总结

当前比较有效的学习方式就是有问题问AI,我们在几个AI助手里都输入:“请帮我快速总结 python爬虫基础,以列表的形式显示” 这样的提问,然后获取一段自己觉得接受程度最高的内容:

  • 请求模块

    • requests:发送HTTP请求获取网页内容。
    • http.client:Python标准库中用于HTTP请求的模块。
  • 解析工具

    • BeautifulSoup:解析HTML或XML文档。
    • lxml:更高效的HTML/XML解析库。
    • re(正则表达式):文本模式匹配和提取数据。
  • 处理JavaScript

    • Selenium:控制浏览器执行JavaScript渲染后的页面抓取。
    • Pyppeteer 或 Puppeteer:无头浏览器操作,异步JavaScript渲染页面。
  • 数据存储

    • 文件写入:使用open()保存到本地文件。
    • 数据库:如SQLite, MySQL等数据库存储。
  • 遵守规则

    • robots.txt:检查网站是否允许爬虫访问特定URL。
    • 尊重User-Agent:设置合适的请求头。
  • 错误处理

    • 异常捕捉:处理网络异常、解析错误等。
    • 重试机制:网络不稳定时自动重试。
  • 效率提升

    • 多线程/多进程:并发请求提高爬取速度。
    • asyncio:异步IO,适用于高并发场景。
  • 合法性与道德

    • 遵守法律法规,避免侵犯隐私或版权。
    • 控制爬取频率,减轻服务器负担。
  • 调试与测试

    • 使用开发者工具检查网络请求。
    • 单元测试确保代码质量。
  • 框架支持

    • Scrapy:强大的爬虫框架,适合大规模项目。

 接着,就把requests库的典型应用复习一遍。

02_爬取翻译的实践

001_单个单词的翻译

这是一个典型的python爬虫的简便应用,它通过requests库模拟了我们在浏览器中的操作,比如说查询dog的中文翻译,我们可以通过浏览器自带的“Inspect / 审查”功能观察到它的信息包括headers,response,通过preview查看就明白网页上显示的就是data里前四条。

使用requests的代码如下

python"># 0 导包
import requests
import json# 1. url与data
headers = {'User-Agent':'Mozilla/5.0 xxxxxxxxxxxx'
}post_url = 'https://fanyi.baidu.com/sug'data = { 'kw':'dog', }# 2. 发起请求
response = requests.post(url = post_url, data = data, headers = headers)dic_obj = response.json()
dic_obj---------------------
{'errno': 0,'data': [{'k': 'dog', 'v': 'n. 狗; 蹩脚货; 丑女人; 卑鄙小人 v. 困扰; 跟踪'},{'k': 'DOG', 'v': 'abbr. Data Output Gate 数据输出门'},{'k': 'doge', 'v': 'n. 共和国总督'},{'k': 'dogm', 'v': 'abbr. dogmatic 教条的; 独断的; dogmatism 教条主义; dogmatist'},{'k': 'Dogo', 'v': '[地名] [马里、尼日尔、乍得] 多戈; [地名] [韩国] 道高'}],'logid': 2057633931}

002_多个单词的翻译与保存

假设我们有一个英文单词的列表,那么就需要循环的每次取一个单词出来,通过requests把中文翻译得到,并放到中文的列表里。

python">all_data = ['dog', 'wolf', 'rabbit', 'sheep', 'sleep']import requests
def get_translation(list1):url = 'https://fanyi.baidu.com/sug'headers = {'User-Agent':'Mozilla/5.0 ......'
}ret_data = []for x in list1:word1 = xdata1 = {'kw': word1}response = requests.post(url = url, data = data1, headers = headers)dic_obj = response.json()ret_data.append(dic_obj['data'][0]['v'])return ret_dataret = get_translation(all_data)
ret------------------------
['n. 狗; 蹩脚货; 丑女人; 卑鄙小人 v. 困扰; 跟踪','n. 狼; 贪婪的人; 有害的幼虫 vt. 狼吞虎咽','n. 兔子,野兔; 兔子皮毛; 兔子肉; 〈俚〉新手,弱手 vi. 猎兔(通常作 go rabbit','n. 羊,绵羊; 胆小鬼; 易受人摆布的人; 缺乏主见或轻易盲从的人','vi.& link-v. 睡,睡觉 vi. 睡,睡觉; 睡眠状态 vt. 为…提供床位; 提供住宿;']

然后可以用Pandas的DataFrame把它们保存下来

python">	En	    CH
No.		
1	dog	n. 狗; 蹩脚货; 丑女人; 卑鄙小人 v. 困扰; 跟踪
2	wolf	n. 狼; 贪婪的人; 有害的幼虫 vt. 狼吞虎咽
3	rabbit	n. 兔子,野兔; 兔子皮毛; 兔子肉; 〈俚〉新手,弱手 vi. 猎兔(通常作 go ra...
4	sheep	n. 羊,绵羊; 胆小鬼; 易受人摆布的人; 缺乏主见或轻易盲从的人
5	sleep	vi.& link-v. 睡,睡觉 vi. 睡,睡觉; 睡眠状态 vt. 为…提供床位; 提...

003_get与post

还是交给AI来完成:

在Web开发中,POSTGET 是两种常用的 HTTP 请求方法,它们有以下主要区别:

  • 用途

    • GET:通常用于从服务器检索信息。数据会附加在URL中作为查询参数。
    • POST:用于向服务器发送数据,通常用于表单提交。数据不会显示在URL中。
  • 安全性

    • GET:不太安全,因为数据暴露在URL中,容易被缓存或记录在浏览历史中。
    • POST:相对更安全,数据通过请求体发送,不会出现在URL中。
  • 数据大小限制

    • GET:数据长度受限于URL长度限制(不同浏览器有不同的限制)。
    • POST:理论上没有数据大小限制。
  • 可缓存性

    • GET:请求可以被浏览器缓存。
    • POST:通常不可缓存。
  • 幂等性

    • GET:多次执行同一个GET请求效果等同于一次执行。
    • POST:多次执行可能有副作用,例如多次提交表单可能导致多次记录创建。
python"># get 示例
import requestsurl = 'https://api.example.com/data'
params = {'key1': 'value1', 'key2': 'value2'}response = requests.get(url, params=params)print(response.url)  # 输出: https://api.example.com/data?key1=value1&key2=value2
print(response.text)# post 示例
import requestsurl = 'https://api.example.com/data'
data = {'key1': 'value1', 'key2': 'value2'}response = requests.post(url, data=data)print(response.status_code)
print(response.text)

从代码上,简单的区别是get要用到params,而post用到data。

爬取股票数据

原本我们进行backtrader回测需要好几个不同的数据,例如全股票列表,ETF列表,单支股票的历史行情......,前面也说了在有akshare或其他接口的情况下,不需要花费太多的时间在每个需要的数据上进行爬取,这里简单的实践2个页面。

01_全股票列表

全股票列表,可以从各种不同的方向来获取,比如说选股器,又比如说行情中心的沪深京A股

第一种方式从选股器进,选择全部A股,每页显示50个,共108页,选择根据股票代码排序。

第二种方式从行情中心进,选择沪深京A股,每页显示20个,共283页。

以第一种方式为例,首先在页面上右键--审查,然后在右侧“网络”--Fetch/XHR中,找到网页上表格对应的文件,在预览里看到数据和网页上一一对应。

然后,回到标头(header)页,复制URL。这里可以看到请求方法是GET,也可以看到每页显示50个(ps=50),当前第1页(p=1)。

直接在浏览器的地址栏里粘贴这段url并回车,就会得到下面这样的页面:

{"version": null,"result": {"nextpage": true,"currentpage": 1,"data": [{"SECUCODE": "000001.SZ","SECURITY_CODE": "000001","SECURITY_NAME_ABBR": "平安银行","NEW_PRICE": 9.65,"CHANGE_RATE": -2.53,"VOLUME_RATIO": 1.17,"HIGH_PRICE": 9.88,"LOW_PRICE": 9.63,"PRE_CLOSE_PRICE": 9.9,"VOLUME": 1108727,"DEAL_AMOUNT": 1078602467.68,"TURNOVERRATE": 0.57,"MARKET": "深交所主板","MAX_TRADE_DATE": "2024-09-11"},{"SECUCODE": "000002.SZ","SECURITY_CODE": "000002","SECURITY_NAME_ABBR": "万科A","NEW_PRICE": 6.31,"CHANGE_RATE": 0.16,"VOLUME_RATIO": 0.55,"HIGH_PRICE": 6.38,"LOW_PRICE": 6.24,"PRE_CLOSE_PRICE": 6.3,"VOLUME": 604082,"DEAL_AMOUNT": 381296490.68,"TURNOVERRATE": 0.62,"MARKET": "深交所主板","MAX_TRADE_DATE": "2024-09-11"},{"SECUCODE": "000004.SZ","SECURITY_CODE": "000004","SECURITY_NAME_ABBR": "国华网安",

由上,这个URL就能得到50个股票的列表:

python">import requests#1 指定URL
url = "https://data.eastmoney.com/dataapi/xuangu/list?st=SECURITY_CODE&sr=1&ps=50&p=1&sty=SECUCODE%2CSECURITY_CODE%2CSECURITY_NAME_ABBR%2CNEW_PRICE%2CCHANGE_RATE%2CVOLUME_RATIO%2CHIGH_PRICE%2CLOW_PRICE%2CPRE_CLOSE_PRICE%2CVOLUME%2CDEAL_AMOUNT%2CTURNOVERRATE%2CMARKET&filter=(MARKET+in+(%22%E4%B8%8A%E4%BA%A4%E6%89%80%E4%B8%BB%E6%9D%BF%22%2C%22%E6%B7%B1%E4%BA%A4%E6%89%80%E4%B8%BB%E6%9D%BF%22%2C%22%E6%B7%B1%E4%BA%A4%E6%89%80%E5%88%9B%E4%B8%9A%E6%9D%BF%22%2C%22%E4%B8%8A%E4%BA%A4%E6%89%80%E7%A7%91%E5%88%9B%E6%9D%BF%22%2C%22%E4%B8%8A%E4%BA%A4%E6%89%80%E9%A3%8E%E9%99%A9%E8%AD%A6%E7%A4%BA%E6%9D%BF%22%2C%22%E6%B7%B1%E4%BA%A4%E6%89%80%E9%A3%8E%E9%99%A9%E8%AD%A6%E7%A4%BA%E6%9D%BF%22%2C%22%E5%8C%97%E4%BA%AC%E8%AF%81%E5%88%B8%E4%BA%A4%E6%98%93%E6%89%80%22))&source=SELECT_SECURITIES&client=WEB"#2 发起请求 + #3 获取响应数据
response = requests.get(url=url)
dic1 = response.json()import pandas as pd
import jsond3 = dic1['result']['data']df = pd.DataFrame(d3)
df.iloc[:,:5]--------------------------------SECUCODE	SECURITY_CODE	SECURITY_NAME_ABBR	NEW_PRICE	CHANGE_RATE
0	000001.SZ	000001	平安银行	9.65	-2.53
1	000002.SZ	000002	万科A	6.31	0.16
2	000004.SZ	000004	国华网安	11.85	-2.63
3	000006.SZ	000006	深振业A	4.11	-1.44
4	000007.SZ	000007	全新好	4.64	-2.52
5	000008.SZ	000008	神州高铁	1.99	-5.24
6	000009.SZ	000009	中国宝安	7.64	0.00

循环108次,每次把p=x的值加1,这样就能得到全股票列表了。

02_添加拼音首字母缩写

这个动作在第2节里已经完整的实践过,这里就不重复了。

  • 量化交易backtrader实践(一)_数据获取篇(2)_tushare与akshare-CSDN博客

03_个股历史行情数据

001_获取历史行情数据的URL

这一段在之前的实践中并不存在,即使之前用tushare.pro不够积分获取全股票列表,但仍然可以请求日线也就是历史行情数据的,所以这一项也从网页上爬取并没有太多的意义。

不过既然咱们这一节本身就属于一个番外的内容,索性就在爬虫的道路上多走一步。而且,这里跟前面我们讲的还都不太一样。

这里的历史行情数据属于动态加载,但它不是Fetch/XHR里(即不是json数据),而是在JS文件中,这种数据要找到它都会比较的困难,我每次都是一个一个找。

在通过预览找到了对应的JS文件之后,再通过标头(header)得到URL,然后在浏览器的地址栏粘贴这个URL,就得到如下图所示,从2024/3/20到2024/9/11的Klines了。

这里显示的时间段只有120天(半年),这点数据放到backtrader里回测似乎有点少,想要增加数据的量,可以直接从负载页看参数:

那么在地址栏中的这部分lmt=120改为240就可以得到差不多一年的数据了,需要更多的数据那么可以改为400,600等。

于是我们可以写如下代码:

python">import requests
url1 = "http://11.push2his.eastmoney.com/api/qt/stock/kline/get?
cb=jQuery35108492299702581476_1726063980126&secid=1.601600
&ut=fa5fd1943c7b386f172d6893dbfba10b&fields1=f1%2Cf2%2Cf3%2Cf4%2Cf5%2Cf6
&fields2=f51%2Cf52%2Cf53%2Cf54%2Cf55%2Cf56%2Cf57%2Cf58%2Cf59%2Cf60%2Cf61
&klt=101&fqt=1&end=20500101&lmt=400&_=1726063980130"#2 发起请求 + #3 获取响应数据
response = requests.get(url=url1)
page_text = response.text
page_text------------------------
'jQuery35108492299702581476_1726063980126({"rc":0,"rt":17,"svr":177617930,"lt":1,"full":0,"dlmkts":"",
"data":{"code":"601600","market":1,"name":"中国铝业","decimal":2,"dktotal":4081,"preKPrice":4.89,
"klines":["2023-01-19,4.87,4.91,4.91,4.85,704244,352357028.00,1.23,0.41,0.02,0.54",
"2023-01-20,4.92,5.08,5.10,4.89,1765222,906405629.00,4.28,3.46,0.17,1.35",
......
"2024-09-10,6.35,6.30,6.38,6.20,975358,613191159.00,2.84,-0.63,-0.04,0.74",
"2024-09-11,6.28,6.25,6.30,6.13,1000724,620802872.00,2.70,-0.79,-0.05,0.76"]}});'

到这里,我们遇到一个问题,即文本是以'jQuery开头的,并不是一个json的数据,我们需要怎么做?

002_从JS代码中获取数据

根据多年来的习惯,我还是会先思考自己能采用什么方式,这些方式里有没有简洁高效的,然后才会去问AI怎么解决。

自己的思考是最开始括号"("和最后")"里的应该是json数据,那么用.find()找到这两个索引,用切片的方式就可以了

python">import jsonistart=page_text.find("(")
istop =page_text.rfind(")")page_data = page_text[istart+1:istop]json_data = json.loads(page_data)

然后还是问了AI: requests请求得到的数据格式如下,怎么直接转换成json的数据? 得到的代码如下

python">import json# 假设这是你的原始响应文本
response_text = page_text# 移除包裹在JSON外面的JavaScript函数调用
trimmed_response = response_text.strip().split('(', 1)[1].rsplit(')', 1)[0]# 将JSON字符串转换为Python字典
json_data = json.loads(trimmed_response)
json_data--------------------------
{'rc': 0,'rt': 17,'svr': 177617930,'lt': 1,'full': 0,'dlmkts': '','data': {'code': '601600','market': 1,'name': '中国铝业','decimal': 2,'dktotal': 4081,'preKPrice': 4.89,'klines': ['2023-01-19,4.87,4.91,4.91,4.85,704244,352357028.00,1.23,0.41,0.02,0.54','2023-01-20,4.92,5.08,5.10,4.89,1765222,906405629.00,4.28,3.46,0.17,1.35','2023-01-30,5.17,5.14,5.20,5.06,1677315,879133051.00,2.76,1.18,0.06,1.28',......'2024-09-10,6.35,6.30,6.38,6.20,975358,613191159.00,2.84,-0.63,-0.04,0.74','2024-09-11,6.28,6.25,6.30,6.13,1000724,620802872.00,2.70,-0.79,-0.05,0.76']}}

看了AI给出的代码,也就是巧妙用split两次得到中间的部分,又一次增长了知识。

003_提取数据到DataFrame

得到json数据后之后,需要把'data'里面的"klines"的值进一步提取,然后里面的数据用逗号进行分割,再转成DataFrame类型就可以了。

python">klines1 = json_data['data']['klines']  # 提取kline的数据listlisttmp = []
for x in klines1:list1 = x.split(',')listtmp.append(list1)import pandas as pd
columns1 = ['date','open','high','low','close','volume','amount','x','x2','x3','x4']
df_klines = pd.DataFrame(listtmp, columns=columns1)
df_klines.iloc[:,:7]-------------------------------date	    open	high	low	    close	volume	amount
0	2023-01-19	4.87	4.91	4.91	4.85	704244	352357028.00
1	2023-01-20	4.92	5.08	5.10	4.89	1765222	906405629.00
2	2023-01-30	5.17	5.14	5.20	5.06	1677315	879133051.00
3	2023-01-31	5.12	5.17	5.23	5.07	1141659	603112241.00
4	2023-02-01	5.22	5.42	5.52	5.20	2328566	1284011402.00
...	...	...	...	...	...	...	...
395	2024-09-05	6.62	6.46	6.68	6.38	1335513	866917546.00
396	2024-09-06	6.50	6.46	6.52	6.43	686603	444664992.00
397	2024-09-09	6.38	6.34	6.41	6.24	1130286	712609591.00
398	2024-09-10	6.35	6.30	6.38	6.20	975358	613191159.00
399	2024-09-11	6.28	6.25	6.30	6.13	1000724	620802872.00
400 rows × 7 columns

这个DataFrame的数据再进行交给backtrader前的格式处理,就可以把数据送到backtrader里进行回测了。

本节小结

这一节是由于前面某个时间段里没有找到好用的金融数据接口包,又想着把python爬虫学到的一点皮毛来实践一下而出现的。通过这一部分的实践,可以让我们不依赖金融数据接口包的情况下,获取需要的数据并用于backtrader回测。

同时经过与金融数据接口包获取数据的对比,发现了自己对股票市场很多东西理解深度还很低,有很多需要改进的地方,虽然对于整个backtrader实践流程没有太多的帮助,但对交易系统的理解和感受又有了一定的进步。


http://www.ppmy.cn/embedded/111018.html

相关文章

XSS 漏洞检测与利用全解析:守护网络安全的关键洞察

在网络安全领域,跨站脚本攻击(XSS)是一种常见的安全漏洞。XSS 漏洞可以让攻击者在受害者的浏览器中执行恶意脚本,从而窃取用户的敏感信息、篡改页面内容或者进行其他恶意操作。本文将介绍 XSS 漏洞的检测和利用方法。 一、XSS 漏洞…

Spring-di基本使用

SpringDI 1 基础环境准备 流程如下 1.在自己的工程中建一个module用于SpringDi注入 2.导入spring相关的依赖 <dependencies><!--导入spring-context依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-cont…

小程序的右侧抽屉开关动画手写效果

<template><view><button click"openDrawer">打开抽屉</button><view v-if"showDrawer" class"drawer" :style"{ backgroundColor: bgColor }" click"closeDrawer"><view class"draw…

卷积神经网络与小型全连接网络在MNIST数据集上的对比

卷积神经网络&#xff08;CNN&#xff09; 深度卷积神经网络中&#xff0c;有如下特性 很多层&#xff08;Compositionality&#xff0c;组合性&#xff09;: 深度卷积神经网络通常由多层卷积和非线性激活函数组成。这种多层结构使得网络能够逐步提取和组合低层次的特征&…

【解决内存泄漏的问题】 Qt 框架中的父子对象关系会自动管理内存,父对象会在其销毁时自动销毁所有子对象。

修改前的代码 这段代码可能会出现内存泄漏问题&#xff0c;主要原因是构造函数中创建的 LoginDialog 和 RegisterDialog 对象未在合适的地方被正确释放。具体分析如下&#xff1a; 1. 构造函数中的问题 _login_dlg new LoginDialog(); setCentralWidget(_login_dlg); _login…

GO 反射

文章目录 基本概念与语法1. **获取类型和值**2. **反射修改值**3. **检查类型种类&#xff08;Kind&#xff09;** 反射的高级使用场景1. **结构体字段操作**2. **调用函数**3. **动态创建和修改切片、映射**4. **JSON 序列化/反序列化**5. **类型安全的通用函数**6. **动态生成…

开源的 Kafka 管理平台

来源&#xff1a;github.com/provectus/kafka-ui Apache Kafka UI 是一个免费的开源 Web UI&#xff0c;用于监控和管理 Apache Kafka 集群&#xff0c;可方便地查看 Kafka Brokers、Topics、消息、Consumer 等情况&#xff0c;支持多集群管理、性能监控、访问控制等功能。 1 …

Python实现群智能算法

博客目录 引言 什么是群智能算法&#xff1f;群智能算法的应用场景群智能算法的基本思想 群智能算法的原理 粒子群优化&#xff08;Particle Swarm Optimization, PSO&#xff09;人工蜂群算法&#xff08;Artificial Bee Colony, ABC&#xff09;萤火虫算法&#xff08;Firef…