网络编程之发送HTTP请求获取网上的信息
简介HTTP请求HTTP是应用层的协议是我们上网最常使用到的协议这篇博文来讲讲我们可以如何使用HTTP来获取到网页上的一些数据首先我们需要知道HTTP的请求有哪些方法作用常用程度有无Body幂等性GET获取资源查⭐⭐⭐⭐⭐❌✅POST创建资源/提交数据增⭐⭐⭐⭐⭐✅❌PUT完整替换资源全改⭐⭐⭐✅✅PATCH部分更新资源改⭐⭐⭐✅❌DELETE删除资源删⭐⭐⭐❌✅HEAD只获取响应头⭐⭐❌✅OPTIONS查询服务器支持的方法⭐⭐❌✅CONNECT建立隧道连接代理⭐❌❌TRACE回显请求内容调试⭐❌✅从上面我们可以看到如果我们只是从网络上面获取数据的话知道GET请求和POST请求就行了因为网上大部分的API都是使用的GET和POST请求的其他请求在获取信息上基本看不见这篇文字将会着重在GET和POST请求的构成和使用上GET请求首先我们来看GET请求GET请求的具体格式GET /path/to/resource?key1value1key2value2 HTTP/1.1必须Host: www.example.com必须User-Agent: MyClient/1.0Accept: text/html,application/jsonAccept-Language: zh-CN,zh;q0.9Accept-Encoding: gzip, deflateConnection: keep-aliveCache-Control: no-cacheCookie: session_idabc123If-Modified-Since: Wed, 12 Aug 2026 07:28:22 GMT别看这里有这么多其实必须的只有两行其他的都是可选项如果平台API没用硬性的GET请求要求的话GET /path/to/resource?key1value1key2value2 HTTP/1.1Host: www.example.com这就是最小的GET请求了我们来一行一行拆解第一行GET /path/to/resource?key1value1key2value2 HTTP/1.1我们可以拆成3个部分来看GET /path/to/resource?key1value1key2value2 HTTP/1.1请求类型 URL?查询参数 HTTP的版本号1请求类型我们这里使用的是GET请求嘛所有请求类型是GET这很合理2URLURL指的是这个服务在网页下的路径一般API都会给出参考的如下图这个是高德地图开放平台中天气查询的GET请求相关参数URL是.com之后的部分如下演示一般https://restapi.amap.com/v3/assistant/coordinate/converthttps:// restapi.amap.com /v3/assistant/coordinate/convert协议 域名 URL这样我们就知道了域名和URL分别是什么了3参数参数都是紧跟在URL后面用?隔开参数一般就需要程序员根据API提供的来写如下图4HTTP版本号这里版本号是1.1说明这是1.1版本的HTTP怎么确认这个HTTP的版本号填多少呢首先可以在官网的API下面找要是想偷懒的话也可以问问AI基本都是1.1版本的这个比较常用第二行Host: www.example.comHost: www.example.com关键字 访问的域名这个我们刚刚分析了在URL那里第三----N行User-Agent: MyClient/1.0Accept: text/html,application/jsonAccept-Language: zh-CN,zh;q0.9Accept-Encoding: gzip, deflateConnection: keep-aliveCache-Control: no-cacheCookie: session_idabc123If-Modified-Since: Wed, 12 Aug 2026 07:28:22 GMTReferer: https://www.google.comAuthorization: Bearer eyJhbGciOiJIUzI1NiIs...这些都请求头不是必须的这些请求头都在干嘛呢User-Agent: MyClient/1.0告诉服务器你是谁Accept: text/html,application/json 告诉服务器你想要哪种数据包格式Accept-Language: zh-CN,zh;q0.9 告诉服务器你想要哪种语言Accept-Encoding: gzip, deflate 告诉服务器你能接受压缩过的数据Connection: keep-alive 告诉服务器发送完毕后还要不要保持连接一般用close省宽带Cache-Control: no-cache是否需要缓存这里是无需缓存直接发送即可Cookie: session_idabc123 相当于告诉服务器你是回头客有cookie的举个例子第一次连接没有cookie服务器会发送一个cookie给你之后连接就知道你是连接过的了If-Modified-Since: Wed, 12 Aug 2026 07:28:22 GMT 上次访问是否有修改没有修改就不用发送Referer: https://www.google.com 告诉服务器你是从哪里来的从谷歌网页来的Authorization: Bearer eyJhbGciOiJIUzI1NiIs... 相当于是用户认证一般一些付费的API都会有只有填上了才能使用使用举例我们来举个例子来发送GET请求实战一下就拿从高德地图提供的天气服务来举例基础 API 文档-开发指南-Web服务 API | 高德地图API从这里可以看到我们必须的所有参数1.域名 restapi.amap.com2.URL /v3/weather/weatherInfo3.parametersAPI中说必须的是key和city2个参数到时候我们就需要填上这2个获取到这3个我们就能够开始构建GET请求了通过GET请求获取信息的流程知道网页的ip--通过tcp与网页建立连接--发送GET请求--等待接收数据包--解包1获取网页ip我们之前已经知道了网页的域名了那么我们如何通过域名得到ip地址呢这就需要我们使用DNS了struct hostent *myhost gethostbyname(restapi.amap.com); char *ipstr *(myhost-h_addr_list); printf(网址官方名:%s\n, myhost-h_name); //存放官方名称 printf(网址别名:%s\n, *(myhost-h_aliases)); //存放别名 printf(网址ip类型:%d\n, myhost-h_addrtype); //存放地址类型IPV4 or IPV6 printf(网址长度:%d\n, myhost-h_length); //存放长度这里向大家介绍一个结构体struct hostent{char *h_name //主机的官方域名char **h_aliases; //主机的所有别名列表int h_addrtype; //主机的地址类型 AF_INET(ipv4) AF_INET6(ipv6)int h_length; //地址的长度 IPV44 IPV616char **h_addr_list; //IP地址的二进制表示网络字节序或者叫大端序}这里我们就需要使用这个结构体来实现DNS将域名解析为ip地址struct hostent *myhost gethostbyname(restapi.amap.com);函数介绍#include netdb.hstruct hostent *gethostbyname(const char *name);返回值成功上面提到的struct hostent 结构体的指针失败NULL 可以使用perror打印失败原因参数name需要解析的域名通过这个我们就能获取到网址ip的二进制网络字节序了我们要使用这个ip还需要将ip转化为字符串之前我们学到过struct sockaddr结构体其中有一个存放ip地址的参数类型为struct in_addr在这里存放的就是需要连接网址ip的二进制网络字节序了那么后续我们就能够直接将数值存放在需要连接的地址里面了当然我们也可以将这个二进制的网络字节序转换为字符串来查看这个域名的ip地址是多少//将二进制IP转换为字符串 struct in_addr addr; addr.s_addr *(unsigned long *)ipstr; printf(网址IP: %s\n, inet_ntoa(addr));2与ip建立连接既然已经获取到了服务器的ip了那么我们就可以使用tcp通信来与其建立连接//创建套接字 int tcpsocket socket(AF_INET, SOCK_STREAM, 0); if(tcpsocket -1) { perror(套接字创建失败\n); return -1; } //创建需要连接的ip和端口 struct sockaddr_in bindaddr; bindaddr.sin_family AF_INET; bindaddr.sin_port htons(80); bindaddr.sin_addr addr; //开始连接服务器 int ret connect(tcpsocket, (struct sockaddr *)bindaddr, sizeof(bindaddr)); if(ret -1) { perror(服务器连接失败); return -1; }3建立连接之后我们就可以开始构建GET请求了//发送GET请求 char request[256] {0}; sprintf(request, GET /v3/weather/weatherInfo?key(填自己的key) HTTP/1.1\r\n Host: restapi.amap.com\r\n \r\n );还记得最小的GET请求有哪些内容吗第一行 第二行GET URL?参数 HTTP/1.1\r\n (注意每行结束记得加上\r\n)Host 域名\r\n\r\n 注意GET请求结束是以\r\n作为标志位不要漏掉了这样我们就构建了一个GET请求可以发送GET请求了ret send(tcpsocket, request, strlen(request), 0);这里发送的长度要用strlen不要用sizeof不然会发送无效数据导致GET请求出现格式错误4接收数据包使用tcp的接收函数recv即可//获得原始数据 char recv_buf[1024] {0}; ret recv(tcpsocket, recv_buf, sizeof(recv_buf), 0); if(ret 0) { //printf打印出来 printf(接收到的数据:%s\n, recv_buf); } else printf(没接收到\n);这样我们就获得了网页发送来的数据包了完整代码如下#include stdio.h #include sys/socket.h #include unistd.h #include sys/stat.h #include string.h #include sys/types.h #include stdlib.h #include fcntl.h #include pthread.h #include netdb.h //linux端序转换头文件 #include sys/socket.h #include netinet/in.h #include arpa/inet.h /* struct hostent *gethostbyname(const char *name) 功能: 返回值: 结构体指针:struct hostent * 参数: name:需要的解析的网址 struct hostent { char *h_name; // 主机官方域名规范名 char **h_aliases; // 主机别名列表指向指针的指针 int h_addrtype; // 地址类型AF_INET 或 AF_INET6 int h_length; // 地址长度IPv44, IPv616 char **h_addr_list; // IP地址列表网络字节序 }; */ int main(int arg, char **argv) { if(arg 2) { printf(没有传递网址\n); return -1; } struct hostent *myhost gethostbyname(argv[1]); char *ipstr *(myhost-h_addr_list); printf(网址官方名:%s\n, myhost-h_name); //存放官方名称 printf(网址别名:%s\n, *(myhost-h_aliases)); //存放别名 printf(网址ip类型:%d\n, myhost-h_addrtype); //存放地址类型IPV4 or IPV6 printf(网址长度:%d\n, myhost-h_length); //存放长度 // 修正将二进制IP转换为字符串 struct in_addr addr; addr.s_addr *(unsigned long *)ipstr; printf(网址IP: %s\n, inet_ntoa(addr)); //连接ip int tcpsocket socket(AF_INET, SOCK_STREAM, 0); if(tcpsocket -1) { perror(套接字创建失败\n); return -1; } struct sockaddr_in bindaddr; bindaddr.sin_family AF_INET; bindaddr.sin_port htons(80); bindaddr.sin_addr addr; int ret connect(tcpsocket, (struct sockaddr *)bindaddr, sizeof(bindaddr)); if(ret -1) { perror(服务器连接失败); return -1; } //发送GET请求 char request[256] {0}; sprintf(request, GET /v3/weather/weatherInfo?key(填自己的)city440100 HTTP/1.1\r\n Host: restapi.amap.com\r\n \r\n ); ret send(tcpsocket, request, strlen(request), 0); //获得原始数据 char recv_buf[1024] {0}; ret recv(tcpsocket, recv_buf, sizeof(recv_buf), 0); if(ret 0) { //printf打印出来 printf(接收到的数据:%s\n, recv_buf); } else printf(没接收到\n); }运行结果可以看到我们获取到了正确的json数据包。那要如何解析这个json数据包呢可以参考后面的博客后面会加更一篇json数据解析的博文POST请求学习完了GET请求我们再来看看POST请求是怎么做的POST请求的格式POST/path/to/resource HTTP/1.1 《--请求行Host: www.example.com 《--请求头是Host到Connection这一部分都是User-Agent: MyClient/1.0 可选Accept: application/json 可选Content-Type: application/jsonContent-Length:52Connection: close 可选《--这个空行一定要有是用来区分请求头和请求体的{username: zhangsan, password: 123456}《--请求体POST请求这里的所有行大部分都是必须的了就算是不必须的也推荐写上能减少很多错误第一行POST /path/to/resource HTTP/1.1POST /path/to/resource HTTP/1.1请求类型 URL HTTP的版本其实与GET请求很相似只不过GET请求把参数给放到了URL的后面而POST没有第二行Host: www.example.comHost www.example.com主机 主机域名这个跟GET的一模一样第三行--倒数第二行User-Agent: MyClient/1.0 自己的信息Accept: application/json 希望对方发来的数据包格式Content-Type: application/json 自己发送给对方的数据包格式Content-Length:52 自己发送的数据包长度Connection: close 数据发送完后是否还需要连接最后一行{username: zhangsan, password: 123456} 发送的数据包这里相当于是GET里面跟着URL?后面的参数这里的格式要跟Content-Type设置的保持一致举例实战这一次我们使用POST请求发送到接口盒子实现获取笑话的功能随机笑话[20万] - 接口盒子 - 免费API-免费接口-站长工具-www.apihz.cn虽然这里没用提供POST的示例但是我们也可以参考GET里面提供的数据来实现1.域名 cn.apihz.cn2.URL /api/zici/xiaohua.php3.参数 id88888888 key用自己的可以注册一个账号获取提醒一下这个API不支持用户使用JSON数据发送参数我们要使用Content-Type: application/x-www-form-urlencoded\r\n表示发送的请求体为表单形式流程与GET请求基本一致只不过GET请求换成了POST请求来讲一下POST请求的构成char *domain cn.apihz.cn; //域名 char *url /api/zici/xiaohua.php;//URL char body[512] {0}; int body_length 0; //创建请求体 sprintf(body, id10019813key64064eba22179058f4bad8d1dbdf9812); //获取请求体长度 body_length strlen(body); //发送GET请求 char request[2048] {0}; sprintf(request, POST %s HTTP/1.1\r\n Host: %s\r\n User-Agent: Test/1.0\r\n Accept: application/json\r\n Content-Type: application/x-www-form-urlencoded\r\n Content-Length: %d\r\n Connection: close\r\n \r\n %s, url, domain, body_length, body );这里可以看到这里的域名、URL、请求体长度以及请求体都使用了参数来填入这样的好处是能够更加灵活的更改POST的请求只需要修改参数就能够换为另一个网址的请求非常的方便完整代码#include stdio.h #include sys/socket.h #include unistd.h #include sys/stat.h #include string.h #include sys/types.h #include stdlib.h #include fcntl.h #include pthread.h #include netdb.h //linux端序转换头文件 #include sys/socket.h #include netinet/in.h #include arpa/inet.h int main(void) { char *domain cn.apihz.cn; //域名 char *url /api/zici/xiaohua.php;//URL char body[1024] {0}; //请求体 int body_length 0; //请求体长度 struct hostent *myhost gethostbyname(domain); char *ipstr *(myhost-h_addr_list); printf(网址官方名:%s\n, myhost-h_name); //存放官方名称 printf(网址别名:%s\n, *(myhost-h_aliases)); //存放别名 printf(网址ip类型:%d\n, myhost-h_addrtype); //存放地址类型IPV4 or IPV6 printf(网址长度:%d\n, myhost-h_length); //存放长度 // 修正将二进制IP转换为字符串 struct in_addr addr; addr.s_addr *(unsigned long *)ipstr; printf(网址IP: %s\n, inet_ntoa(addr)); //连接ip int tcpsocket socket(AF_INET, SOCK_STREAM, 0); if(tcpsocket -1) { perror(套接字创建失败\n); return -1; } struct sockaddr_in bindaddr; bindaddr.sin_family AF_INET; bindaddr.sin_port htons(80); bindaddr.sin_addr addr; int ret connect(tcpsocket, (struct sockaddr *)bindaddr, sizeof(bindaddr)); if(ret -1) { perror(服务器连接失败); return -1; } //创建请求体 sprintf(body, id10019813key64064eba22179058f4bad8d1dbdf9812); //获取请求体长度 body_length strlen(body); //发送GET请求 char request[2048] {0}; sprintf(request, POST %s HTTP/1.1\r\n Host: %s\r\n User-Agent: Test/1.0\r\n Accept: application/json\r\n Content-Type: application/x-www-form-urlencoded\r\n Content-Length: %d\r\n Connection: close\r\n \r\n %s, url, domain, body_length, body ); ret send(tcpsocket, request, strlen(request), 0); //获得原始数据 char recv_buf[1024] {0}; ret recv(tcpsocket, recv_buf, sizeof(recv_buf), 0); if(ret 0) { //printf打印出来 printf(接收到的数据:%s\n, recv_buf); } else printf(没接收到\n); }运行结果

相关新闻

星瀚移动端相关

星瀚移动端相关

一、 人人费用 日常报销界面 http:// /ierp/mobile.html?former_mainpage_daily 这个移动端页面 是在这里配置 二、费用报销单 手机端页面,定位页面,隐藏添加字段 1.依次点击【开发服务云】→【开发平台】→【财务云】→【费用核算】→【人人费用单…

2026/10/9 16:43:29 阅读更多 →
G-Helper为什么打不开?5个高频启动失败原因与一步步修复指南

G-Helper为什么打不开?5个高频启动失败原因与一步步修复指南

G-Helper为什么打不开?5个高频启动失败原因与一步步修复指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbo…

2026/10/5 0:25:49 阅读更多 →
LLM-as-a-Judge评分为什么忽高忽低?位置偏差、长度偏差与校准完整排查

LLM-as-a-Judge评分为什么忽高忽低?位置偏差、长度偏差与校准完整排查

文章摘要 LLM-as-a-Judge能够低成本评估相关性、完整性、事实支持、风格和Pairwise偏好,因此被大量用于离线评测和线上抽样。但很多团队发现,同一个答案重复评测会得到不同分数;交换A、B位置后胜负反转;内容更长的答案更容易高分…

2026/10/7 8:15:20 阅读更多 →

最新新闻

6 天 1000 星就是风口?警惕技能包赛道的刷星与注水

6 天 1000 星就是风口?警惕技能包赛道的刷星与注水

6 天 1000 星就是风口?警惕技能包赛道的刷星与注水 【免费下载链接】replica-skill Eleven free Claude skills that clone any app: reverse-engineer it, rebuild it, test it for bugs, then fix what its users hate. Free, MIT. 项目地址: https://gitcode.c…

2026/10/10 2:02:49 阅读更多 →
西语项目杀进 GitHub 前五:tendedero 背后,非英语开源作者正在崛起

西语项目杀进 GitHub 前五:tendedero 背后,非英语开源作者正在崛起

西语项目杀进 GitHub 前五:tendedero 背后,非英语开源作者正在崛起 【免费下载链接】tendedero Screenshots, hung out to dry. A tiny native macOS app that hangs every screenshot on a line at the top of your screen. 项目地址: https://gitcod…

2026/10/10 2:02:49 阅读更多 →
MinIO 进入维护模式:RustFS 的机会窗口已经打开

MinIO 进入维护模式:RustFS 的机会窗口已经打开

MinIO 进入维护模式:RustFS 的机会窗口已经打开 【免费下载链接】rustfs RustFS is an open-source, S3-compatible high-performance object storage system supporting migration and coexistence with other S3-compatible platforms such as MinIO and Ceph. …

2026/10/10 2:02:49 阅读更多 →
向日葵vs ToDesk 2026实测:谁在偷偷压你的画质?

向日葵vs ToDesk 2026实测:谁在偷偷压你的画质?

向日葵vs ToDesk 2026实测:谁在偷偷压你的画质?一、文件传输和画质:远程办公最容易踩的坑出差在外,需要从公司电脑传一个2GB的PSD设计稿到本地;周末在家,需要远程剪辑电脑上的视频素材;旅行途中…

2026/10/10 2:02:49 阅读更多 →
如何为P2WLAN配置HTTPS反向代理与TLS:安全公网部署完整指南

如何为P2WLAN配置HTTPS反向代理与TLS:安全公网部署完整指南

如何为P2WLAN配置HTTPS反向代理与TLS:安全公网部署完整指南 【免费下载链接】p2wlan 跨平台 P2P 虚拟局域网(VirtualLAN)工具,让异地设备像在同一局域网中互联。支持 NAT 穿透、IPv6 直连、房间组网与 Relay 回退,覆盖 Windows / macOS / Lin…

2026/10/10 2:02:49 阅读更多 →
Context Hub 中的 Azure Data Tables Python 客户端实战:Azure Table Storage 与 Cosmos DB for Table 开发指南

Context Hub 中的 Azure Data Tables Python 客户端实战:Azure Table Storage 与 Cosmos DB for Table 开发指南

【免费下载链接】context-hub 项目地址: https://gitcode.com/gh_mirrors/co/context-hub 点击查看 免费下载 本指南以 Context Hub 仓库中 content/azure/docs/data-tables/python/DOC.md 为权威主体,系统讲解 Python 生态中 azure-data-tables&#x…

2026/10/10 2:01:49 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练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/9 6:17:20 阅读更多 →