尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python爬虫实战:抓取财经社区组合调仓记录的完整方案
简介基于Python实现的雪球网组合调仓记录采集资源面向Python爬虫初学者与股票数据研究者帮助自动化抓取投资大神的调仓动态为趋势分析和策略复盘提供数据支撑。压缩包共包含2个文件均为.py脚本整体大小仅10KB两个脚本分别为4线程正式版和早期迭代版便于对比多线程优化与代码演进。目前已有382人学习下载。脚本完整覆盖发送HTTP请求、解析HTML、提取与存储数据的标准流程可基于调仓频率、买入卖出行为展开趋势判断、策略研究和风险评估并可延伸至组合监控与异常告警。阅读这两个版本读者既能掌握采用多线程提升采集效率的完整开发框架也能理解在遵守robots协议前提下设计可持续运行的金融数据采集工具的方法。1. 抓取财经社区大神组合的调仓记录从手动翻页到定时自动入库写 Python 爬虫的人很多但真正把股票数据采集这件事做顺手的靠的不是猜接口而是弄明白业务数据长什么样。我之前有段时间天天手动打开网页盯着几个投资达人的组合调仓看谁加仓、谁清仓一天来回翻好几遍又累又容易漏。直到我意识到这些调仓记录在页面渲染之前就已经以 JSON 格式躺在接口里了于是把思路从“盯页面”改成“抓接口”。这份资源就是一套基于 Python 实现的组合调仓记录采集工具从目标财经社区抓取头部用户的调仓操作随包给了两个版本一个走接口直连、一个解析页面 HTML覆盖了构造请求、Cookie 维护、分页解析、数据清洗和落盘保存的完整链路。适合自己做调仓记录研究、盯盘复盘的个人投资者也适合需要为量化回测补充持仓快照的开发者。2. 两个版本各自的采集路径接口直连与页面解析解压资源包后你会看到两个相对独立的目录分别对应版本 A 和版本 B。版本 A 的核心逻辑在请求模块里版本 B 的核心逻辑在页面解析模块里两者共用同一套清洗和存储代码。先别急着运行理解两个版本的设计差异后续调整字段、新增组合、应对反爬时才不会改错文件。2.1 版本A走JSON接口的轻量方案速度与反查成本最优版本 A 的采集路径最直接模拟浏览器发起的异步请求。你在组合调仓页面上看到的各种买卖记录其实不是静态 HTML 直接带出来的而是页面加载后由 JavaScript 再发起一次 XHR 请求拿到 JSON 后再渲染成表格。所以版本 A 要做的事情就是三件用合理的请求头访问接口、解析 JSON 数组、把字段按需求重组。这里最需要理解的是请求头。Cookie标识当前登录用户很多接口在没有有效 Cookie 时不会直接报错而是返回一个空列表Referer用来表示请求来源是组合页面部分接口会校验这个字段User-Agent则负责不让服务端一眼识别出 Python 脚本。我一般把这些请求头集中在一个函数里维护Cookie 过期了只改配置不改代码import requests API_HISTORY_URL https://api.example.com/v5/stock/portfolio/history.json def build_headers(cookie: str) - dict: # 集中维护三个关键请求头Cookie 失效时只在这里替换 return { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36, Referer: https://www.example.com/portfolio/, Cookie: cookie, Accept: application/json, text/plain, */*, } def make_session(cookie: str) - requests.Session: session requests.Session() session.headers.update(build_headers(cookie)) return sessionmake_session返回一个带公共请求头的会话对象之后抓取每个组合时请求都会自动带上这些头省去每次手动拼装。注意这里我把build_headers单独拆出来目的是让 Cookie 更新点只有一个。如果某天登录态过期打开这个文件换掉 Cookie 字符串就行不用在取数逻辑里翻来翻去找。接下来是真正的取数逻辑def fetch_history(session: requests.Session, portfolio_id: str, page: int 1, count: int 20) - list: params { uid: portfolio_id, page: page, count: count, } resp session.get(API_HISTORY_URL, paramsparams, timeout10) resp.raise_for_status() payload resp.json() # 常见返回结构是 data.list每个元素是一条调仓记录 return payload.get(data, {}).get(list, [])这里参数设计的思路是uid是组合的唯一标识通常是一串数字从组合主页 URL 里可以直接看到count控制单页返回条数page控制翻页位置timeout10给每个请求设置 10 秒上限防止某个接口挂起后整个任务卡死。返回的list里每一项一般包含股票代码、股票名称、操作方向、成交金额、时间戳但字段名在不同接口版本里可能不一样有的叫action_type有的叫direction。所以拿到数据后第一步应该是打印一条原始记录看字段再写后面的重组逻辑。版本 A 的最大优势是数据天然结构化不需要做 HTML 清洗单组合请求量也少。它的维护成本在于接口参数和返回结构可能调整一旦调整脚本里的字段名和参数都要跟着改。这也是为什么资源包同时放了版本 B。2.2 版本B解析页面HTML的兜底方案版本 B 走的是另一条路直接请求组合调仓页面再用 BeautifulSoup 把渲染后的 HTML 表格内容抠出来。它适用的场景是接口参数校验变严、字段名频繁变动或者你不想反复断点调试接口返回体时页面表格反而更稳定。解析原理并不复杂三步定位承载调仓记录的那张表格遍历表格行把每一行的单元格按顺序提取成字段。核心代码大致如下from bs4 import BeautifulSoup def parse_adjust_table(html_text: str) - list: soup BeautifulSoup(html_text, html.parser) result [] # 目标站点的调仓记录一般放在这个 ID 的表格里 tbody soup.select_one(#portfolio-adjust-table tbody) if tbody is None: return result for tr in tbody.select(tr): cells tr.select(td) if len(cells) 5: continue result.append({ stock_code: cells[0].get_text(stripTrue), stock_name: cells[1].get_text(stripTrue), action: cells[2].get_text(stripTrue), amount: cells[3].get_text(stripTrue), created_at: cells[4].get_text(stripTrue), }) return result这段代码里最需要注意的是选择器稳定性。#portfolio-adjust-table这个 ID 通常不会变但表格里偶尔会出现分隔行、合计行或者某些行为空所以我在遍历时加了len(cells) 5的判断先把结构不完整的行过滤掉。get_text(stripTrue)用于去掉单元格里的空白字符不过遇到单元格内嵌子标签的场景文本可能会被拼在一起这时候还需要额外写一个清洗函数做后处理。版本 B 的代价是请求的是整个页面单组合单页的数据量更大而且翻页方式完全依赖 URL 参数结构不像 JSON 接口那样有统一的page字段。混合使用时我习惯把版本 B 定位成兜底方案平时主力用版本 A 跑定时任务A 出现接口问题再临时切到 B 补数。2.3 选型对照表与使用建议两份代码放在一起你的时间应该花在按场景选型上而不是纠结哪个版本写得更好。在金融商贸类的数据任务里我一般先问自己一个问题这个任务要跑多久如果是每天长期跑版本 A 更合适如果只是临时调研某个组合的历史调仓版本 B 反而更快落地。维度版本A接口直连版本B页面解析数据源后端 JSON 接口页面渲染后的 HTML主要依赖requests jsonrequests BeautifulSoup4单组合请求量少翻页走参数多需要拉整页稳定性受接口参数变动影响受页面样式变动影响字段完整度高直接可取中需清洗文本适合场景长期定时任务、入数据库临时调研、接口故障兜底就我自己的使用习惯来看主力任务用版本 A每天收盘后定时拉取组合调仓遇到接口改版导致返回空数据时临时切到版本 B 把当天数据补齐。资源包把两个版本都提供出来省去你自己另写一套兜底逻辑的功夫。3. 部署与改造从请求到落盘跑通完整流程3.1 环境准备与依赖安装整个项目第三方依赖只有 requests 和 beautifulsoup4另外建议装 pandas后面做数据清洗和 CSV 输出会方便很多。Python 版本 3.8 以上即可我实测 3.10、3.11 都能直接跑。安装命令pip install requests beautifulsoup4 pandas装完后建议在项目目录下建好 config、fetcher、parser、storage 四个子目录。config 放 Cookie 和组合 ID 配置fetcher 放请求模块parser 放页面解析模块storage 放落盘代码。这样组合 ID 变更时不用动主体代码只改配置文件。3.2 核心流程串联从一个组合到批量产出下面是版本 A 主流程的完整串联版我做了适当简化保留核心步骤import random import time import csv from pathlib import Path def clean_record(raw: dict) - dict: # 字段归一化amount 做空值兜底 return { portfolio_id: raw.get(uid), stock_code: raw.get(stock_code, ).strip(), stock_name: raw.get(stock_name, ).strip(), action: raw.get(action, ).strip(), amount: float(raw.get(amount) or 0), created_at: raw.get(created_at, ), } def main(): cookie 你的登录Cookie portfolio_ids [100001, 100002, 100003] session make_session(cookie) all_record [] for pid in portfolio_ids: try: history fetch_history(session, pid, page1, count20) for item in history: all_record.append(clean_record(item)) print(f[OK] 组合 {pid} 抓取到 {len(history)} 条调仓记录) except Exception as exc: print(f[ERR] 组合 {pid} 抓取失败: {exc}) # 随机延时避免请求过于集中 time.sleep(random.uniform(2, 5)) out_path Path(output) / history.csv out_path.parent.mkdir(exist_okTrue) with out_path.open(w, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[ portfolio_id, stock_code, stock_name, action, amount, created_at ]) writer.writeheader() writer.writerows(all_record) print(f完成共 {len(all_record)} 条记录已写入 {out_path})读这段代码时有几个点值得展开。clean_record做了字段归一化和空值兜底尤其amount用float(raw.get(amount) or 0)这样即使接口返回的金额是字符串或 None写入 CSV 后也不会因为类型不一致导致后续分析报错。time.sleep(random.uniform(2, 5))不是玄学而是让每次请求之间有一定间隔降低被服务端限流的概率。输出路径用Path而不是字符串拼接是为了让目录创建和文件写入在跨平台上更可靠这个习惯在定时任务场景下尤其有用。如果你用的是版本 B主流程只有取数函数不同把fetch_history(session, pid)换成请求页面的函数再把返回的 HTML 文本传给parse_adjust_table其余清洗和落盘逻辑可以完全复用。这也是我推荐大家保持模块化拆分的原因两个版本切换时改动成本很小。3.3 请求参数怎么调频率、超时与重试抓取频率是这里最需要认真对待的参数。组合数量少时random.uniform(2, 5)的间隔足够温和组合数量超过 50 个建议把间隔放宽到random.uniform(5, 8)同时把单页count调大到 50减少翻页请求次数。超时参数timeout设的是每个请求的上限不是总时长不需要担心整体任务因此变慢。重试这边我习惯给每个组合包一层重试逻辑遇到超时或 5xx 错误时退避重试最多 3 次def fetch_with_retry(session, portfolio_id, retries3): for attempt in range(retries): try: return fetch_history(session, portfolio_id) except Exception as exc: print(f[WARN] 第 {attempt 1} 次重试组合 {portfolio_id}: {exc}) time.sleep(2 ** attempt) # 指数退避1s, 2s, 4s return []这里的退避用的是2 ** attempt即第 1 次重试等 2 秒第 2 次等 4 秒第 3 次等 8 秒。指数退避的好处是给服务端留出恢复时间避免刚缓过来又被连续请求打垮。这是我做了多轮定时采集后踩出来的经验简单说就是重试要有耐心越快重试反而越容易触发二次封禁。3.4 定时调度与日志输出手动跑通后把它挂到定时任务里才算真正解放双手。Linux 服务器上我用 cron每天下午收盘后跑一次0 15 * * 1-5 cd /opt/portfolio-spider python main.py logs/run.log 210 15 * * 1-5表示每个工作日 15 点整执行 logs/run.log把标准输出和错误信息都追加到日志文件方便第二天早上检查执行情况。Windows 环境可以在“任务计划程序”里配同样的规则。日志这块建议在脚本入口加一句logging.basicConfig(filenamelogs/run.log, levellogging.INFO)把每次运行的结果、异常堆栈都保留下来。没有日志的话遇到问题排查成本会高很多。4. 避坑指南调仓采集最常见的五个翻车现场4.1 现象接口返回200但列表永远是空的这种问题最迷惑的地方在于请求没有报错状态码也是 200但返回的调仓列表是空数组。新手很容易怀疑是自己解析写错了反复改 JSON 解析逻辑折腾一上午发现原封不动。原因大概率是 Cookie 过期或请求头缺少Referer。目标站点的调仓接口在登录态失效时不会直接抛错而是安静地返回空数据。Referer字段缺失时部分接口也会按来源过滤掉请求。解决方式先打印原始返回体看data字段是null还是空数组。如果是null直接重新登录浏览器复制最新 Cookie 更新到配置文件。同时确认Referer和浏览器一致。4.2 现象跑十分钟后突然大批量超时组合数量多、请求间隔又短的情况下很容易触发服务端频率限制。它不是直接封 IP而是开始让部分请求超时或返回 403。体感就是任务跑到一半报错突然增多重试也不管用。解决方式把延时从random.uniform(1, 3)放宽到random.uniform(4, 8)并给每个组合加一层重试逻辑。另一个容易被忽略的点是保持单线程循环不要图快上多线程。Session默认复用 TCP 连接但服务端对连接数有限制并发一高反而整体更慢。4.3 现象时间字段看起来总是差了8小时接口返回的时间戳是 UTC 时区而我们看的是东八区时间。如果直接datetime.fromtimestamp(ts)转换落在数据库里的记录会统一比实际时间早 8 小时。解决方式转换时手动指定时区偏移用datetime.fromtimestamp(ts, tztimezone(timedelta(hours8)))再格式化成字符串。我第一次遇到时第二天早上看数据觉得所有调仓都发生在凌晨排查了很久才想到时区问题。这种坑一旦进了库后续清洗时再想追回来会很麻烦。4.4 现象清仓记录和加仓记录对不上账组合调仓里清仓操作往往对应一条“卖出全部持仓”的记录但部分接口为了简化展示只记录当前剩余持仓的变化导致清仓后该股票的记录直接消失。解决方式不要只依赖单条记录判断。采集时保留完整历史再按“股票代码 操作方向 时间”去重分析清仓行为时用“该股票最后一笔卖出”加“剩余持仓为 0”两个条件一起判断。至少这样不会因为单条记录缺失得出错误结论。4.5 现象增量任务重复入库表格越积越重定时任务一旦中断后重启新跑的数据会把上次已经入库的记录再写一遍。没有唯一约束时重复数据会不断累积后续做统计时数字全都偏高。解决方式在数据库里建唯一索引(portfolio_id, stock_code, action, created_at)入库前用INSERT OR IGNORE跳过重复记录。如果是直接用 CSV 存储就在每次任务启动前加载旧记录做集合差再用差集写入新文件。这套逻辑多加几行代码却能省掉后续人工清理的时间。5. 增量更新与数据校验把采集结果变成可信台账脚本跑起来之后真正的问题变成了数据能不能信。见过不少人抓下来的调仓记录和网页对不上原因往往是数据存在多个副本、接口偶发缺数据而脚本没有复检机制。解决办法也简单建一张带唯一约束的本地表用增量窗口去更新每次跑完任务后做一次数据自检。建表逻辑CREATE TABLE IF NOT EXISTS adjust_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, portfolio_id TEXT NOT NULL, stock_code TEXT NOT NULL, stock_name TEXT, action TEXT, amount REAL, created_at TEXT, UNIQUE(portfolio_id, stock_code, action, created_at) );UNIQUE那一行就是防止重复入库的关键。写入时改用INSERT OR IGNORE重复记录直接静默跳过。增量窗口则用上一次采集的最大created_at作为起点每次只拉取这个时间之后的数据省掉当天全量翻页的开销。采集完成后的自检我一般看两个指标。第一是“当日买卖平衡”对一个组合当天买入总额与卖出总额不应有数量级的差异如果某天买入总和是卖出的几十倍大概率是漏抓了卖出记录。第二是“末尾连续性”连续两天调度今天的数据里应包含昨天最后一条记录的时间戳如果没有说明中间有数据空洞需要回补。这套自检逻辑我第一次跑的时候就发现一个组合连续三天缺清仓记录。问题不是接口变动而是那个组合名字里带有特殊符号URL 编码没处理干净导致请求始终只拿到第一页。从那以后我每次部署爬虫前都会强制走一遍“空 Cookie 测试、延时压测、唯一约束检查”三件套不留侥幸。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

UI自动化实战:下拉框、弹窗与滚动条的稳定处理方案

UI自动化实战:下拉框、弹窗与滚动条的稳定处理方案

做UI自动化这些年,下拉选择框、弹出框、滚动条这三个货,真是让人又爱又恨。说难吧,定位方式就那么几种;说简单吧,实际跑起来总是莫名其妙地失败。尤其是下拉选择框,今天能用明天就报错,弹出框换…

📅 2026/10/20 18:14:46
NeuDecide:面向儿童语音交互的端到端轻量决策模型

NeuDecide:面向儿童语音交互的端到端轻量决策模型

1. 项目概述:一个被严重低估的“语音直通决策”范式转变最近在几个开源模型仓库和儿童教育硬件论坛里反复看到NeuDecide这个名字,点进去发现它不是又一个微调版 Whisper 或者 Whisper,而是一个彻底绕开传统语音识别(ASR&#xff0…

📅 2026/10/20 18:14:46
企业大模型选型方法论:场景匹配、成本、工程与安全四维评估

企业大模型选型方法论:场景匹配、成本、工程与安全四维评估

前阵子有个做企业服务的哥们儿来找我聊,说他们准备把正在用的模型服务换掉,原因是“排行榜第一名已经换人了,我们是不是也该跟着换”。我当时就劝他先把预算和业务场景摆到桌面上,别急着追榜单。做模型选型这些年,我见…

📅 2026/10/20 18:14:46
MORE NEWS

更多资讯

📰

基于MATLAB的分布式发电配电网网损与电压支撑仿真分析

搞配电网的人对“分布式发电”这个词肯定不陌生,但真正在MATLAB里把它的影响算清楚、讲明白,却不是看一眼潮流结果就能交差的事。我在做这个关于分布式发电对配电网影响的综合研究时,最深的感受是:网损和电压支撑这两个指标&#…

📰

别再用new ObjectMapper()!Jackson实例复用与性能优化之道

第 N 次看到new ObjectMapper()出现在业务代码里,我实在忍不住了。今天想把这件事一次说透:ObjectMapper 不能当成普通工具对象随手 new,它是个典型的重量级组件,每次 new 都在烧 CPU、堆内存和 GC 停顿。文章的后半部分会给出线程…

📰

OpenFeign声明式调用核心机制与生产实践:代理、超时、连接池、拦截器

最近在梳理微服务调用这块的内容,发现很多团队的调用链路里都藏着OpenFeign的身影。这组件用起来确实方便——接口一声明,远程调用就跟调本地方法一样。但真到排查问题的时候,不少人会卡在"它到底是怎么把接口变成可用对象的""…

📰

KMeans预筛+KNN精判:解决小样本高维医疗数据分类难题

简介:本资源是一份面向机器学习初学者与课程设计实践者的乳腺癌分类预测完整方案,聚焦KMeans聚类与KNN分类协同建模,解决医学数据中良恶性二分类预测问题,适用于本科机器学习、数据挖掘类课程设计及算法实战训练。压缩包共5个文件…

📰

LLaMA-Factory实战:从数据准备到LoRA微调完整指南

把跑通微调的经验沉淀一下,是个挺磨人的过程。我最初是拿现成脚本改,改来改去总会遇到数据格式不对、学习率不合适、一爆显存就得重新来一遍这类问题。后来换到LLaMA-Factory,整体链路才算是理顺了——数据准备、模型加载、LoRA训练、评测、推理、导出合并,它基本都替你封装好了…

📰

GPT-4更新与API降价:创业公司如何应对大模型生态冲击

1. 一场开发者大会背后的行业震荡1.1 从标题拆解出的三个核心信号看到这个标题的第一反应,大部分技术圈的人会心一笑,但笑完之后脊背发凉。这个标题信息密度极高,至少释放了三个关键信号:第一,头部大模型厂商开始把开发…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬