mootdx 数据接口实战:手把手搭建分钟级量化监控系统的完整指南
mootdx 数据接口实战手把手搭建分钟级量化监控系统的完整指南【免费下载链接】mootdx通达信数据读取的一个简便使用封装项目地址: https://gitcode.com/GitHub_Trending/mo/mootdxmootdx 是面向通达信行情与财务数据的一站式 Python 数据接口封装库它把晦涩的 TDX 二进制协议、服务器选路、复权因子、财报 zip 解析等脏活全部收敛进几十个高内聚的类里。本文将以从零搭建一套分钟级异动监控 季度财报筛选系统为贯穿全文的业务目标带你逐步掌握实时行情、历史数据、财务挖掘、性能优化与异常处理等完整链路。如果你正在做 A 股量化研究却受困于行情库各有短板、通达信本地文件读不懂、财报数据要手工维护那么本文就是为你准备的。mootdx 的定位是通达信数据读取的简便使用封装它既能在线上直接拉取行情也能离线解析通达信本地数据文件还能自动下载并解析全市场财务报表一条龙解决数据接口的三大痛点。一、先回答三个问题它解决什么、适合谁、凭什么动手写代码之前我们先把项目的价值坐标系建立起来。很多开发者是带着具体痛点来的搞清楚工具边界能帮你少走弯路。1.1 它解决什么问题市面上的行情数据方案大致分三类商业 API 按量收费、爬虫方案随时可能被反爬失效、自研协议解析则要啃晦涩的字节流。mootdx 走的是第四条路——直接复用通达信生态线上行情通过Quotes类连接通达信行情服务器支持实时报价、K 线、分时、分笔、指数、板块、F10、除权除息返回统一规范的 pandas DataFrame本地数据通过Reader类直接读取通达信安装目录下的vipdoc二进制文件日线、分钟线、自定义板块都能解析速度远快于网络请求财务数据通过Affair/Financial自动下载全市场财报 zip 并解析为带中文列名的 DataFrame省去手工维护数据源的烦恼。1.2 适合什么人使用使用者画像典型诉求mootdx 的切入方式个人量化研究者快速拿到干净、可复权的 K 线bars(..., adjustqfq)一行完成前复权策略开发工程师分钟级盘中监控、信号触发quotes实时快照 断线自动重连数据分析师全市场财报横向对比Affair.files()获取文件清单Affair.parse批量解析通达信老用户复用本地已有的 vipdoc 数据Reader.factory(tdxdir...)离线解析1.3 和竞品相比的优势以 pytdx 这类底层协议库为例它能做的事情 mootdx 都能做但 mootdx 在此基础上补齐了工程化能力——服务器自动测速选路、请求重试与超时管理、复权因子计算、财报文件增量下载、pandas 缓存装饰器、甚至配套的 CLI 命令行工具。你会发现同样的活儿用底层库要写两百行胶水代码用 mootdx 往往三五行就结束了。二、搭建环境与打通第一条链路环境配置很简单但有一个小坑值得提前预警首次连接会触发服务器测速如果网络环境不佳建议手动指定服务器而不是依赖自动选路。2.1 安装与依赖核对# 推荐直接安装最新开发版 pip install -U githttps://gitcode.com/GitHub_Trending/mo/mootdx # 如果你偏好命令行工具quotes/reader/affair/bundle 等子命令需要 CLI 依赖 pip install mootdx[cli]安装完成后做一个冒烟测试import pandas as pd from mootdx import __version__ print(fmootdx 版本: {__version__}) print(fpandas 版本: {pd.__version__}) from mootdx.quotes import Quotes client Quotes.factory(marketstd, bestipFalse) print(行情连接初始化成功)2.2 服务器选路的两个姿势mootdx 内置了行情服务器地址池并通过bestip并发测速自动挑选延迟最低的节点。推荐两种用法# 方式一命令行测速结果写入 ~/.mootdx/config.json python -m mootdx bestip --limit 5# 方式二代码内测速拿到最优服务器元组后手动指定 from mootdx.server import bestip servers bestip(limit5, consoleTrue, syncFalse) print(f推荐服务器: {servers[0]}) client Quotes.factory(marketstd, serverservers[0])这里有个坑bestip测速结果会缓存在~/.mootdx/config.json而Quotes.factory(bestipTrue)会在每次实例化时重新测速。如果你的策略是定时任务、追求快速启动建议手动指定server参数把测速交给离线步骤完成。2.3 连接参数的工程化语义StdQuotes的构造函数暴露了几个对生产环境至关重要的参数它们不是摆设参数默认值生产建议作用heartbeatFalseTrue开启心跳保活避免长连接被服务端静默断开auto_retryTrueTrue请求失败自动重试raise_exceptionFalseFalse为 False 时失败返回空 DataFrame配合重试更安全timeout155~10单次请求超时秒数监控场景应调小# 生产环境推荐配置 client Quotes.factory( marketstd, bestipFalse, heartbeatTrue, auto_retryTrue, timeout8, )三、盘中监控的数据采集实时快照、K 线与分笔现在进入本文业务目标的第一块拼图——分钟级监控的数据层。盘中系统对延迟和稳定性要求最高关键在于少建连接、勤取快照。3.1 用单连接批量轮询实时行情quotes方法支持传入代码列表一次调用返回多只股票的实时快照这是监控系统最常用的入口from mootdx.quotes import Quotes class MinuteWatcher: def __init__(self): # 全程只维护一个连接 self.client Quotes.factory( marketstd, heartbeatTrue, timeout8 ) def snapshot(self, symbols): 获取一组股票的实时快照返回 dict[symbol] - DataFrame df self.client.quotes(symbolsymbols) return {s: df[df[code] s] for s in symbols} watcher MinuteWatcher() data watcher.snapshot([600000, 000001, 000858]) print(data[600000])关键点在于quotes内部会通过get_stock_markets自动识别沪深市场前缀你无需手工区分sh/sz。3.2 K 线频率对照表bars的frequency参数同时支持整数和字符串别名映射关系如下整数别名周期05m5 分钟115m15 分钟230m30 分钟31h1 小时4days日线81m1 分钟9day日线103mon季线# 拉取最近 500 根 5 分钟 K 线含前复权 bars watcher.client.bars(symbol600000, frequency5m, adjustqfq, offset500)注意offset单次上限 800 根需要更长历史时用start参数分页拉取或用k(symbol, begin, end)按日期区间自动分页拼接——后者内部已处理节假日与翻页逻辑kdata watcher.client.k(symbol600000, begin2024-01-01, end2024-12-31)3.3 分时与分笔捕捉异动的最后一环监控系统里分笔数据用来确认大单行为分时数据用来还原日内走势# 当日分时 minute_df watcher.client.minute(symbol600000) # 历史某日分时 hist_minute watcher.client.minutes(symbol600000, date20240115) # 当日分笔成交前 800 笔 tx watcher.client.transaction(symbol600000, start0, offset800) # 历史分笔 hist_tx watcher.client.transactions(symbol600000, date20240115, start0, offset800)四、历史数据批量解析技巧吃透本地 vipdoc 文件盘中数据靠网络回测数据则要稳定、快速、可复现。如果你本机装有通达信客户端Reader类能把本地二进制数据变成同样的 DataFrame而且零网络开销。4.1 先理解通达信的目录结构通达信数据根目录下行情文件统一放在vipdoc中vipdoc/ ├── sh/lday/sh600000.day # 沪市日线 ├── sz/lday/sz000001.day # 深市日线 ├── sh/minline/sh600000.lc1 # 1 分钟线 ├── sh/fzline/sh600000.lc5 # 5 分钟线 └── ds/lday/4#XXXX.day # 扩展市场带 # 前缀Reader的find_path方法会根据代码前缀自动判断市场、补全sh/sz前缀并匹配扩展名你完全不需要手工拼路径。4.2 批量读取日线与分钟线from pathlib import Path from mootdx.reader import Reader class LocalArchive: def __init__(self, tdxdir: str): # tdxdir 指向通达信安装根目录如 /data/tdx 或 C:/new_tdx self.reader Reader.factory(marketstd, tdxdirtdxdir) def load_daily(self, symbols, start_dateNone): result {} for code in symbols: df self.reader.daily(symbolcode) if df is None or df.empty: continue df df.reset_index() if start_date: df df[df[date] start_date] result[code] df return result def load_minutes(self, symbols, period1): # period1 取 1 分钟线period5 取 5 分钟线fzline return { code: self.reader.minute(symbolcode, suffixperiod) for code in symbols } archive LocalArchive(/data/tdx) daily archive.load_daily([600000, 000001], start_date2023-01-01)一个值得注意的细节Reader.daily内部对代码做了Path(symbol).stem处理因此你传sh600000、600000甚至带路径的字符串都能被正确归一化。4.3 板块数据的读写自选板块、概念板块在通达信里对应T0002/blocknew目录下的*.blk文件。mootdx 提供了完整的增删查改能力# 读取系统板块板块文件如 block_gn.dat 等在 parse 层处理 block_df archive.reader.block(symbolblock_gn.dat, groupFalse) # 自定义板块把候选股票写入一个名为异动池的板块 from mootdx.tools.customize import Customize custom Customize(tdxdir/data/tdx) custom.create(name异动池, symbol[600000, 000001, 000858]) # 按名称检索板块内代码 codes archive.reader.block_new(name异动池) print(codes)五、财务数据挖掘从季度财报 zip 到可分析数据集策略如果只看量价很容易在基本面突变上翻车。mootdx 的财务链路把全市场财报这个原本要花大功夫维护的数据源变成了三个方法的组合。5.1 文件清单与增量下载通达信服务器会维护一份gpcw.txt清单Affair.files()直接解析出所有可用财报文件名、大小与 MD5 哈希。利用哈希可以做到真正的增量同步import hashlib from pathlib import Path from mootdx.affair import Affair class FinancialMirror: def __init__(self, store_dir./finance_store): self.store Path(store_dir) self.store.mkdir(exist_okTrue, parentsTrue) def sync(self): manifest Affair.files() for item in manifest: target self.store / item[filename] if target.exists(): local_hash hashlib.md5(target.read_bytes()).hexdigest() if local_hash item[hash]: continue # 已是最新跳过下载 Affair.fetch(downdirstr(self.store), filenameitem[filename]) return len(manifest) mirror FinancialMirror() print(f已同步 {mirror.sync()} 个财报文件)Affair.fetch内部通过TqdmUpTo展示下载进度并且下载目录不存在时会自动创建这些细节都替你处理好了。5.2 解析指定季度与股票财报 zip 解压后是结构化二进制FinancialReader().to_data负责把它转成带中文列名的 DataFramefrom mootdx.financial.financial import FinancialReader reader FinancialReader() df reader.to_data(./finance_store/gpcw2024.zip, headerzh) print(df.head()) print(list(df.columns)) # 中文列名如 总股本、净利润 等拿到全市场某季度数据后横向筛选就非常顺手# 筛选净利润同比为正且总股本大于阈值的标的 candidates df[ (df[净利润] 0) (df[总股本] 5e8) ] print(candidates.index.tolist())5.3 配合除权除息做回测对齐财务数据给出的是报告期快照而回测 K 线需要复权对齐。xdxr方法返回股票的除权除息记录配合bars的adjust参数即可完成前/后复权# 除权除息信息 xdxr_df watcher.client.xdxr(symbol600000) # 拉取前复权日线直接用于回测 qfq_bars watcher.client.bars(symbol600000, frequency9, adjustqfq)六、性能调优把重复请求拦在缓存层盘中监控最怕两件事重复请求拖垮带宽、异常抖动导致数据缺失。这一节解决怎么让系统跑得更省、更稳。6.1 用 pd_cache 实现磁盘级结果缓存pd_cache装饰器会把函数的源码哈希与入参组合成缓存键结果落盘为 pkl并支持过期时间from mootdx.utils.pandas_cache import pd_cache # 10 分钟内同样的参数直接命中缓存 pd_cache(cache_dir./.pd_cache, expired600) def load_daily_cached(symbol: str, adjust: str qfq): from mootdx.quotes import Quotes client Quotes.factory(marketstd, heartbeatTrue) return client.bars(symbolsymbol, frequency9, adjustadjust) bars load_daily_cached(600000, qfq)这个装饰器非常适合定时任务 日内多次调用同一标的的场景K 线在盘中其实没必要每秒重拉。6.2 批量任务与结果落地CLI 的bundle子命令就是批量拉取的标准范例——一次连接、循环取数、按扩展名落盘python -m mootdx bundle -s 600000,000001,000858 -a daily -e csv -o ./output代码里同样有to_file这个万能落盘函数按扩展名自动选择格式from mootdx.utils import to_file to_file(qfq_bars, ./output/600000_qfq.csv) # CSV to_file(qfq_bars, ./output/600000_qfq.xlsx) # Excel to_file(qfq_bars, ./output/600000_qfq.h5) # HDF56.3 重试、异常与日志的工程化组合行情接口的失败往往是瞬时的网络抖动、服务器换线正确姿势是分类重试 静默兜底。mootdx 的异常体系以MootdxException为根同时请求层默认auto_retry失败返回空 DataFrame 而非抛异常import logging import time from functools import wraps from mootdx.exceptions import MootdxException, MootdxValidationException logging.basicConfig( levellogging.INFO, format%(asctime)s %(levelname)s %(name)s: %(message)s, ) def resilient(max_tries4, backoff1.5): def deco(func): wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_tries): try: return func(*args, **kwargs) except (MootdxException, MootdxValidationException) as exc: logging.warning(调用 %s 第 %d 次失败: %s, func.__name__, attempt 1, exc) if attempt max_tries - 1: raise time.sleep(backoff * (attempt 1)) return None return wrapper return deco resilient(max_tries4) def safe_snapshot(symbol): from mootdx.quotes import Quotes return Quotes.factory(marketstd).quotes(symbolsymbol)另外提醒一点quotes返回空 DataFrame 属于正常失败用df.empty判断即可别指望它抛异常——这也是防御式编程的常见陷阱。七、系统落地把前面所有能力拼成一台分钟级监控器现在是验收时刻。我们把前六节的零件组装成一台分钟级异动监控 季度财报初筛系统跑通完整的业务闭环。7.1 整体架构与模块划分minute_monitor/ ├── collector.py # 数据采集快照 / K线 / 分笔 ├── archive.py # 本地归档Reader 批量读取 ├── screener.py # 财报筛选Affair 同步与解析 ├── cache_layer.py # 缓存与落盘pd_cache to_file └── main.py # 定时调度与告警职责单一、边界清晰任何一环出问题都不会拖垮整体。7.2 核心实现# collector.py —— 采集模块 from mootdx.quotes import Quotes WATCH_LIST [600000, 000001, 000858] class Collector: def __init__(self): self.client Quotes.factory( marketstd, heartbeatTrue, auto_retryTrue, timeout8 ) def snapshot_all(self): return self.client.quotes(symbolWATCH_LIST) def recent_5m(self, symbol, count120): return self.client.bars(symbolsymbol, frequency5m, offsetcount) # screener.py —— 财报初筛模块 from mootdx.financial.financial import FinancialReader from mootdx.affair import Affair class QuarterlyScreener: def __init__(self, store_dir./finance_store): self.store_dir store_dir Affair.fetch(downdirstore_dir, filenamegpcw2024.zip) def screen(self): df FinancialReader().to_data( f{self.store_dir}/gpcw2024.zip, headerzh ) return df[(df[净利润] 0)].index.tolist()7.3 调度与运维要点# main.py —— 调度入口 import time from collector import Collector from screener import QuarterlyScreener def main(): collector Collector() screener QuarterlyScreener() while True: snap collector.snapshot_all() # 异动判定涨幅超过阈值则记录并落盘 alert snap[snap[涨幅].abs() 3] if not alert.empty: print(f[{time.strftime(%H:%M:%S)}] 异动: {alert[[code, price]].to_dict(records)}) time.sleep(60) # 分钟级轮询 if __name__ __main__: main()部署时建议配合系统级systemd或crontab管理生命周期日志统一走标准库logging把监控器挂到日志平台。别忘了用python -m mootdx bestip定期刷新服务器并在启动前做一次连通性自检。八、总结与下一步行动清单至此我们从环境搭建出发依次打通了行情服务器选路、实时快照与 K 线采集、本地二进制归档解析、财报增量同步与解析、缓存与异常治理最终组装出一台可运行的分钟级监控系统。你会发现整条链路的共同点mootdx 把每个环节的脏活都封装成了语义清晰的类与函数让开发者把精力集中在策略与业务上。接下来可以按这个顺序继续深入读源码重点看mootdx/quotes.py的StdQuotes与mootdx/reader.py的find_path理解市场判定与文件匹配规则跑测试项目自带覆盖行情、读取器、财务、工具函数的完整测试套件运行一遍能快速建立 API 心智模型扩展场景把监控系统接上指标计算如 MACD/RSI或把财报筛选结果推送到自选板块Customize.create上生产用pd_cache压住重复请求用resilient装饰器兜底瞬时故障用 CLI 子命令做数据批量初始化。数据接口的稳定性决定了量化系统的下限而 mootdx 的价值正在于把这条下限拉得足够高。现在就把上面第一个示例跑起来你的量化之旅已经开始了。【免费下载链接】mootdx通达信数据读取的一个简便使用封装项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

G-Helper完整实测:卸掉Armoury Crate之后,我的华硕笔记本安静了

G-Helper完整实测:卸掉Armoury Crate之后,我的华硕笔记本安静了

G-Helper完整实测:卸掉Armoury Crate之后,我的华硕笔记本安静了 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, V…

2026/10/18 22:20:53 阅读更多 →
3 步给 Bootstrap 页面装上专业取色器,bootstrap-colorpicker 实战记录

3 步给 Bootstrap 页面装上专业取色器,bootstrap-colorpicker 实战记录

3 步给 Bootstrap 页面装上专业取色器,bootstrap-colorpicker 实战记录 【免费下载链接】bootstrap-colorpicker bootstrap-colorpicker - 这是一个基于 Bootstrap 的开源前端库,提供了丰富的颜色选择器组件,可以用于构建漂亮的 Web 应用程序…

2026/10/18 23:14:35 阅读更多 →
扩散模型学习资源终极指南:Awesome-Diffusion-Models 从入门到实战

扩散模型学习资源终极指南:Awesome-Diffusion-Models 从入门到实战

扩散模型学习资源终极指南:Awesome-Diffusion-Models 从入门到实战 【免费下载链接】Awesome-Diffusion-Models A collection of resources and papers on Diffusion Models 项目地址: https://gitcode.com/gh_mirrors/aw/Awesome-Diffusion-Models 想系统学…

2026/10/18 23:13:51 阅读更多 →

最新新闻

DeskcommCRM实战:轻量级客户管理与销售跟进全流程落地指南

DeskcommCRM实战:轻量级客户管理与销售跟进全流程落地指南

1. 项目概述:DeskcommCRM 到底是什么,以及为什么值得关注这两年市面上叫 CRM 的产品多得数不过来,但绝大多数都陷入了一个怪圈:功能越做越重,权限越加越细,你要是只为了管好手头几百个客户,得先…

2026/10/19 23:47:06 阅读更多 →
以沟通为核心的轻量级CRM系统设计与实践

以沟通为核心的轻量级CRM系统设计与实践

一个叫 DeskcommCRM 的项目,听起来有点像某个内部系统的代号,但实际上它代表了一类特别值得聊的东西:以“沟通”为核心的客户关系管理工具。我一开始接触这个项目名的时候,第一反应是“Deskcomm”可能是 Desk Communication 的缩写…

2026/10/19 23:47:06 阅读更多 →
从线程池失控到全局限流:高并发系统稳定性实战指南

从线程池失控到全局限流:高并发系统稳定性实战指南

1. 并发失控的第一现场:线程池是怎么被击穿的先说一个我实际排查过的案例场景。某公司核心交易链路的订单服务,平时性能表现很稳定,高峰期单机支撑上千QPS没有任何压力。但一次大促活动中,下游的库存服务因为数据库连接池被打满&a…

2026/10/19 23:39:50 阅读更多 →
STC单片机范例程序实战指南:从点灯到多外设工程搭建

STC单片机范例程序实战指南:从点灯到多外设工程搭建

简介:这是一份面向嵌入式初学者与单片机开发者的STC单片机C语言范例程序合集,围绕ADC采样、串口通信、EEPROM读写、ISP在系统编程、时钟中断、端口测试及优化编程思想等核心功能展开,适合用于课程实验、自学练手与项目参考。资源包共612个文件…

2026/10/19 23:39:50 阅读更多 →
Kafka全链路性能调优:吞吐量与延迟的实战权衡

Kafka全链路性能调优:吞吐量与延迟的实战权衡

前阵子业务告警群里又炸了——消费延迟从平时的几百毫秒一路飙到分钟级,P99 延迟曲线在监控面板上直接拉成一条陡坡。接手这套 Kafka 系统两年,类似的戏码每隔一阵就重演一次。与其每次都临时救火,不如把全链路参数优化这件事系统捋一遍。这篇…

2026/10/19 23:37:47 阅读更多 →
ZXing.Net 0.16.8.0 条码识别实战:从解码链路到性能调优

ZXing.Net 0.16.8.0 条码识别实战:从解码链路到性能调优

简介:ZXing.Net.0.16.8.0 是面向 C# 与 Unity 开发者的开源条码处理库,基于 Zebra Crossing 项目,可在 .NET 框架与 Unity 引擎中实现 QR 码、Aztec、Code 128、EAN 等多种条码的读取与生成。开发者只需调用少量 API 即可为桌面应用、移动端或…

2026/10/19 23:37:47 阅读更多 →

日新闻

周新闻

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

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

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

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

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

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

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

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

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

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 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/19 2:54:50 阅读更多 →
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/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →