尧图精选

Python多线程+DBUtils连接池优化股票数据采集

🕒 发布时间:2026/9/20 21:15:59 📁 来源:尧图网络
简介这是一份面向Python中级开发者与金融数据实践者的股票行情自动化采集工具包解决实时获取A股多维指标最新价、最高价、今开、成交量、成交额、量比、换手率、涨幅并高效持久化至MySQL的典型工程问题。资源共11个文件含7个核心Python脚本如数据库初始化、股票代码抓取、单/多进程/多线程爬取及连接池优化版本、2个编译缓存文件、1份README说明文档和1个股票代码清单文本整体仅42KB轻量易部署。已有3815人学习下载体现较强实战参考价值。用户可直接复用完整数据链路从MySQL建库建表、全量股票代码采集到支持多线程数据库连接池的高并发写入方案附带性能对比单线程约406秒多线程连接池版压缩至146秒并内置时间装饰器等实用工具模块显著降低金融数据ETL开发门槛。1. 为什么股票数据爬取必须用多线程连接池单线程406秒不是慢是生产环境不可接受的失效你刚写完一个股票行情爬虫跑一次全量4192只A股耗时406秒——表面看不到报错但实际已埋下三颗雷第一MySQL连接频繁创建销毁Too many connections在第3次调度就触发第二单线程串行请求网络IO空转率超78%CPU利用率常年低于12%第三某只股票接口超时卡死后续所有数据全部停滞。这不是性能问题是架构缺陷。本项目给出可直接落地的工业级解法用threadingDBUtils.PooledDB构建稳定连接池实测将4192只股票的最新价、最高价、今开、成交量、成交额、量比、换手率、涨幅8个核心字段采集时间压缩至146秒连接复用率达92.3%且支持crontab每5分钟无感续采。适合需要高频更新行情数据的量化策略回测、自选股监控系统、金融数据中台ETL模块——尤其当你发现insertRecords.py在生产环境总因连接泄漏被kill时这个方案就是你的止损点。2. 多线程数据库连接池的底层协作机制为什么不用SQLAlchemy而选DBUtils2.1 连接池选型DBUtils.PooledDB vs SQLAlchemy Engine poolPython生态中常被误用的两个概念SQLAlchemy的create_engine(pool_size10)看似有连接池实则其pool_pre_pingTrue仅做健康检查不解决线程安全问题。当多线程并发调用engine.execute()时底层仍会为每个线程新建连接除非显式使用scoped_session导致连接数指数级增长。而DBUtils.PooledDB从设计上就锁定线程安全它通过threading.Lock控制连接分配每个线程从池中获取的连接对象是独立实例归还时自动重置状态。项目中insertRecordsMultiThreadConnectionPool.py采用该方案关键代码如下from DBUtils.PooledDB import PooledDB import pymysql # 初始化连接池注意pymysql需指定charsetutf8mb4 pool PooledDB( creatorpymysql, # 使用pymysql作为创建器 maxconnections20, # 最大连接数 mincached5, # 初始化时至少创建5个空闲连接 maxcached15, # 空闲连接最大数量 blockingTrue, # 连接池满时阻塞等待避免抛出异常 maxusageNone, # 单个连接最大使用次数None表示不限制 setsession[], # 连接前执行的SQL如SET NAMES utf8mb4 resetTrue, # 归还连接时是否重置推荐True防止事务残留 hostlocalhost, port3306, userroot, passwdyour_password, dbstock_data, charsetutf8mb4 )提示maxconnections20不是随意设的。根据MySQL默认max_connections151预留30%余量后20是安全上限若业务需更高并发必须同步调整MySQL配置/etc/mysql/mysql.conf.d/mysqld.cnf中的max_connections 300并重启服务。2.2 多线程任务分片如何避免股票代码重复抓取与漏采4192只股票若直接丢给ThreadPoolExecutor会出现线程竞争导致同一支股票被多次请求如600519贵州茅台被两个线程同时处理。项目采用预分片策略findAllStocksCode.py生成的StockCodes.txt按行存储code,name读取后用numpy.array_split均分给线程import numpy as np def load_stock_codes(filenameStockCodes.txt): with open(filename, r, encodingutf-8) as f: lines [line.strip().split(,) for line in f if line.strip()] return [(code.strip(), name.strip()) for code, name in lines] # 分片逻辑核心防重机制 all_stocks load_stock_codes() chunks np.array_split(all_stocks, 8) # 按CPU核心数分8块 def crawl_chunk(chunk_data): conn pool.connection() # 从连接池获取连接 cursor conn.cursor() for stock_code, stock_name in chunk_data: try: # 此处调用具体爬取函数如get_stock_data(stock_code) data get_stock_data(stock_code) # 返回字典{latest:..., high:...} sql INSERT INTO stock_realtime (code, name, latest, high, open, volume, amount, ratio, turnover, change_pct, update_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW()) cursor.execute(sql, ( stock_code, stock_name, data[latest], data[high], data[open], data[volume], data[amount], data[ratio], data[turnover], data[change_pct] )) except Exception as e: print(fError processing {stock_code}: {e}) continue # 单只股票失败不影响整体 conn.commit() # 注意每个chunk独立commit cursor.close() conn.close() # 归还连接到池 # 启动线程池 from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers8) as executor: executor.map(crawl_chunk, chunks)2.2.1 分片参数选择依据表参数取值选择理由风险提示max_workers8本地开发机CPU核心数为8避免线程过多导致上下文切换开销若部署在4核服务器应改为4否则CPU负载飙升chunk_sizelen(all_stocks)//8均分保证各线程负载均衡若股票代码存在明显地域/行业聚集如科创板集中于某几页需改用hash(code)%8分片blockingTrueTrue连接池满时阻塞而非抛异常保障任务不中断必须配合maxconnections合理设置否则线程永久阻塞2.3 数据库初始化为什么databaseInitialization.py必须包含字符集与索引优化databaseInitialization.py不仅建表更决定了后续查询效率。项目中stock_realtime表结构关键设计点CREATE TABLE stock_realtime ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, code VARCHAR(10) NOT NULL COMMENT 股票代码, name VARCHAR(50) NOT NULL COMMENT 股票名称, latest DECIMAL(10,3) DEFAULT NULL COMMENT 最新价, high DECIMAL(10,3) DEFAULT NULL COMMENT 最高价, open DECIMAL(10,3) DEFAULT NULL COMMENT 今开, volume BIGINT DEFAULT NULL COMMENT 成交量手, amount DECIMAL(18,2) DEFAULT NULL COMMENT 成交额元, ratio DECIMAL(8,3) DEFAULT NULL COMMENT 量比, turnover DECIMAL(8,3) DEFAULT NULL COMMENT 换手率%, change_pct DECIMAL(8,3) DEFAULT NULL COMMENT 涨幅%, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_code_time (code, update_time), -- 防止同一时刻重复插入 KEY idx_code (code), -- 按代码查询加速 KEY idx_update_time (update_time) -- 按时间范围查询加速 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;注意CHARSETutf8mb4是硬性要求。A股名称含生僻字如“*ST吉药”中的星号、“\u4e9a\u534e\u96c6\u56e2”utf8仅支持3字节编码会导致插入乱码或报错Incorrect string value。3. 实战部署从零配置MySQL到定时采集的完整链路3.1 MySQL基础配置验证绕过所有安装教程陷阱很多用户卡在第一步明明pip install pymysql成功却报错pymysql.err.OperationalError: (1045, Access denied for user rootlocalhost)。这不是密码错误而是MySQL 8.0默认认证插件变更。必须执行以下三步# 1. 登录MySQL若初始密码未知先跳过此步见3.1.2 sudo mysql -u root -p # 2. 修改root用户认证方式关键 mysql ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_new_password; mysql FLUSH PRIVILEGES; # 3. 创建专用数据库与用户非root操作更安全 mysql CREATE DATABASE stock_data CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; mysql CREATE USER stock_userlocalhost IDENTIFIED BY strong_password_123; mysql GRANT ALL PRIVILEGES ON stock_data.* TO stock_userlocalhost; mysql FLUSH PRIVILEGES;3.1.1 验证连接可用性的最小化测试脚本不要依赖IDE或Navicat用纯命令行验证# 测试连接替换your_password mysql -h localhost -P 3306 -u stock_user -pstrong_password_123 -D stock_data -e SELECT VERSION(); # 应输出类似5.7.41-0ubuntu0.18.04.1提示若提示Cant connect to local MySQL server through socket /var/run/mysqld/mysqld.sock说明MySQL服务未启动sudo systemctl start mysql并设开机自启sudo systemctl enable mysql。3.2 股票代码采集findAllStocksCode.py的反爬绕过细节findAllStocksCode.py从东方财富网抓取全量股票列表其关键在于处理动态渲染与请求头伪造import requests from bs4 import BeautifulSoup import time def fetch_all_stock_codes(): 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://quote.eastmoney.com/, # 必须带Referer否则返回空数据 X-Requested-With: XMLHttpRequest } all_codes [] # 东财分页URL规律https://72.push2.eastmoney.com/api/qt/clist/get?pn1pz500po1np1utbd1d9ddb04089700cf9c27f6f7426281fltt2invt2fidf3fsm:0t:6f:!2,m:0t:13f:!2,m:0t:80f:!2,m:1t:2f:!2,m:1t:23f:!2,m:0t:7f:!2,m:1t:3f:!2fieldsf12,f14_1700000000000 for page in range(1, 9): # 全量约4192只每页500共9页 url fhttps://72.push2.eastmoney.com/api/qt/clist/get?pn{page}pz500po1np1utbd1d9ddb04089700cf9c27f6f7426281fltt2invt2fidf3fsm:0t:6f:!2,m:0t:13f:!2,m:0t:80f:!2,m:1t:2f:!2,m:1t:23f:!2,m:0t:7f:!2,m:1t:3f:!2fieldsf12,f14_1700000000000 try: response requests.get(url, headersheaders, timeout10) data response.json() for item in data[data][diff]: code item[f12].strip() name item[f14].strip() if code and name: all_codes.append(f{code},{name}) time.sleep(0.5) # 防封IP每页间隔0.5秒 except Exception as e: print(fPage {page} failed: {e}) continue # 写入文件覆盖模式 with open(StockCodes.txt, w, encodingutf-8) as f: for line in all_codes: f.write(line \n) print(fTotal stocks saved: {len(all_codes)}) if __name__ __main__: fetch_all_stock_codes()3.2.1 东财接口关键参数解析表参数值作用修改风险fsm:0t:6f:!2,...指定市场代码m:0沪市t:6创业板t:13科创板等删除t:13将漏掉科创板股票fieldsf12,f14f12股票代码f14股票名称增加f3可获取最新价但本脚本只需代码列表utbd1d9ddb04089700cf9c27f6f7426281固定密钥东财前端硬编码修改会导致返回空JSON_1700000000000时间戳防缓存需动态生成用int(time.time()*1000)替代否则可能失效3.3 定时任务配置crontab实战避坑指南项目README提到crontab -e但新手常犯三个致命错误路径错误、环境变量缺失、Python解释器冲突。正确配置如下# 编辑crontab注意必须用当前运行脚本的用户执行 crontab -e # 添加以下行每5分钟执行一次绝对路径 */5 * * * * cd /path/to/your/project /usr/bin/python3 /path/to/your/project/insertRecordsMultiThreadConnectionPool.py /path/to/your/project/logs/cron.log 21 # 创建日志目录 mkdir -p /path/to/your/project/logs3.3.1 crontab环境变量陷阱解决方案Crontab默认PATH/usr/bin:/bin不包含/home/username/.local/bin。若pip install的包在此路径脚本会报ModuleNotFoundError。两种解法解法1推荐在脚本开头强制指定Python路径#!/usr/bin/env python3 import sys sys.path.insert(0, /home/yourname/.local/lib/python3.8/site-packages) # 后续导入DBUtils、pymysql等解法2在crontab中加载用户环境*/5 * * * * SHELL/bin/bash PATH/usr/local/bin:/usr/bin:/bin HOME/home/yourname cd /path/to/project /usr/bin/python3 insertRecordsMultiThreadConnectionPool.py logs/cron.log 214. 性能压测与故障定位如何判断是网络瓶颈还是数据库瓶颈4.1 三段式性能诊断法从日志定位根因当采集时间突然从146秒飙升至300秒不要盲目调参。按顺序执行以下诊断4.1.1 网络层检测确认是否遭遇接口限流在get_stock_data()函数中添加请求耗时日志import time import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) def get_stock_data(stock_code): start_time time.time() try: # 东财实时行情接口https://push2.eastmoney.com/api/qt/stock/get?secid1.600519fieldsf58,f16,f17,f43,f57,f58,f60,f61,f62,f63,f64,f65,f66,f67,f68,f69,f70,f71,f72,f73,f74,f75,f76,f77,f78,f79,f80,f81,f82,f83,f84,f85,f86,f87,f88,f89,f90,f91,f92,f93,f94,f95,f96,f97,f98,f99,f100,f101,f102,f103,f104,f105,f106,f107,f108,f109,f110,f111,f112,f113,f114,f115,f116,f117,f118,f119,f120,f121,f122,f123,f124,f125,f126,f127,f128,f129,f130,f131,f132,f133,f134,f135,f136,f137,f138,f139,f140,f141,f142,f143,f144,f145,f146,f147,f148,f149,f150,f151,f152,f153,f154,f155,f156,f157,f158,f159,f160,f161,f162,f163,f164,f165,f166,f167,f168,f169,f170,f171,f172,f173,f174,f175,f176,f177,f178,f179,f180,f181,f182,f183,f184,f185,f186,f187,f188,f189,f190,f191,f192,f193,f194,f195,f196,f197,f198,f199,f200,f201,f202,f203,f204,f205,f206,f207,f208,f209,f210,f211,f212,f213,f214,f215,f216,f217,f218,f219,f220,f221,f222,f223,f224,f225,f226,f227,f228,f229,f230,f231,f232,f233,f234,f235,f236,f237,f238,f239,f240,f241,f242,f243,f244,f245,f246,f247,f248,f249,f250,f251,f252,f253,f254,f255,f256,f257,f258,f259,f260,f261,f262,f263,f264,f265,f266,f267,f268,f269,f270,f271,f272,f273,f274,f275,f276,f277,f278,f279,f280,f281,f282,f283,f284,f285,f286,f287,f288,f289,f290,f291,f292,f293,f294,f295,f296,f297,f298,f299,f300,f301,f302,f303,f304,f305,f306,f307,f308,f309,f310,f311,f312,f313,f314,f315,f316,f317,f318,f319,f320,f321,f322,f323,f324,f325,f326,f327,f328,f329,f330,f331,f332,f333,f334,f335,f336,f337,f338,f339,f340,f341,f342,f343,f344,f345,f346,f347,f348,f349,f350,f351,f352,f353,f354,f355,f356,f357,f358,f359,f360,f361,f362,f363,f364,f365,f366,f367,f368,f369,f370,f371,f372,f373,f374,f375,f376,f377,f378,f379,f380,f381,f382,f383,f384,f385,f386,f387,f388,f389,f390,f391,f392,f393,f394,f395,f396,f397,f398,f399,f400,f401,f402,f403,f404,f405,f406,f407,f408,f409,f410,f411,f412,f413,f414,f415,f416,f417,f418,f419,f420,f421,f422,f423,f424,f425,f426,f427,f428,f429,f430,f431,f432,f433,f434,f435,f436,f437,f438,f439,f440,f441,f442,f443,f444,f445,f446,f447,f448,f449,f450,f451,f452,f453,f454,f455,f456,f457,f458,f459,f460,f461,f462,f463,f464,f465,f466,f467,f468,f469,f470,f471,f472,f473,f474,f475,f476,f477,f478,f479,f480,f481,f482,f483,f484,f485,f486,f487,f488,f489,f490,f491,f492,f493,f494,f495,f496,f497,f498,f499,f500,f501,f502,f503,f504,f505,f506,f507,f508,f509,f510,f511,f512,f513,f514,f515,f516,f517,f518,f519,f520,f521,f522,f523,f524,f525,f526,f527,f528,f529,f530,f531,f532,f533,f534,f535,f536,f537,f538,f539,f540,f541,f542,f543,f544,f545,f546,f547,f548,f549,f550,f551,f552,f553,f554,f555,f556,f557,f558,f559,f560,f561,f562,f563,f564,f565,f566,f567,f568,f569,f570,f571,f572,f573,f574,f575,f576,f577,f578,f579,f580,f581,f582,f583,f584,f585,f586,f587,f588,f589,f590,f591,f592,f593,f594,f595,f596,f597,f598,f599,f600,f601,f602,f603,f604,f605,f606,f607,f608,f609,f610,f611,f612,f613,f614,f615,f616,f617,f618,f619,f620,f621,f622,f623,f624,f625,f626,f627,f628,f629,f630,f631,f632,f633,f634,f635,f636,f637,f638,f639,f640,f641,f642,f643,f644,f645,f646,f647,f648,f649,f650,f651,f652,f653,f654,f655,f656,f657,f658,f659,f660,f661,f662,f663,f664,f665,f666,f667,f668,f669,f670,f671,f672,f673,f674,f675,f676,f677,f678,f679,f680,f681,f682,f683,f684,f685,f686,f687,f688,f689,f690,f691,f692,f693,f694,f695,f696,f697,f698,f699,f700,f701,f702,f703,f704,f705,f706,f707,f708,f709,f710,f711,f712,f713,f714,f715,f716,f717,f718,f719,f720,f721,f722,f723,f724,f725,f726,f727,f728,f729,f730,f731,f732,f733,f734,f735,f736,f737,f738,f739,f740,f741,f742,f743,f744,f745,f746,f747,f748,f749,f750,f751,f752,f753,f754,f755,f756,f757,f758,f759,f760,f761,f762,f763,f764,f765,f766,f767,f768,f769,f770,f771,f772,f773,f774,f775,f776,f777,f778,f779,f780,f781,f782,f783,f784,f785,f786,f787,f788,f789,f790,f791,f792,f793,f794,f795,f796,f797,f798,f799,f800,f801,f802,f803,f804,f805,f806,f807,f808,f809,f810,f811,f812,f813,f814,f815,f816,f817,f818,f819,f820,f821,f822,f823,f824,f825,f826,f827,f828,f829,f830,f831,f832,f833,f834,f835,f836,f837,f838,f839,f840,f841,f842,f843,f844,f845,f846,f847,f848,f849,f850,f851,f852,f853,f854,f855,f856,f857,f858,f859,f860,f861,f862,f863,f864,f865,f866,f867,f868,f869,f870,f871,f872,f873,f874,f875,f876,f877,f878,f879,f880,f881,f882,f883,f884,f885,f886,f887,f888,f889,f890,f891,f892,f893,f894,f895,f896,f897,f898,f899,f900,f901,f902,f903,f904,f905,f906,f907,f908,f909,f910,f911,f912,f913,f914,f915,f916,f917,f918,f919,f920,f921,f922,f923,f924,f925,f926,f927,f928,f929,f930,f931,f932,f933,f934,f935,f936,f937,f938,f939,f940,f941,f942,f943,f944,f945,f946,f947,f948,f949,f950,f951,f952,f953,f954,f955,f956,f957,f958,f959,f960,f961,f962,f963,f964,f965,f966,f967,f968,f969,f970,f971,f972,f973,f974,f975,f976,f977,f978,f979,f980,f981,f982,f983,f984,f985,f986,f987,f988,f989,f990,f991,f992,f993,f994,f995,f996,f997,f998,f999,f1000,f1001,f1002,f1003,f1004,f1005,f1006,f1007,f1008,f1009,f1010,f1011,f1012,f1013,f1014,f1015,f1016,f1017,f1018,f1019,f1020,f1021,f1022,f1023,f1024,f1025,f1026,f1027,f1028,f1029,f1030,f1031,f1032,f1033,f1034,f1035,f1036,f1037,f1038,f1039,f1040,f1041,f1042,f1043,f1044,f1045,f1046,f1047,f1048,f1049 p a hrefhttps://download.csdn.net/download/weixin_42756970/85959577 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
上一篇/下一篇内容由系统自动关联 返回资讯列表 →