【爬虫】爬取A股数据写入数据库(二)

在这里插入图片描述
前几天有写过一篇 【爬虫】爬取A股数据写入数据库(一),现在继续完善下,将已有数据通过ORM形式批量写入数据库。
2024/05,本文主要内容如下:

  1. 对东方财富官网进行分析,并作数据爬取,使用python,使用pip install requests 模拟http数据请求,获取数据。
  2. 将爬取的数据写入通过 sqlalchemy ORM 写入 sqlite数据库。
  3. 记录爬取股票的基本信息,如果库中已存在某个股票代码,则进行更新。
  4. 后续计划:会不断完善,最终目标是做出一个简单的股票查看客户端。
  5. 本系列所有源码均无偿分享,仅作交流无其他,供大家参考。
    python依赖环境如下:
conda create --name xuan_gu python=3.9 --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda remove --name xuan_gu --all
conda activate xuan_gu

#pip install PyQt5==5.15.10 -i https://pypi.tuna.tsinghua.edu.cn/simple
#pip install pyqtgraph==0.13.6 -i https://pypi.tuna.tsinghua.edu.cn/simple
#python -m pyqtgraph.examples 查看图形化的pyqtgraph示例

pip install requests==2.31.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pandas==2.2.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install jsonpath==0.8.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install sqlalchemy==2.0.30 -i https://pypi.tuna.tsinghua.edu.cn/simple

1. 对东方财富官网的分析

东方财富网页地址:https://data.eastmoney.com/gdhs/
通过分析网页,发现https://datacenter-web.eastmoney.com/api/data/v1/get?请求后面带着一些参数即可以获取到相应数据,我们不断调试,模拟这类请求即可。分析过程如下图所示,F12调出调试框,不断尝试:
| ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/dcd5a9558b7a49e29f834d9fa0cebad4.png
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 爬取数据代码逻辑

如下即爬取数据的可运行代码,复制后直接能跑:

import pandas as pd
from typing import List
import requests

class CustomedSession(requests.Session):
    def request(self, *args, **kwargs):
        kwargs.setdefault('timeout', 60)
        return super(CustomedSession, self).request(*args, **kwargs)
session = CustomedSession()
adapter = requests.adapters.HTTPAdapter(pool_connections = 50, pool_maxsize = 50, max_retries = 5)
session.mount('http://', adapter)
session.mount('https://', adapter)

# 请求地址
QEURY_URL = 'http://datacenter-web.eastmoney.com/api/data/v1/get'
# HTTP 请求头
EASTMONEY_REQUEST_HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko',
    'Accept': '*/*',
    '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',
    # 'Referer': 'http://quote.eastmoney.com/center/gridlist.html',
}

# 请求返回值过滤
RESULT_FIELDS = {
    'SECURITY_CODE': '股票代码',
    'SECURITY_NAME_ABBR': '股票名称',
    'END_DATE': '本次股东户数统计截止日',
    'PRE_END_DATE': '上次股东户数统计截止日',
    'INTERVAL_CHRATE': '区间涨跌幅',
    'AVG_MARKET_CAP': '户均持股市值',
    'AVG_HOLD_NUM': '户均持股数量',
    'TOTAL_MARKET_CAP': '总市值',
    'TOTAL_A_SHARES': '总股本',
    'HOLD_NOTICE_DATE': '公告日期',
    'HOLDER_NUM': '本次股东户数',
    'PRE_HOLDER_NUM': '上次股东户数',
    'HOLDER_NUM_CHANGE': '股东户数增减',
    'HOLDER_NUM_RATIO': '股东户数较上期变化百分比',  
    'f2': '最新价',
    'f3': '涨跌幅百分比',
}

"""
获取沪深A股市场最新公开的股东数目变化情况: 当作获取所有股票
"""
def get_latest_holder_number() -> pd.DataFrame:
    # 请求页码
    QEURY_PAGE = 1
    PAGE_COUNT = 100
    dfs: List[pd.DataFrame] = []
    while 1:
        if QEURY_PAGE > PAGE_COUNT:
            break
        # 请求参数
        QUERY_PARAM = [
            ('sortColumns', 'HOLD_NOTICE_DATE,SECURITY_CODE'),
            ('sortTypes', '-1,-1'),
            ('pageSize', 500),
            ('pageNumber', QEURY_PAGE),
            ('columns', 'SECURITY_CODE,SECURITY_NAME_ABBR,END_DATE,INTERVAL_CHRATE,AVG_MARKET_CAP,AVG_HOLD_NUM,TOTAL_MARKET_CAP,TOTAL_A_SHARES,HOLD_NOTICE_DATE,HOLDER_NUM,PRE_HOLDER_NUM,HOLDER_NUM_CHANGE,HOLDER_NUM_RATIO,END_DATE,PRE_END_DATE',),
            ('quoteColumns', 'f2,f3'),
            ('source', 'WEB'),
            ('client', 'WEB'),
            ('reportName', 'RPT_HOLDERNUMLATEST'),
        ]
        params = tuple(QUERY_PARAM)
        response = session.get(QEURY_URL, headers=EASTMONEY_REQUEST_HEADERS, params=params)
        resultJson = response.json()
        PAGE_COUNT = resultJson.get('result').get('pages')
        print('json len=', len(str(resultJson)), 'page count=', PAGE_COUNT, 'page number=', QEURY_PAGE)
        if PAGE_COUNT is None:
            break
        data = resultJson.get('result').get('data')
        if data is None:
            break
        df = pd.DataFrame(data)
        df = df.rename(columns=RESULT_FIELDS)[RESULT_FIELDS.values()]
        dfs.append(df)
        QEURY_PAGE += 1
    if len(dfs) == 0:
        df = pd.DataFrame(columns=RESULT_FIELDS.values())
        return df
    df = pd.concat(dfs, ignore_index=True)
    return df

if __name__ == "__main__":
    data = get_latest_holder_number()
    print(data)

调用如上函数即可:
在这里插入图片描述

3. 将爬取的数据通过ORM形式写入数据库

from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float, Index
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy.orm import scoped_session
from datetime import datetime

# 声明一个基类,所有的ORM类都将继承自这个基类
Base = declarative_base()

# 创建引擎
engine = create_engine('sqlite:///a.db',  echo=False)
# 绑定引擎
Session = sessionmaker(bind=engine)
# 创建数据库链接池,直接使用session即可为当前线程拿出一个链接对象conn
session = scoped_session(Session)

# 股票基础信息表
class stock_base_info(Base):
    __tablename__ = 'stock_base_info'
    SECURITY_CODE = Column(String, primary_key=True, index=True, nullable=False, comment="股票代码")
    SECURITY_NAME_ABBR = Column(String, nullable=False, comment="股票名称")
    TOTAL_MARKET_CAP = Column(Float, comment="总市值")
    TOTAL_A_SHARES = Column(Float, comment="总股本")
    HOLD_NOTICE_DATE = Column(String, comment="公告日期")
    HOLDER_NUM = Column(Integer, comment="本次股东户数")
    HOLDER_NUM_RATIO = Column(String, comment="股东户数较上期变化百分比")
    PRE_HOLDER_NUM = Column(Integer, comment="上次股东户数")
    f2 = Column(String, comment="股票价格")
    last_updated = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="最后更新时间")
    
    __table_args__ = (
        Index('idx_SECURITY_CODE_index', SECURITY_CODE, unique=True),
    )
    
# 创建表, 创建所有class xx(Base)
Base.metadata.create_all(engine)  


def insert_or_update_stock_info(net_list):
    all_instances = session.query(stock_base_info).all()
    db_list = []
    for v in all_instances:
        db_list.append({
            'SECURITY_CODE': v.SECURITY_CODE,
            'SECURITY_NAME_ABBR': v.SECURITY_NAME_ABBR,
            'TOTAL_MARKET_CAP': v.TOTAL_MARKET_CAP,
            'TOTAL_A_SHARES': v.TOTAL_A_SHARES,
            'HOLD_NOTICE_DATE': v.HOLD_NOTICE_DATE,
            'HOLDER_NUM': v.HOLDER_NUM,
            'HOLDER_NUM_RATIO': v.HOLDER_NUM_RATIO,
            'PRE_HOLDER_NUM': v.PRE_HOLDER_NUM,
            'f2': v.f2,
        })

    # 查询出库中所有的数据 db_list; 从爬取的数据 net_list 中找到库中已有的数据进行更新 形成 db_map 并批量更新
    # 将 net_list 中不在 db_list 中的数据,形成 net_map 并批量插入
    db_map, not_exist_map = {}, {}
    for v in db_list:
        db_map[v['SECURITY_CODE']] = v
        
    for item in net_list:
        code = item['SECURITY_CODE']
        if code in db_map:
            db_map[code].update(item)
        else:     
            not_exist_map[code] = item
    update_result = list(db_map.values())
    insert_result = list(not_exist_map.values())
    if len(update_result) > 0:
        session.bulk_update_mappings(stock_base_info, update_result)
    if len(insert_result) > 0:
        session.bulk_insert_mappings(stock_base_info, insert_result)
    
    session.commit()
    
if __name__ == "__main__":
    pass

4. 整体逻辑流程

步骤:

  1. 爬取数据得到返回结果
  2. 将返回结果组成数组,并写入数据库
  3. 对于库中已存在的信息根据 股票代码 进行批量更新,对于不存在的进行批量插入
import stock
import db_orm

def update_base_info_db():
    data_df = stock.get_latest_holder_number()
    print('获取的股票数量=', data_df.shape)
    net_list = []
    for index, row in data_df.iterrows():
        code = row['股票代码']
        name = row['股票名称']
        cap = row['总市值']
        shares = row['总股本']
        data = row['公告日期']
        num = row['本次股东户数']
        pre_num = row['上次股东户数']
        ratio = row['股东户数较上期变化百分比']
        f2 = row['最新价'] # float类型
        net_list.append({
            'SECURITY_CODE': code,
            'SECURITY_NAME_ABBR': name,
            'TOTAL_MARKET_CAP': cap,
            'TOTAL_A_SHARES': shares,
            'HOLD_NOTICE_DATE': data,
            'HOLDER_NUM': num,
            'HOLDER_NUM_RATIO': ratio,
            'PRE_HOLDER_NUM': pre_num,
            'f2': str(f2),
        })
    if len(net_list) > 0:
        db_orm.insert_or_update_stock_info(net_list=net_list)

if __name__ == "__main__":
    update_base_info_db()

最终结果保存在 a.db中,例如:
在这里插入图片描述
更多内容可关注我,后续源码包均在上面回复下载:
【爬虫】爬取A股数据系列工具

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/603867.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB添加自编写.m文件或.mat数据并永久全局调用方法

菜单栏选择设置路径,然后“添加并包含子文件夹”,在弹出窗口中找到目标文件夹即可。此方案可以永久地将文件夹加入MATLAB路径。 添加包含自编写.m文件的文件夹: 即可实现永久全局调用。

Spring Web MVC 快速入门

🎥 个人主页:Dikz12🔥个人专栏:Spring学习之路📕格言:吾愚多不敏,而愿加学欢迎大家👍点赞✍评论⭐收藏 目录 什么是Spring MVC? MVC模式介绍 ​编辑学习Spring MVC…

Llama 3 模型上下文长度扩展至1048K

AI苏妲己: Gradient AI 近日宣布,通过其创新的渐进式训练方法,成功将 Llama 3 系列模型的上下文长度扩展至超过100万令牌。 Llama 3 模型最初设计用于处理8000个令牌的上下文长度,这大约相当于6000字或10页文档。为了适应更长的…

一站式IT运维管理平台CAT

什么是 CAT ? CAT(Coffee And Tea)是专为 IT 运维从业者打造的一个开源的、开放的一站式 IT 运维管理平台。包含资产管理、工单、工作流、仓储等功能模块,以及可靠的移动端应用(Uniapp)支持。 CAT 项目是 c…

node.js安装及环境配置超详细教程【Windows系统安装包方式】

Step1:下载安装包 https://nodejs.org/zh-cn/download/ 根据自己电脑系统及位数选择,我的电脑是Windows系统、64位、想下载稳定版的.msi(LTS为长期稳定版)这里选择windows64位.msi格式安装包。 .msi和.zip格式区别:…

【智能算法】雪消融优化算法(SAO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献5.代码获取 1.背景 2023年,L Deng受到雪升华和融化行为启发,提出了雪消融优化算法(Snow Ablation Optimizer, SAO)。 2.算法原理 2.1算法思想 SAO模拟了雪的…

大模型微调之 在亚马逊AWS上实战LlaMA案例(四)

大模型微调之 在亚马逊AWS上实战LlaMA案例(四) 在 Amazon SageMaker JumpStart 上微调 Llama 2 以生成文本 Meta 能够使用Amazon SageMaker JumpStart微调 Llama 2 模型。 Llama 2 系列大型语言模型 (LLM) 是预先训练和微调的生成文本模型的集合&#x…

stm32 st7735驱动 详解

初始化指令 void LCD_Init(void) { #if USE_SIM_SPILCD_SIM_SPI_GPIO_Init(); #endifLCD_RES_0();//复位HAL_Delay(100);LCD_RES_1();HAL_Delay(100);LCD_BLK_1();//打开背光HAL_Delay(100);//************* Start Initial Sequence **********//LCD_SPI_Send_Cmd(0x11); //Sl…

merge函数占用内存过大

🏆本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&…

TinyEngine 低代码引擎区块局域网部署方案全新上线!

本文由体验技术团队 TinyEngine 项目组成员创作~ 在 TinyEngine 开源后,对私有化部署存在诉求的用户越来越多,而当前 TinyEngine 多项内容都依托在公网中,当前官网提供的区块发布方案,为公网环境下的发布,不能完全满足…

JavaEE技术之MySql高级-ShardingSphere5(SpringBoot版本:3.0.5)

文章目录 1 ShardingSphere-JDBC读写分离1.1 创建SpringBoot程序1.1.1、创建项目1.1.2、添加依赖1.1.3、创建实体类1.1.4、创建Mapper1.1.5、配置 Spring Boot1.1.6、配置shardingsphere 1.2 测试1.2.1 读写分离测试1.2.2 负载均衡测试1.2.3 事务测试常见错误 2 ShardingSphere…

EMAP的Root工程及其他工具

首先右击项目导航,新建EMAP系统配置 上方辅助工具功能: 1 2 3 4 5 6 7 8 9 10 查看重复数据模型:显示为放大镜标识,可以显示所有应用中相同…

rabbitmq集群搭建失败解决

1. 现象 1. 三台机器都已经修改hosts,各个节点ping节点名正常 2. erlang.cookie各节点值一样 执行下面步骤加入失败 rabbitmqctl stop_app # 停止rabbitmq服务 rabbitmqctl reset # 清空节点状态 rabbitmqctl join_cluster rabbitrabbitmq3 rabbitmqctl start_ap…

STM32 GPIO介绍

每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL, GPIOx_CRH),两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。 通过软件配置寄…

Redis-三主三从高可用集群搭建

正式搭建之前,注意事项(坑)提前放到最开始,也可以出问题回来看, (1)第二步中最好将配置文件中的logfile自定义一个目录,以便于在第五步中启动出错的时候迅速定位错误。 &#xff0…

【SpringBoot】 什么是springboot(一)?如何搭建springboot项目?

文章目录 SpringBoot第一章1、什么是springboot1、回顾ssm项目搭建流程2、springboot项目的优点2、搭建springboot项目方式1:方式2:第二章1、基本配置1、热部署2、注解3、端口配置application.properties特点application.yml特点注意4、环境配置springboot中的配置文件要求5、…

笔记:编写程序,绘制一个展示支付宝月账单报告的饼图

文章目录 前言一、饼图是什么?二、编写代码总结 前言 笔记:编写程序,绘制一个展示支付宝月账单报告的饼图 (1) 导入 matplotlib.pyplot 模块; (2) 准备饼图所需的数据; …

进程状态与优先级

Linux内核源代码: 首先我们需要明确一点,Linux操作系统和操作系统的进程状态是不同的 上图大概标识了各个状态对应在操作系统的状态 普通进程 R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在…

【论文笔记 | 异步联邦】FedSA

FedSA:一种处理 non-IID 数据 的 过时感知 异步联邦算法 1. 论文信息 FedSA:A staleness-aware asynchronous Federated Learning algorithm with non-IID data,Future Generation Computer Systems,2021.7,ccfc 是…

「网络流 24 题」太空飞行计划 【最大权值闭合图】

「网络流 24 题」太空飞行计划 题意 有 n n n 个实验 和 m m m 个器械,每个实验都需要若干个指定的器械才能进行 实验 i i i 的盈利为 p i p_i pi​, 器械 j j j 的花销为 c j c_j cj​ 找出纯利润最大的实验计划 思路 这是非常典型的最大权值…