User Agent 详解:从结构解析到实战 UA 池与轮换技巧
1. 从一个不起眼的字符串说起为什么我们需要认识 User Agent如果你做过爬虫、写过接口自动化测试、或者只是单纯在浏览器里按过 F12 看过 Network 面板那你一定见过那一长串看起来像乱码的字符串——Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36。这就是User Agent简称 UA。它本质上就是客户端在发起 HTTP 请求时主动贴在脑门上的一张“自我介绍便签”告诉服务端“我是谁、我来自哪里、我大概能干什么”。很多人第一次接触 UA 是因为遇到了“同一个页面浏览器打开正常代码请求却返回一堆乱码或者直接 403”。这背后的核心原因往往就是服务端根据 UA 做了内容协商或者访问控制。UA 能做的事情远比想象中多它决定了服务端返回桌面版还是移动版页面、决定是否触发反爬策略、影响 CDN 缓存命中、甚至影响某些前端库的兼容性分支。可以说UA 是客户端与服务端之间最廉价、最普遍、也最容易被忽视的“身份凭证”。这篇文章适合三类人看第一类是刚入门爬虫和数据采集的朋友经常被 UA 卡住却不知道从哪下手第二类是做前端兼容和自动化测试的工程师需要模拟不同设备环境第三类是对 HTTP 协议细节感兴趣、想把“知其所以然”补齐的开发者。我会从 UA 的结构讲起把常见 UA 拆开揉碎再给出可直接复用的 UA 列表、构造方法和实战避坑经验。读完你至少能做到看到一串 UA 能立刻判断出浏览器内核、操作系统、设备类型并且知道在什么场景下该用哪一类 UA。2. User Agent 的结构拆解一串字符里到底藏了多少信息2.1 UA 的历史包袱与字段构成UA 字符串之所以看起来又长又乱是因为它背负了三十年的历史包袱。最早的浏览器叫 Mosaic后来 Netscape 的浏览器代号是 Mozilla为了兼容只认 Mozilla 的服务器后来的浏览器纷纷在 UA 开头加上Mozilla/5.0来“伪装”自己。这个传统一直延续到今天所以你会看到 Chrome、Edge、Safari 的 UA 全都以Mozilla/5.0开头这纯粹是历史遗留的兼容性妥协跟 Mozilla 本身已经没什么关系了。一个典型的现代 UA 大致由以下几段组成用空格和括号分隔Mozilla/5.0历史兼容前缀几乎所有主流浏览器都保留。(操作系统标识; 加密等级标识; 浏览器语言)括号内是平台信息比如Windows NT 10.0; Win64; x64表示 64 位 Windows 10。AppleWebKit/537.36渲染引擎标识WebKit 系内核的通用标记。(KHTML, like Gecko)又一处历史兼容标记表示“我像 Gecko 一样渲染”。Chrome/120.0.0.0真正的浏览器名称和版本号。Safari/537.36为了兼容 Safari 检测而保留的尾巴。理解这个结构之后你就能像读身份证一样读 UA 了。比如看到iPhone; CPU iPhone OS 17_0就知道是 iOS 17 的 iPhone看到Android 13; Pixel 7就知道是安卓 13 的某款设备。这种“逐段解析”的能力是后面所有实战操作的基础。2.2 主流浏览器 UA 的差异对比不同浏览器厂商在 UA 里塞的东西并不一样这直接影响了服务端的判断逻辑。我整理了一张对比表把几个主流桌面浏览器的 UA 关键字段列出来方便你快速对照浏览器内核标识典型版本字段特殊标记ChromeAppleWebKit/537.36Chrome/120.0.0.0无EdgeAppleWebKit/537.36Edg/120.0.0.0注意是Edg不是EdgeFirefoxGecko/20100101Firefox/121.0不含 AppleWebKitSafariAppleWebKit/605.1.15Version/17.0含 Safari/605.1.15这里有个非常容易踩的坑Edge 的 UA 里写的是Edg/而不是Edge/。早期 Edge 用的是Edge/后来为了和某些老检测逻辑区分改成了Edg/。如果你写正则去匹配 Edge用Edge会漏掉一大批新版本。同理Firefox 的 UA 里根本没有AppleWebKit如果你用“是否包含 AppleWebKit”来判断是不是现代浏览器Firefox 会被误判。这些细节在写兼容逻辑时都是实打实的坑。2.3 移动端 UA 的识别要点移动端 UA 比桌面端更复杂因为设备碎片化严重。但抓住几个关键词就能覆盖绝大多数情况iPhone包含iPhone系统字段形如CPU iPhone OS 17_0 like Mac OS X。iPad包含iPad注意 iPadOS 13 之后默认请求桌面版 UA需要额外处理。Android 手机包含Android和Mobile比如Android 13; Pixel 7 Build/...。Android 平板包含Android但通常不含Mobile这是区分手机和平板的关键。微信内置浏览器包含MicroMessenger这个标记在做微信生态适配时极其重要。提示判断移动端时不要只匹配Mobile一个词。有些安卓平板 UA 里也会出现Mobile稳妥做法是结合Android和屏幕尺寸信息综合判断。3. 常见 User Agent 实战清单可直接抄作业的 UA 库3.1 桌面端高频 UA 推荐在实际做数据采集时最省事的做法是维护一个 UA 池每次请求随机挑一个。下面这些是我实测下来比较稳定、不容易被识别为异常的桌面 UA你可以直接拿去用DESKTOP_UA_POOL [ # Chrome on Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, # Chrome on macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, # Firefox on Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0, # Safari on macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15, # Edge on Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0, ]选这些 UA 的逻辑是它们都是当前主流版本覆盖面广且版本号不会太旧。版本号太旧是常见失误——你用一个 Chrome 80 的 UA 去请求服务端一看就知道这是“老古董”很容易触发风控。所以 UA 池需要定期更新建议每季度检查一次主流版本号。3.2 移动端 UA 与设备模拟移动端采集或者做响应式测试时需要模拟具体设备。下面这组覆盖了 iOS、Android、微信等高频场景MOBILE_UA_POOL [ # iPhone Safari Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1, # Android Chrome Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36, # 微信内置浏览器 Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.44, # Android 平板 Mozilla/5.0 (Linux; Android 13; SM-X700) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, ]这里要特别说明微信 UA 的用法。微信内置浏览器的 UA 里带MicroMessenger很多做微信分享、H5 页面适配的逻辑都依赖这个标记。如果你要测试“在微信里打开是什么效果”就必须把 UA 换成带MicroMessenger的否则服务端会返回普通浏览器版本测出来的结果完全不对。3.3 特殊场景 UA搜索引擎与爬虫标识除了浏览器还有一类 UA 是搜索引擎爬虫和自动化工具的。这类 UA 的特点是“自报家门”比如GooglebotMozilla/5.0 (compatible; Googlebot/2.1; http://www.google.com/bot.html)BingbotMozilla/5.0 (compatible; bingbot/2.0; http://www.bing.com/bingbot.htm)通用爬虫python-requests/2.31.0、curl/8.4.0、Go-http-client/1.1这里有个关键认知用python-requests这种默认 UA 去请求等于直接告诉服务端“我是脚本”。绝大多数有风控的站点会第一时间拦截。所以做采集时第一件事就是把默认 UA 换掉。反过来如果你自己写服务端想识别爬虫也可以从这些默认 UA 入手做基础过滤。4. 动手实操构造、解析与轮换 UA 的完整流程4.1 用 Python 构造和发送自定义 UA最基础的用法是在请求头里手动指定 UA。以requests库为例import requests 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, Accept-Language: zh-CN,zh;q0.9,en;q0.8, } resp requests.get(https://example.com, headersheaders, timeout10) print(resp.status_code)注意我同时加了Accept-Language。只改 UA 不改其他头是很容易被识破的。因为一个真实的 Chrome 请求除了 UA 还会带Accept、Accept-Language、Accept-Encoding、Connection等一系列头。如果你的请求只有 UA 像浏览器其他头都是 requests 默认值风控系统一眼就能看出破绽。所以正确的做法是“成套伪装”把常用请求头一起补上。4.2 用正则解析 UA 提取关键信息有时候我们需要反过来从一串 UA 里提取浏览器、系统、设备信息。用正则是最直接的方式import re def parse_ua(ua: str) - dict: result {browser: Unknown, os: Unknown, device: Desktop} # 浏览器识别注意顺序Edge 要在 Chrome 之前判断 if Edg/ in ua: result[browser] Edge elif Firefox/ in ua: result[browser] Firefox elif Chrome/ in ua: result[browser] Chrome elif Safari/ in ua: result[browser] Safari # 系统识别 if Windows NT in ua: result[os] Windows elif Mac OS X in ua: result[os] macOS elif Android in ua: result[os] Android elif iPhone in ua or iPad in ua: result[os] iOS # 设备类型 if Mobile in ua or iPhone in ua: result[device] Mobile elif iPad in ua or (Android in ua and Mobile not in ua): result[device] Tablet return result ua Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0 print(parse_ua(ua)) # {browser: Edge, os: Windows, device: Desktop}这段代码里最关键的是判断顺序。Edge 的 UA 里同时包含Chrome/和Edg/如果你先判断 ChromeEdge 就会被误判成 Chrome。同理很多国产浏览器 UA 里也带Chrome/需要根据额外标记区分。这个“先特殊后一般”的判断顺序是写 UA 解析逻辑的铁律。4.3 构建可轮换的 UA 中间件在规模化采集时单个 UA 反复请求同一个站点很容易被限流。这时候需要 UA 轮换。下面是一个简单的轮换中间件思路import random class UARotator: def __init__(self, ua_pool): self.ua_pool ua_pool self._last_ua None def get(self): # 避免连续两次拿到同一个 UA candidates [ua for ua in self.ua_pool if ua ! self._last_ua] ua random.choice(candidates) self._last_ua ua return ua rotator UARotator(DESKTOP_UA_POOL) for _ in range(3): print(rotator.get()[:60], ...)这个实现里我加了一个“避免连续重复”的小逻辑。原因是如果随机池里只有 5 个 UA纯随机很容易连续两次抽到同一个而连续相同 UA 的请求模式反而更可疑。这个细节在常规教程里很少提但实测下来对降低触发风控的概率有帮助。5. 常见问题与排查技巧实录5.1 UA 相关的典型报错与解决思路做采集和接口调试时跟 UA 相关的问题主要集中在下面几类。我整理成速查表遇到问题可以直接对照现象可能原因排查与解决返回 403 ForbiddenUA 被识别为脚本换成主流浏览器 UA补齐其他请求头返回内容为移动版UA 被判定为移动设备检查 UA 是否含 Mobile换成桌面 UA返回乱码或空内容服务端按 UA 返回了不同编码检查响应头 Content-Type确认 UA 与预期一致页面元素定位失败服务端返回了不同版本页面固定 UA确保每次请求环境一致登录态频繁失效UA 变化导致会话校验失败同一会话内保持 UA 不变这里重点说“登录态失效”这一条。很多站点的会话校验会把 UA 作为指纹的一部分如果你在同一个会话里换了 UA服务端会认为“换人了”直接踢掉登录态。所以UA 轮换只适合无状态请求涉及登录的会话必须固定 UA。这个坑我在早期做自动化时踩过排查了大半天才定位到是 UA 变了。5.2 那些文档里不会写的避坑经验第一个经验不要迷信“最新版本 UA 最好”。有些站点的风控规则是“只允许最近 N 个版本”你用一个刚发布没几天的版本号反而可能因为规则库还没更新而被拦。稳妥做法是用“当前主流版本往前推一到两个大版本”比如现在主流是 120你用 118 或 119 通常更安全。第二个经验UA 要和 IP、时区、语言保持一致。如果你用美国 IP却配一个zh-CN的语言头和中文系统 UA这种“地理矛盾”很容易被识别。做海外采集时UA 里的系统语言、请求头的Accept-Language、出口 IP 的地理位置三者最好能对上。第三个经验移动端 UA 不要随便混用。比如你用一个 iPhone 的 UA但请求头里Accept写的是桌面浏览器的值或者屏幕分辨率参数对不上也会露馅。真要做移动端模拟建议直接用成熟的设备模拟方案把 UA、视口、触摸事件等一整套都配齐而不是只改一个 UA 字符串。注意UA 只是客户端指纹的一部分。现代风控还会看 TLS 指纹、Canvas 指纹、鼠标轨迹等。UA 能解决大部分“低级拦截”但面对高级风控单靠 UA 是不够的。认清这一点能帮你少走很多弯路。5.3 服务端视角如何正确使用 UA 做判断如果你站在服务端想用 UA 做设备适配有几点建议。首先优先用响应式设计和特性检测而不是 UA 判断。UA 判断是出了名的脆弱浏览器版本一更新、UA 一改你的判断逻辑就可能失效。其次如果非要用 UA一定要做“优雅降级”——判断不出来时返回通用版本而不是直接报错。最后不要把 UA 当作安全边界。UA 是客户端可以随意伪造的任何基于 UA 的“权限控制”都是纸糊的真正的安全校验必须放在服务端会话和令牌层面。6. 关于 UA 的一些个人体会我做了这么多年采集和自动化UA 这个看似最简单的东西反而是最容易被低估的。新手往往觉得“随便填一个就行”结果卡在 403 上半天找不到原因老手则容易过度依赖 UA 池忽略了请求头一致性、会话固定这些更本质的问题。我的建议是把 UA 当成整个请求指纹体系的一环来对待而不是孤立的一个字段。维护一个定期更新的 UA 池配合一致的请求头、合理的请求频率基本能覆盖 80% 的常规场景。剩下那 20% 的硬骨头靠的是对目标站点行为的细致观察而不是堆砌更多的 UA。这个内容后续还可以往“客户端指纹体系”方向扩展把 TLS、Canvas、字体列表这些维度串起来看会更有意思。

相关新闻

AI入门指南:技术小白零基础学习路径与避坑大全

AI入门指南:技术小白零基础学习路径与避坑大全

这几年AI的热度能把人烤化,但随之而来的不是人人都会用AI,而是一大批技术小白站在门口疯狂内耗:不知道从哪开始、不知道该学什么、买书买课买了一堆却越看越慌、甚至还没动手就觉得自己"不是这块料"。我自己当年从零接触AI时也是这…

2026/10/16 12:16:24 阅读更多 →
决策成本暴降 400 倍:不写文章的模型,正在重写 AI 的性价比公式

决策成本暴降 400 倍:不写文章的模型,正在重写 AI 的性价比公式

决策成本暴降 400 倍:不写文章的模型,正在重写 AI 的性价比公式 【免费下载链接】openjev 项目地址: https://ai.gitcode.com/hf_mirrors/AlexWortega/openjev 过去一个月,技术社区被一个"不写文章的模型"刷了屏。TypeSafe…

2026/10/16 12:16:24 阅读更多 →
智诺方AI|盲审前毕业论文AI辅助写作边界,弱化AIGC痕迹的正确思路

智诺方AI|盲审前毕业论文AI辅助写作边界,弱化AIGC痕迹的正确思路

从风险倒推:为什么「AI辅助与学术边界」总被盯 核心判断:先把研究写清楚,再谈降重与降 AIGC,顺序反了必翻车。 AI 辅助写作的边界正在变清晰:可用工具润色与降痕,不能用工具替代研究与代写。讲清边界&#…

2026/10/16 12:16:24 阅读更多 →

最新新闻

数据结构第一章时间复杂度选择题全解析:从大O记号到实战避坑

数据结构第一章时间复杂度选择题全解析:从大O记号到实战避坑

1. 为什么时间复杂度是数据结构的第一道门槛很多人翻开数据结构的第一章,看到时间复杂度那一堆大O符号,第一反应就是"这玩意儿到底有啥用"。我当年也是这么想的,觉得不就是数数循环跑了几次嘛,至于搞得这么正式吗。后来…

2026/10/16 15:12:31 阅读更多 →
Python 3.12中dir()排序变化:__dir__魔术方法的底层原理与实战指南

Python 3.12中dir()排序变化:__dir__魔术方法的底层原理与实战指南

先把结论放在前面: __dir__ 这个魔术方法,在Python整个对象模型里存在感一直不算高,但它几乎是所有“探索对象属性”的工具的底层依赖。你平时用的 dir() 、IDE的自动补全、调试器的变量面板、甚至有些序列化库在收集字段时的内部逻辑&am…

2026/10/16 15:12:31 阅读更多 →
Python魔术方法__dir__:控制dir()输出与补全体验

Python魔术方法__dir__:控制dir()输出与补全体验

聊到Python魔术方法,dir__算是存在感很低的那一类。它平时不显山露水,但只要你敲过dir(obj),就已经在跟它打交道了——dir()函数的底层协议就是__dir。这个系列走到现在,在Python 3.12环境里逐一验证,我发现__dir__的表…

2026/10/16 15:12:31 阅读更多 →
Hadoop 2.7.6 集群部署搭建实战:从环境规划到任务验证

Hadoop 2.7.6 集群部署搭建实战:从环境规划到任务验证

1. 为什么还要聊 Hadoop 2.7.6 这个“老版本”1.1 一个绕不开的现实场景现在打开任何一个技术社区,讨论大数据框架的内容几乎被 Spark、Flink、Doris、Iceberg 这些新面孔占满了。Hadoop 看起来像是上个时代的产物,MapReduce 更是被很多人当成“历史遗迹…

2026/10/16 15:12:31 阅读更多 →
内网环境自建Yum仓库实战:从原理到多机共享

内网环境自建Yum仓库实战:从原理到多机共享

1. 项目缘起与整体设计思路 1.1 为什么内网环境必须自建软件仓库 很多刚接触Linux运维的朋友会有一个思维惯性:装软件不就是敲一条 yum install 的事吗?在能直连外部镜像源的机器上确实如此,但一旦进入企业内网、生产隔离区或者实验室的封…

2026/10/16 15:12:31 阅读更多 →
Java提取字符串日期年份:从substring到LocalDate的完整方案

Java提取字符串日期年份:从substring到LocalDate的完整方案

上周做报表需求时碰到一个很不起眼的问题:数据源从老系统导出来,日期字段全是String类型,规范统一是"2004-07-11"这种写法。统计口径要按年份分组,所以第一步就变成了一道经典 Java 小题——从 String 类型的"2004…

2026/10/16 15:11:30 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →