Laravel+QueryList电影站源码:自适应内容聚合实战
简介这份资源是一套基于 Laravel 与 QueryList 框架搭建的影视站点源码面向希望快速上线影视内容站点的开发者与站长尤其适合具备一定 PHP 基础、想省去从零开发成本的中级用户。源码主打自动采集影视资源前端采用电脑与手机自适应布局并支持自行添加尝鲜设置、对接微信自定义回复图片等扩展玩法可用于搭建个人影视导航或内容聚合平台。压缩包为 rar 格式整体约 20.74MB文件包内附有安装说明便于按文档完成部署与调试。目前已有 550 人学习下载说明该方案在同类影视建站需求中具备一定参考价值。读者可从中获取完整的站点目录结构、采集逻辑实现思路、前后端自适应模板以及微信接口对接示例适合对照源码理解 Laravel 项目组织方式与 QueryList 采集流程并在此基础上二次开发或排错调试。1. 电影站源码-laravelquerylist框架-自适应一套能跑起来的内容聚合方案很多人第一次看到「电影站源码-laravelquerylist框架-自适应」这个组合第一反应是「这不就是个采集站模板吗」。但真正动手搭过的人知道Laravel 负责的是整站骨架——路由、ORM、模板、队列、缓存、后台权限QueryList 负责的是把外部页面里的结构化数据抠出来转成模型能直接入库的数组。两者拼在一起才是一套能长期维护的内容聚合系统而不是一次性爬完就烂尾的脚本。这套方案适合三类人想学 Laravel 实战但缺一个完整业务场景的后端开发者需要把多个来源的影视元数据标题、封面、简介、播放地址、分类统一到自己库里的站长以及想研究「采集 去重 自适应前端」这条链路的工程师。它解决的核心问题不是「怎么爬」而是「爬完之后怎么让数据像正常业务数据一样被管理、被检索、被展示」。下面按选型、落地、避坑、进阶的顺序拆开讲。2. 为什么是 Laravel 加 QueryList选型逻辑与最小可跑环境2.1 两个框架各自负责什么边界在哪Laravel 在这套系统里承担的是「业务层」用户表、影片表、分类表、播放源表、采集任务表都由 Eloquent 管理前台列表页、详情页、搜索页走 Blade 或 API 输出后台用中间件做权限隔离采集任务用队列异步执行避免请求超时。QueryList 承担的是「抽取层」给它一个 URL 和一组选择器规则它返回 DOM 节点集合再通过map或each把节点转成数组。两者的衔接点通常是一个 Service 类比如MovieSpiderService内部调用 QueryList外部返回标准数组给 Laravel 的 Model 去updateOrCreate。边界要划清楚QueryList 不负责去重、不负责限速、不负责重试这些都得在 Laravel 侧用队列、缓存锁和异常处理来做。很多新手把重试逻辑写进 QueryList 的onError回调里结果队列一堆积就雪崩。常见做法是 QueryList 只做「抓取 解析」失败就抛异常由 Laravel 的 Job 决定重试几次、间隔多久。2.2 环境准备与依赖安装的最小命令集先确认 PHP 版本。Laravel 10 要求 PHP 8.1 以上QueryList 4.x 对 PHP 7.4 到 8.2 都兼容。数据库用 MySQL 5.7 或 8.0 都行但注意utf8mb4字符集影视标题里经常有生僻字和 emoji。# 创建 Laravel 项目假设已装 Composer composer create-project laravel/laravel movie-aggregator cd movie-aggregator # 安装 QueryList 核心包 composer require jaeger/querylist # 安装 Redis 扩展用于队列和缓存按系统实际包名调整 # Ubuntu/Debian 示例 sudo apt install php8.1-redis # 安装后重启 PHP-FPM sudo systemctl restart php8.1-fpm # 配置 .env 中的数据库和队列 # DB_CONNECTIONmysql # DB_DATABASEmovie_agg # QUEUE_CONNECTIONredis # CACHE_DRIVERredis安装完成后用php artisan queue:work启动队列监听。这里有个血泪经验队列驱动别用database采集任务量大时jobs表会锁得你怀疑人生Redis 驱动稳定得多。参数上queue:work加--tries3 --timeout120超时设 120 秒是因为部分来源页面加载慢QueryList 默认的 Guzzle 超时是 30 秒需要在实例化时单独调。2.3 数据库表结构的最小设计至少需要四张表movies影片主表、movie_sources播放源一个影片多个源、categories分类、spider_tasks采集任务记录。movies表的关键字段包括title、slug、cover_url、description、category_id、source_hash。source_hash是去重核心用「标题 年份 来源站标识」做 MD5入库前先查这个哈希是否存在。// database/migrations/xxxx_create_movies_table.php 片段 Schema::create(movies, function (Blueprint $table) { $table-id(); $table-string(title)-index(); $table-string(slug)-unique(); $table-string(cover_url)-nullable(); $table-text(description)-nullable(); $table-unsignedBigInteger(category_id)-nullable(); $table-string(source_hash, 32)-unique(); // 去重关键 $table-timestamps(); });source_hash加唯一索引配合updateOrCreate就能实现「存在则更新不存在则插入」。注意别用title做唯一索引不同来源的同一部影片标题可能有细微差异比如多一个「HD」后缀用哈希更稳。3. 用 QueryList 写采集规则从选择器到入库的完整链路3.1 一个可复用的采集 Service 类不要在每个 Job 里重复写 QueryList 的初始化代码。抽一个基类把 Guzzle 的超时、User-Agent、重试次数统一配置。下面是一个最小可用的采集服务采集列表页里的影片卡片。?php namespace App\Services; use QL\QueryList; use GuzzleHttp\Client; class MovieSpiderService { protected QueryList $ql; public function __construct() { // 自定义 Guzzle 客户端控制超时和请求头 $client new Client([ timeout 30, headers [ User-Agent Mozilla/5.0 (compatible; MovieAggregator/1.0), ], ]); $this-ql new QueryList(); $this-ql-setClient($client); } /** * 采集列表页返回影片数组 * param string $url 列表页地址 * param array $rules 选择器规则 * return array */ public function fetchList(string $url, array $rules): array { // 注意QueryList 的 rules 里 key 是字段名value 是选择器数组 $data $this-ql-get($url)-rules($rules)-query()-getData(); return $data-all(); } }逻辑说明setClient替换默认的 Guzzle 实例这样超时和 UA 可控。rules的格式是[title [.card-title, text], cover [.card-img img, src]]第二个元素是属性名text表示取文本src表示取图片地址。参数上timeout设 30 秒是折中值太短容易漏采太长会拖垮队列UA 建议带上项目标识方便对方站长识别减少被封概率。3.2 列表页与详情页的两级采集列表页通常只有标题和封面简介和播放地址在详情页。做法是先用fetchList拿到详情页链接再对每个链接发起第二次请求。这里必须用队列不能在同一个请求里循环否则 100 个影片就能让 PHP 超时。// app/Jobs/SpiderDetailJob.php namespace App\Jobs; use App\Services\MovieSpiderService; use App\Models\Movie; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class SpiderDetailJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $tries 3; public $timeout 120; protected string $detailUrl; public function __construct(string $detailUrl) { $this-detailUrl $detailUrl; } public function handle(MovieSpiderService $spider) { $rules [ title [.detail-title, text], description [.detail-desc, text], cover [.detail-cover img, src], play_url [.play-list a, href], ]; $data $spider-fetchList($this-detailUrl, $rules); if (empty($data)) { throw new \Exception(采集为空: . $this-detailUrl); } $item $data[0]; $hash md5($item[title] . | . parse_url($this-detailUrl, PHP_URL_HOST)); Movie::updateOrCreate( [source_hash $hash], [ title trim($item[title]), cover_url $item[cover] ?? , description $item[description] ?? , slug str_slug($item[title]) . - . substr($hash, 0, 6), ] ); } }逻辑说明updateOrCreate的第一个参数是查询条件第二个是更新或插入的值。source_hash用标题加来源域名生成保证同一来源的同一影片不会重复插入。tries3表示失败重试三次timeout120给慢页面留足时间。注意play_url如果有多条getData()返回的是数组需要单独处理这里只取了第一条做示例。3.3 采集频率控制与队列并发参数QueryList 本身不限速限速得靠 Laravel 的队列和缓存。常见做法是在 Job 的handle开头用Cache::lock加一个按域名维度的锁同一域名同时只允许一个采集任务执行间隔用sleep或RateLimiter控制。use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\RateLimiter; public function handle(MovieSpiderService $spider) { $host parse_url($this-detailUrl, PHP_URL_HOST); // 每个域名每分钟最多 20 次请求 $executed RateLimiter::attempt( spider: . $host, 20, function () use ($spider) { // 实际采集逻辑 }, 60 ); if (! $executed) { // 超出限制释放回队列稍后重试 $this-release(30); } }参数说明RateLimiter::attempt的第二个参数是允许次数第三个是回调第四个是时间窗口秒。release(30)表示 30 秒后重新入队。队列 worker 数量建议从 2 个起步php artisan queue:work --queuespider,default把采集任务单独放一个队列避免和普通业务任务抢资源。4. 自适应前端与数据展示让采集来的内容像原生数据4.1 自适应布局的三个断点与模板组织「自适应」在这类站点里通常指前台模板能在手机、平板、桌面三种宽度下正常显示。用 Blade 加 CSS Grid 就能做不需要引入重型前端框架。断点设 640px、1024px、1280px 三档列表页用grid-template-columns: repeat(auto-fill, minmax(160px, 1fr))详情页播放器区域用aspect-ratio保持比例。/* resources/css/app.css 片段 */ .movie-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; } media (min-width: 1024px) { .movie-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } } .player-wrapper { aspect-ratio: 16 / 9; width: 100%; }逻辑说明auto-fill加minmax让列数随容器宽度自动变化手机上一列或两列桌面四到六列。aspect-ratio是现代浏览器都支持的属性比 padding-top 百分比方案更直观。注意采集来的封面图尺寸不一加object-fit: cover避免拉伸变形。4.2 播放地址的存储与前端渲染采集来的播放地址通常是第三方页面的链接不是直链。存储时用单独的movie_sources表一个影片对应多条记录字段包括movie_id、source_name、play_url、sort_order。前端渲染时按sort_order排序用选项卡切换不同来源。// 详情页控制器片段 public function show($slug) { $movie Movie::where(slug, $slug) -with([sources function ($q) { $q-orderBy(sort_order); }]) -firstOrFail(); return view(movie.show, compact(movie)); }模板里遍历$movie-sources每个源渲染一个按钮点击后把play_url塞进 iframe 的src。注意 iframe 要加referrerpolicyno-referrer否则部分来源会校验来源页。参数上sort_order默认 0采集时按页面出现顺序赋值方便人工调整。4.3 搜索与分类的索引优化采集来的数据量大了之后搜索会变慢。title字段加普通索引如果要做模糊搜索MySQL 的LIKE %关键词%用不上索引常见做法是加一个search_text字段把标题、简介、分类拼在一起用全文索引或者交给外部搜索引擎。小规模站点用WHERE title LIKE ?加LIMIT就够了但记得在movies表上对category_id和created_at建联合索引列表页按分类筛选再按时间排序时能走索引。-- 联合索引示例 ALTER TABLE movies ADD INDEX idx_category_created (category_id, created_at DESC);注意DESC索引在 MySQL 8.0 才真正生效5.7 会忽略。如果还在用 5.7把created_at换成id倒序也能达到类似效果。5. 避坑与排查采集站最容易翻车的五个地方5.1 现象采集一段时间后全部返回空数据原因通常是目标站加了反爬识别了你的 UA 或请求频率。解决先换 UA 测试如果恢复说明是 UA 被标记如果换 UA 无效检查是否 IP 被限。降低频率把RateLimiter的每分钟次数从 20 降到 5观察是否恢复。另外 QueryList 默认不携带 Cookie部分站点需要先访问首页拿 Cookie可以在 Guzzle 客户端里开cookies true。5.2 现象入库时唯一索引冲突报错原因source_hash生成规则里包含了来源域名但同一部影片从不同来源采集时哈希不同理论上不会冲突。冲突往往是因为同一来源的同一影片被两个 Job 同时处理。解决在 Job 里用Cache::lock(hash: . $hash, 10)加锁或者把updateOrCreate包在try-catch里捕获QueryException后忽略。更彻底的做法是队列 worker 设为单进程处理同一来源。5.3 现象详情页采集到的播放地址是空数组原因选择器写错了或者页面结构是 JavaScript 渲染的。QueryList 基于 Guzzle 抓的是原始 HTML不执行 JS。解决先用浏览器查看页面源码不是审查元素确认播放地址是否在初始 HTML 里。如果确实是 JS 渲染QueryList 无能为力需要换用支持无头浏览器的方式但那会大幅增加资源消耗建议优先找有服务端渲染的来源站。5.4 现象队列任务积压CPU 跑满原因queue:work启动太多或者单个 Job 里循环请求太多页面。解决控制 worker 数量采集队列单独用 1 到 2 个 worker把批量采集拆成单个 Job一个 Job 只处理一个详情页。另外检查是否有 Job 陷入死循环重试failed_jobs表里如果有大量相同异常先修异常再重启队列。5.5 现象前端封面图加载慢或 403原因采集时直接存了对方站的图片地址对方做了防盗链。解决在 Laravel 侧加一个图片代理路由用 Guzzle 带上正确的 Referer 去请求图片再输出给前端。或者采集时就把图片下载到本地storage目录用本地地址。后者更稳但要注意磁盘空间和清理策略。// 图片代理路由示例 Route::get(/img-proxy, function (\Illuminate\Http\Request $request) { $url $request-query(url); $client new \GuzzleHttp\Client([headers [Referer parse_url($url, PHP_URL_SCHEME) . :// . parse_url($url, PHP_URL_HOST)]]); $resp $client-get($url); return response($resp-getBody(), 200) -header(Content-Type, $resp-getHeaderLine(Content-Type)); });注意这个代理路由要做白名单校验只允许代理采集时记录过的域名否则会变成公开代理被人滥用。6. 进阶技巧用自定义 QueryList 扩展处理复杂页面6.1 当选择器不够用时写一个自定义采集器有些来源站的播放地址藏在>$rules [ cover [.card-img, data-src, , function ($content) { // 补全相对路径 if (strpos($content, http) ! 0) { return https://example-source.com . $content; } return $content; }], ];第四个参数是回调$content是选择器取到的原始值。这个技巧在处理懒加载图片时特别有用很多站点把真实地址放在>$emptyCount collect($data)-filter(function ($item) { return empty($item[title]) || empty($item[play_url]); })-count(); if ($emptyCount / max(count($data), 1) 0.5) { \Log::warning(采集规则可能失效, [url $this-detailUrl, empty_rate $emptyCount / count($data)]); }这个习惯帮我省了很多事后排查的时间。规则失效往往不是突然全空而是先部分字段变空有日志就能提前发现。6.3 一个验证采集质量的小脚本写完规则后别急着入库先用一个 Artisan 命令跑单页验证。命令里调用MovieSpiderService把结果打印成表格人工核对字段是否对得上。// app/Console/Commands/TestSpider.php public function handle(MovieSpiderService $spider) { $rules [ title [.detail-title, text], cover [.detail-cover img, src], ]; $data $spider-fetchList(https://example-source.com/detail/123, $rules); $this-table(array_keys($data[0] ?? []), $data); }跑php artisan spider:test就能看到输出。这个命令我一般会保留在项目里每次对方站改版先跑它确认规则再改 Job 里的正式规则。参数上测试地址建议固定一个已知存在的详情页方便对比历史输出。最后说个我自己的习惯采集站最怕的不是技术难而是「规则悄悄失效了没人知道」。所以我会把采集命中率做成一个简单的监控面板每天扫一眼。如果某个来源的命中率从 95% 掉到 30%不用等用户反馈自己就先发现了。这套 Laravel 加 QueryList 的组合骨架搭好之后真正花时间的永远是规则维护和异常处理而不是框架本身。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

易语言调用Windows打印API实战:从设备上下文到分页打印

易语言调用Windows打印API实战:从设备上下文到分页打印

1. 从零拆解“打印函数”这件事到底在做什么很多人第一次听到“易语言调用API之打印函数”这个说法,脑子里冒出来的画面可能是把一段文字输出到屏幕上,或者把调试信息写到日志文件里。但真正做过Windows桌面开发的人会告诉你,这里的“打印”大…

2026/10/13 16:25:40 阅读更多 →
怕错过的项目,往往最不该上:一套冷静评估框架

怕错过的项目,往往最不该上:一套冷静评估框架

1. “怕错过”本身就是最大的风险信号先讲一个发生在我自己身上的事。前几年某个风口项目特别火,身边好几个圈子里的朋友都在聊,今天谁拿到了测试资格,明天谁晒出了后台截图,后天谁开始讲“再不上车就晚了”。我当时被那个氛围裹挟…

2026/10/13 16:25:40 阅读更多 →
Web AI助手刷新丢任务?会话ID+任务ID双锚点实现幂等恢复

Web AI助手刷新丢任务?会话ID+任务ID双锚点实现幂等恢复

1. 问题到底出在哪:刷新丢任务的本质做过 Web AI 助手的人大概率都遇到过这个场景:用户跟助手聊了十几轮,中间还上传了文件、触发了长任务,结果手一抖按了 F5,页面白屏重载,对话记录没了,正在跑…

2026/10/13 16:25:40 阅读更多 →

最新新闻

金字塔原理:职场沟通结构化表达的四个原则与实战技巧

金字塔原理:职场沟通结构化表达的四个原则与实战技巧

废话不多说,这可能是你在职场沟通中最值得补的一课。我见过太多人,明明业务能力很强,可一开口就败下阵来:汇报讲了十分钟,领导眉头越皱越紧;邮件写了几百字,对方回复“所以你想表达什么”&#…

2026/10/13 17:10:09 阅读更多 →
实时数据采集架构设计:从需求量化到全链路调优

实时数据采集架构设计:从需求量化到全链路调优

直接开讲。实时数据采集架构设计这件事,我前前后后做了三年多,从最早只会写脚本定时拉数据,到后来能独立设计一套覆盖采集、缓冲、计算、落库全链路的实时方案,中间踩过不少坑。很多人一听到“实时数据采集”这几个字,…

2026/10/13 17:10:09 阅读更多 →
专科生AI论文写作平台实测:8个靠谱工具全解析

专科生AI论文写作平台实测:8个靠谱工具全解析

实话实说,专科生写毕业论文,难度不比本科生低,甚至更难。难在哪?时间紧、任务重,还有相当一部分同学是第一次接触完整的学术写作流程。我自己当年在专科阶段写毕业设计报告时,光是“怎么开头”“怎么把一段…

2026/10/13 17:10:09 阅读更多 →
Docker部署Ceph实现RBD镜像创建与挂载全流程

Docker部署Ceph实现RBD镜像创建与挂载全流程

1. 为什么非得用 Docker 部署 Ceph?——从“三台物理机起步”到“单机快速验证”的真实动因很多人看到“Docker 部署 Ceph”第一反应是皱眉:“Ceph 不是分布式存储系统吗?跑在容器里靠谱?”这问题我当年在某高校实验室带学生做存储…

2026/10/13 17:10:09 阅读更多 →
Python机器学习入侵检测系统实战:从NSL-KDD到Flask部署

Python机器学习入侵检测系统实战:从NSL-KDD到Flask部署

简介:这是一份基于Python机器学习的入侵检测系统项目源码,面向希望入门或进阶网络安全与机器学习方向的学习者,可作为毕业设计、课程设计、大作业或工程实训的参考方案。项目围绕网络流量嗅探与异常检测展开,包含数据预处理、特征…

2026/10/13 17:10:09 阅读更多 →
荷兰耕地遥感语义分割:从数据标注到模型训练全流程实战

荷兰耕地遥感语义分割:从数据标注到模型训练全流程实战

简介:这份荷兰耕地语义分割遥感影像数据集面向遥感图像处理、计算机视觉与农业监测方向的研究者和开发者,用于训练和验证耕地识别与像素级分割模型。资源包共2000个文件,以xml标注文件为主要类型,对应每张遥感影像的像素级类别标签…

2026/10/13 17:09:08 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →