tushare源码解析:Python量化数据接口封装与本地缓存实践

简介:这份资源是 tushare 开源 Python 财经数据接口库的最新源代码包,面向金融数据分析从业者、量化研究者以及希望深入理解数据接口实现原理的开发者。包内共 44 个文件,以 25 个 py 源码文件为核心,辅以 7 个 rst 文档、3 个 txt 说明、2 个 md 文档,以及 makefile、yml、csv、bat、manifest 等配置与测试文件,压缩包约 269KB,结构紧凑、模块划分清晰。目前已有 1494 人学习下载。通过研读源码,读者可以掌握股票、期货、期权、基金、外汇、债券等数据的获取封装方式,理解 HTTP 请求、数据解析与异常处理逻辑;同时能学习 SQLite、MongoDB 等数据库的存储与检索设计,数据清洗、缺失值填充与时间序列处理方案,以及 matplotlib、seaborn 可视化接口的调用方法。多线程与异步 IO 的并发调度、错误处理与日志记录机制也值得借鉴,有助于提升大规模金融数据处理的效率与程序健壮性。

1. 从一份源码包说起:tushare 到底能帮你拿到什么数据

如果你写过 A 股相关的策略回测,大概率遇到过这种场景:想拉一份完整的日线行情,从某财经页面复制粘贴,格式乱、字段缺、除权除息还得自己算。折腾半天,策略逻辑一行没写,数据清洗倒写了三百行。tushare 这个项目解决的正是这个痛点——它把行情、财务、指数、基金等数据封装成统一的 Python 接口,一行调用就能拿到结构化 DataFrame。这份最新源代码适合两类人:一是想快速搭建数据管道的量化新手,二是想读懂数据接口层设计、准备做二次封装的老手。源码包里通常包含核心请求模块、接口定义、字段映射和示例脚本,拿到手就能本地跑通。

2. 项目定位与技术选型:为什么它成了数据接口层的常客

2.1 它解决的不是「取数」,而是「统一取数」

很多人第一次接触 tushare,以为它就是个爬虫封装。实际拆开源码会发现,它的核心价值在于把不同来源、不同格式、不同频率的数据,统一成一套调用约定。比如日线行情、复权因子、财务指标,在底层可能是完全不同的接口协议,但对外都暴露成pro.daily()、pro.adj_factor()、pro.fina_indicator()这种风格一致的函数。这种设计让策略代码和数据源解耦——哪天某个接口换了地址,你只需要改配置,不用动策略逻辑。

从源码结构看,它通常分成三层:最底层是 HTTP 请求与重试机制,中间层是接口路由与参数校验,最上层是面向用户的 API 对象。这种分层在 Python 项目里很常见,但 tushare 做得比较克制,没有过度抽象,读起来不费劲。

2.2 技术栈:轻依赖、重约定

源码包里的依赖通常不多,核心就是requests做网络请求,pandas做数据容器,simplejson或标准库处理序列化。这种轻依赖的好处是部署成本低,一台普通开发机pip install就能跑。但轻依赖也意味着它不帮你做太多事——比如缓存、并发、断点续传,这些得自己加。

我一般会在它的请求层外面包一层本地缓存,用 SQLite 或 Parquet 存已经拉过的数据,避免重复请求。源码里通常留了timeout和retry参数,但默认值偏保守,实际用的时候建议根据网络情况调大。

2.3 接口鉴权与 token 机制

tushare 的接口大多需要 token,源码里一般会有一个set_token()方法,把 token 存到全局配置或环境变量里。拆开看,它其实就是在请求头里加了一个字段。这里有个容易翻车的点:token 不要硬编码在脚本里,尤其是要提交到代码仓库的时候。常见做法是读环境变量,或者用一个单独的config.py并加入.gitignore。

import os import tushare as ts # 从环境变量读取 token,避免硬编码 token = os.environ.get("TUSHARE_TOKEN") if not token: raise ValueError("请先设置 TUSHARE_TOKEN 环境变量") ts.set_token(token) pro = ts.pro_api() # 拉取单只股票日线,指定日期区间 df = pro.daily(ts_code="000001.SZ", start_date="20240101", end_date="20240131") print(df[["trade_date", "open", "close", "vol"]].head())

这段代码的逻辑很直白:先拿 token,再初始化 API 对象,最后调具体接口。参数说明上,ts_code是带交易所后缀的股票代码,start_date和end_date格式是YYYYMMDD。返回的 DataFrame 字段名是英文缩写,源码里通常有一份字段映射表,想改成中文列名可以自己写个 rename。

3. 目录结构与核心模块拆解:一个请求从发起到返回

3.1 源码目录长什么样

拿到源码包后,先别急着跑setup.py,花五分钟把目录扫一遍。典型的 tushare 源码结构大致如下:

tushare/ ├── __init__.py # 暴露顶层 API,如 pro_api、set_token ├── pro/ │ ├── client.py # 请求客户端,封装 HTTP 调用与重试 │ ├── data_pro.py # 各接口的具体实现,按数据类别分文件 │ └── utils.py # 参数校验、日期处理、字段映射 ├── stock/ │ └── cons.py # 股票列表、交易日历等基础数据 ├── examples/ │ └── demo_daily.py # 官方示例脚本 └── setup.py # 安装配置

这个结构不是固定的,不同版本会有调整,但核心思路一致:client.py管通信,data_pro.py管业务,utils.py管工具。读源码时建议从__init__.py入手,看它导出了哪些名字,再顺着调用链往下追。

3.2 请求客户端:重试与超时怎么做的

client.py是整个项目的咽喉。它通常定义一个Client类,内部持有requests.Session,并在query()方法里处理重试。下面是一段常见的实现风格:

import time import requests class Client: def __init__(self, token, timeout=15, max_retry=3): self.token = token self.timeout = timeout self.max_retry = max_retry self.session = requests.Session() self.session.headers.update({"Authorization": f"Bearer {token}"}) def query(self, url, params): """带重试的 GET 请求,失败时指数退避""" for attempt in range(self.max_retry): try: resp = self.session.get(url, params=params, timeout=self.timeout) resp.raise_for_status() return resp.json() except requests.RequestException as e: if attempt == self.max_retry - 1: raise # 指数退避,避免瞬间打满 time.sleep(2 ** attempt)

逻辑说明:query()是统一入口,所有接口最终都走这里。参数上,timeout控制单次请求超时,max_retry控制重试次数,2 ** attempt实现指数退避。这里有个细节:raise_for_status()会把 4xx 和 5xx 都抛出来,但实际业务里 4xx 重试没意义,可以加个判断只对 5xx 重试。我一般会改成if resp.status_code >= 500才重试。

3.3 接口实现层:参数校验与字段裁剪

data_pro.py里通常是一堆方法,每个方法对应一个接口。以daily()为例,它做三件事:校验参数、拼请求、裁剪字段。

def daily(self, ts_code=None, trade_date=None, start_date=None, end_date=None): """日线行情,支持单只或按日期区间""" if not ts_code and not trade_date: raise ValueError("ts_code 和 trade_date 至少传一个") params = { "api_name": "daily", "token": self.token, "params": {"ts_code": ts_code, "trade_date": trade_date, "start_date": start_date, "end_date": end_date}, "fields": "ts_code,trade_date,open,high,low,close,vol,amount" } data = self.client.query(self.url, params) return pd.DataFrame(data["items"], columns=data["fields"].split(","))

参数说明:ts_code和trade_date是互斥的常用组合,源码里一般会做校验。fields字段控制返回列,不传就返回全部,但全部字段往往很多,按需裁剪能省带宽。返回时用pd.DataFrame包装,列名从fields里切分。这里有个坑:如果接口返回空,data["items"]可能是空列表,直接构造 DataFrame 会得到空表,后续计算要判空。

3.4 基础数据模块:交易日历与股票列表

stock/cons.py这类文件通常放的是「不常变但每次都要用」的数据,比如交易日历、股票列表、复权因子。这些数据的特点是:拉一次可以缓存很久,但每次策略启动都要读。源码里一般会提供一个trade_cal()接口,返回指定年份的交易日。

# 获取 2024 年交易日历,只保留开市日 cal = pro.trade_cal(exchange="SSE", start_date="20240101", end_date="20241231") open_days = cal[cal["is_open"] == 1]["cal_date"].tolist() print(f"2024 年开市天数:{len(open_days)}")

这段代码的用途很实际:回测时遍历交易日,而不是遍历自然日。参数exchange指定交易所,is_open为 1 表示开市。我一般会把结果存成 CSV,下次直接读,省得每次都请求。

4. 本地跑通与数据落库:从安装到第一份 Parquet

4.1 安装与初始化

源码包通常支持两种安装方式:pip install .从本地安装,或者直接python setup.py develop做开发模式安装。开发模式的好处是改源码立即生效,不用反复重装。安装完先跑一遍examples/demo_daily.py,确认能拿到数据。

# 进入源码目录,开发模式安装 cd tushare-src pip install -e . # 设置 token 后运行示例 export TUSHARE_TOKEN="你的token" python examples/demo_daily.py

如果示例报错,先看报错类型:ModuleNotFoundError说明依赖没装全,HTTPError 401说明 token 无效,KeyError说明返回字段和预期不一致。这三种是最常见的。

4.2 批量拉取与本地缓存

单次拉一只股票没意思,实际用的时候都是批量。下面这段代码演示了如何遍历股票列表,把日线数据存成 Parquet,下次直接读本地。

import os import pandas as pd import tushare as ts pro = ts.pro_api() cache_dir = "./data/daily" os.makedirs(cache_dir, exist_ok=True) # 获取全市场股票列表 stocks = pro.stock_basic(exchange="", list_status="L", fields="ts_code") codes = stocks["ts_code"].tolist() for code in codes[:50]: # 先跑 50 只试试 path = os.path.join(cache_dir, f"{code}.parquet") if os.path.exists(path): continue # 已有缓存就跳过 df = pro.daily(ts_code=code, start_date="20230101", end_date="20241231") if df.empty: continue df.to_parquet(path, index=False) print(f"{code} 已缓存 {len(df)} 行")

逻辑说明:先拿股票列表,再逐个拉取,存成 Parquet。Parquet 比 CSV 省空间,读取也快。参数上,list_status="L"表示只取上市状态的股票,避免拉到退市的。这里有个优化点:pro.daily()一次只能拉一只,但有些接口支持按日期拉全市场,那种方式更适合做全量回测。

4.3 数据校验:拉完不等于能用

拉完数据别急着跑策略,先做几项校验:行数是否合理、日期是否连续、价格是否有异常值。下面是一个简单的校验脚本。

import pandas as pd df = pd.read_parquet("./data/daily/000001.SZ.parquet") df["trade_date"] = pd.to_datetime(df["trade_date"], format="%Y%m%d") df = df.sort_values("trade_date") # 检查缺失交易日 full_range = pd.date_range(df["trade_date"].min(), df["trade_date"].max(), freq="B") missing = set(full_range) - set(df["trade_date"]) print(f"缺失交易日数量:{len(missing)}") # 检查价格是否为负 neg = df[(df[["open", "high", "low", "close"]] <= 0).any(axis=1)] print(f"异常价格行数:{len(neg)}")

这段代码检查两件事:工作日是否有缺失、价格是否有非正数。缺失交易日可能是停牌,也可能是数据没拉到;非正价格基本可以判定为脏数据。校验通过再入库,能省掉后面很多玄学问题。

5. 避坑与常见问题:那些年我们踩过的数据接口坑

5.1 现象:接口返回空 DataFrame,但代码没报错

原因:ts_code格式不对,或者日期区间内确实没有数据。tushare 的股票代码必须带交易所后缀,比如000001.SZ,只写000001会返回空。另外,停牌期间也可能返回空。

解决:先确认代码格式,再用trade_cal查一下区间内是否有交易日。如果都没问题,换一只活跃股票试试,排除是单只股票的问题。

5.2 现象:频繁请求后接口变慢或返回限流提示

原因:tushare 对请求频率有限制,具体阈值和账号权限有关。源码里的重试机制如果没做退避,容易触发限流。

解决:在client.py里加请求间隔,比如每次请求后time.sleep(0.3)。批量拉取时用队列控制并发,别开太多线程。我一般会把批量任务放到晚上跑,避开高峰期。

5.3 现象:复权价格和行情软件对不上

原因:tushare 返回的daily()是不复权价格,需要配合adj_factor()自己算。很多人直接拿close做回测,结果除权日出现巨大跳空。

解决:拉取复权因子,按公式后复权价 = 不复权价 × 复权因子 / 最新复权因子计算。或者直接用pro_bar()接口,它内部帮你处理了复权。

# 用 pro_bar 直接拿后复权数据 df = ts.pro_bar(ts_code="000001.SZ", adj="hfq", start_date="20230101", end_date="20241231") print(df[["trade_date", "close"]].head())

参数adj="hfq"表示后复权,qfq是前复权。回测一般用后复权,避免未来函数。

5.4 现象:字段名和文档不一致,KeyError 频发

原因:不同版本源码的字段命名可能有调整,或者接口返回的字段比你请求的少。

解决:不要硬编码字段名,先用df.columns打印出来看。源码里的fields参数建议显式指定,避免依赖默认值。如果做二次封装,把字段映射单独放一个字典里,改起来方便。

5.5 现象:Parquet 文件越存越多,磁盘吃紧

原因:每只股票一个文件,全市场几千只就是几千个文件,小文件多了既占空间又影响读取。

解决:按年份或按交易所合并,比如daily_2024.parquet存全市场当年数据。或者用分区表,按trade_date分区。我一般按月合并,单文件控制在几百 MB,读取时用pandas的columns参数只读需要的列。

6. 二次开发:把接口层改造成自己的数据管道

6.1 封装一个带缓存的 DataFeed 类

源码里的pro_api()是裸接口,每次调用都走网络。二次开发的第一步,是在外面包一层带缓存的DataFeed。下面是一个简化实现:

import os import pandas as pd import tushare as ts class DataFeed: def __init__(self, token, cache_dir="./cache"): ts.set_token(token) self.pro = ts.pro_api() self.cache_dir = cache_dir os.makedirs(cache_dir, exist_ok=True) def daily(self, code, start, end): """优先读本地缓存,没有则请求并落盘""" path = os.path.join(self.cache_dir, f"{code}_{start}_{end}.parquet") if os.path.exists(path): return pd.read_parquet(path) df = self.pro.daily(ts_code=code, start_date=start, end_date=end) if not df.empty: df.to_parquet(path, index=False) return df def adj_daily(self, code, start, end): """后复权行情,内部调用 pro_bar""" return ts.pro_bar(ts_code=code, adj="hfq", start_date=start, end_date=end)

这个类的价值在于:策略代码只依赖DataFeed,不直接依赖 tushare。哪天换数据源,改DataFeed内部实现就行。参数上,cache_dir建议放在项目根目录下的data/里,方便统一管理。

6.2 加一层字段标准化

tushare 返回的字段是英文缩写,不同接口风格还不完全一致。我一般会在DataFeed里加一个_standardize()方法,把列名统一成date, open, high, low, close, volume, amount这种通用格式。

COLUMN_MAP = { "trade_date": "date", "vol": "volume", "amount": "amount", "ts_code": "code" } def _standardize(self, df): df = df.rename(columns=COLUMN_MAP) if "date" in df.columns: df["date"] = pd.to_datetime(df["date"], format="%Y%m%d") return df.sort_values("date").reset_index(drop=True)

这样上层策略就不用关心数据源字段差异,换接口时只改映射表。表格里列出常用映射关系:

原始字段标准字段说明
trade_datedate交易日期
volvolume成交量(手)
amountamount成交额(千元)
ts_codecode股票代码

6.3 验证改造是否成功

改完之后怎么验证?我一般跑三个检查:一是同一只股票,缓存前后数据是否一致;二是复权价格和不复权价格在除权日的比例是否等于复权因子;三是随机抽几个交易日,和行情软件对一下收盘价。这三个过了,基本就能放心用。

feed = DataFeed(token=os.environ["TUSHARE_TOKEN"]) df1 = feed.daily("000001.SZ", "20240101", "20240131") df2 = feed.daily("000001.SZ", "20240101", "20240131") # 第二次走缓存 assert df1.equals(df2), "缓存前后不一致" print("缓存校验通过")

6.4 一个容易忽略的点:异常处理要分层

源码里的异常处理比较粗,网络错误和业务错误混在一起。二次开发时建议分层:网络层抛ConnectionError,业务层抛DataNotFoundError,上层策略只捕获业务异常。这样排查问题时能快速定位是网络挂了还是数据没有。

从那以后我每次封装数据接口,都强制先写一个最小可用的DataFeed,跑通缓存和字段标准化再往上叠策略。这个习惯帮我省了很多重复调试的时间。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

轻量响应式数据流框架Rea:原理、实操与踩坑全解析

轻量响应式数据流框架Rea:原理、实操与踩坑全解析

写响应式处理的时候&#xff0c;很多人上来就是引入一个重量级框架&#xff0c;结果一个简单需求拖了几十个依赖进来。我自己折腾过好几轮类似方案&#xff0c;最后沉淀出一套足够轻、足够容易理解、又能覆盖日常开发核心场景的写法&#xff0c;代号就叫“Rea”。这篇把它的设计…

2026/10/20 12:56:47 阅读更多 →
Claude模型实战指南:从API参数调优到Agent工作流搭建

Claude模型实战指南:从API参数调优到Agent工作流搭建

做AI应用落地这两年&#xff0c;我前后在好几个项目里深度用过Claude系列的模型&#xff0c;从早期的Claude 1代到最近的4代更新&#xff0c;踩过不少坑&#xff0c;也沉淀了一些自己的判断。这篇文章想把Claude系列的技术演进、关键用法和工程实践从头到尾梳理一遍&#xff0c…

2026/10/20 12:56:47 阅读更多 →
Makefile舒适陷阱:6个致命错误与工程化修正方案

Makefile舒适陷阱:6个致命错误与工程化修正方案

刚接手一个别人留下的C工程&#xff0c;第一件事就是打开Makefile看构建逻辑。结果我发现&#xff0c;这已经不是我第一次看到同样的场景了&#xff1a;一个看似完整、随手就能跑通的Makefile&#xff0c;里面堆着几十条规则&#xff0c;依赖关系靠猜&#xff0c;增量构建看命&…

2026/10/20 12:56:47 阅读更多 →

最新新闻

Ubuntu下PostgreSQL安装配置与Navicat连接指南

Ubuntu下PostgreSQL安装配置与Navicat连接指南

做后端开发或者数据相关工作的朋友&#xff0c;应该都经历过这么一幕&#xff1a;好不容易在 Ubuntu 上把 PostgreSQL 装好了&#xff0c;却发现命令行操作没问题&#xff0c;客户端工具却怎么都连不上&#xff1b;或者反过来&#xff0c;Navicat 装好了&#xff0c;连接时报一…

2026/10/20 17:33:06 阅读更多 →
uniCloud Serverless 实战:英语学习小程序全链路开发

uniCloud Serverless 实战:英语学习小程序全链路开发

简介&#xff1a;这是一套面向英语学习者与小程序开发者的完整项目源码&#xff0c;基于uniapp与uniCloud Serverless架构构建&#xff0c;可直接在微信环境中运行。项目集生词本记录、拍照识别单词、内置词典查询、AI语音翻译、跟读打分评测于一体&#xff0c;并针对考研人群补…

2026/10/20 17:33:06 阅读更多 →
WiFi指纹室内定位实战:从原理到Demo搭建

WiFi指纹室内定位实战:从原理到Demo搭建

1. 室内定位的现实困境与WiFi方案的破局点实话实说&#xff0c;第一次接手“基于WiFi的室内定位”这个方向时&#xff0c;我心里是打鼓的。日常大家用GPS导航已经习惯了&#xff0c;掏出手机就能知道自己在哪&#xff0c;但一旦走进大型商场、地下车库、机场航站楼&#xff0c;…

2026/10/20 17:33:06 阅读更多 →
大模型后端长对话记忆架构全解析:从无状态到有状态

大模型后端长对话记忆架构全解析:从无状态到有状态

做后端的人都有过这种体验&#xff1a;用户和AI助手聊了三十轮&#xff0c;前二十轮里明明已经把需求背景、偏好、禁忌都交代清楚了&#xff0c;结果聊到第三十一轮&#xff0c;AI突然反问一句“您刚刚提到的那个项目背景&#xff0c;方便再描述一下吗&#xff1f;”当场血压就…

2026/10/20 17:33:06 阅读更多 →
D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

最近不少做视觉入门的朋友卡在第一关——把D435相机的画面真正“看”到屏幕上。很多人买了一台Intel D435深度相机&#xff0c;兴冲冲插上USB线&#xff0c;结果CPU占用飙高、程序报错、画面黑屏&#xff0c;折腾一晚上就劝退了。其实用D435搭配OpenCV显示彩色图和深度图&#…

2026/10/20 17:33:06 阅读更多 →
自考论文降AI率实战:智能体改写与人工复核全流程

自考论文降AI率实战:智能体改写与人工复核全流程

自考论文写到提交前&#xff0c;最让人血压升高的往往不是查重率&#xff0c;而是学校突然要求提交一份AI检测报告。A同学就是这种状况&#xff1a;论文初稿在查重系统里已经降到合格线&#xff0c;结果学院的通知里多了一句“AI疑似率不得超过20%”。他拿自己的稿子去测了一下…

2026/10/20 17:32:04 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →