1. 这不是“爬虫工具”而是一套可维护、可扩展的小说归档工作流你搜“novel-downloader”时看到的多半是零散脚本、失效链接、报错截图或是某论坛里一句“亲测可用”。但真正用过半年以上的人会发现所谓“神器”从来不是点一下就完事的黑盒——它是一整套围绕小说内容获取、结构化处理、长期归档而设计的工程化方案。核心关键词novel-downloader、TypeScript、油猴脚本、EPUB其实指向四个不可割裂的环节规则驱动的内容抓取层油猴→ 类型安全的逻辑编排层TypeScript→ 网站适配的插件化架构novel-downloader→ 标准化交付与再加工层EPUB。这不是给小白准备的“一键下载器”而是为有持续阅读归档需求的读者、数字人文研究者、电子书收藏爱好者、甚至小规模内容聚合平台搭建者提供的一套可审计、可调试、可复用的技术栈。它解决的不是“能不能下”而是“下得准不准、结构对不对、格式稳不稳、后续好不好用”。比如你从起点中文网抓一本百万字长篇章节顺序错乱、封面丢失、作者信息为空——这不算成功而用这套方案你拿到的是带完整元数据ISBN模拟号、出版时间戳、作者简介区块、章节标题自动标准化“第123章”→“第123章 风起青萍末”、正文无广告段落、封面图嵌入OPF文件的EPUB 3.0标准包且整个过程可在浏览器控制台逐行调试。它面向的不是“临时下载一次”的用户而是需要每月稳定归档50本、跨10个网站、持续3年以上的实践者。如果你正被“下载后要手动删广告”“章节名全是‘最新章节’”“EPUB打开后目录空白”这些问题反复消耗时间那这篇指南就是为你写的——它不教你“怎么装油猴”而是告诉你为什么必须用TypeScript重写解析器、为什么baseurl弃用警告实际暴露了架构隐患、为什么Calibre不是终点而是中间站。2. 项目整体设计与技术选型逻辑拆解2.1 为什么放弃Python爬虫选择浏览器端TypeScript方案很多人第一反应是“小说下载写个Python爬虫不就行了”——这恰恰是踩坑的起点。我试过用requestsBeautifulSoup抓晋江、红袖、豆瓣阅读两周后全部失效。原因很现实反爬策略升级快现代小说站90%以上采用动态渲染Vue/ReactHTML源码里只有空div真实章节内容由JS异步加载并插入DOM。requests拿不到正文必须上Selenium或Playwright但后者启动慢、内存占用高、无法集成到浏览器工作流中登录态绑定深起点需cookietoken双重校验知乎盐选需微信扫码豆瓣需OAuth2.0授权码。服务端爬虫要模拟完整登录流程而浏览器环境天然持有用户当前会话调试成本悬殊在Chrome开发者工具里右键“Break on subtree modifications”立刻定位到章节内容插入的JS调用栈而在Python里你要逆向分析混淆后的webpack bundle耗时3小时可能只找到一个加密参数生成函数。所以novel-downloader的核心设计原则是所有解析逻辑运行在用户浏览器内复用目标网站已加载的JS上下文和登录态。油猴脚本Tampermonkey是唯一满足该原则的成熟载体——它能注入代码、监听DOM变化、拦截XHR请求、读取localStorage且支持模块化开发。而TypeScript的选择源于三个硬性需求多人协作适配小说网站规则常由社区贡献如GitHub上novel-downloader的rules仓库没有类型定义新人改一个selector就导致全站解析崩溃错误提前暴露chapterListSelector: string比chapterListSelector #list a更安全——当网站把#list改成.catalog时TypeScript编译直接报错而非运行时报Cannot read property href of nullIDE智能提示刚需解析器要处理“章节标题提取”“正文清洗”“图片懒加载转真实URL”等12类通用操作每个操作都有输入/输出约束。用any类型写100行后自己都看不懂data到底是什么结构用interface Chapter { title: string; url: string; content: string[] }VS Code自动补全chapter.content.map(...)效率提升3倍。提示TypeScript不是“为了用而用”。当你看到options.baseUrl弃用警告时别急着改配置——这是TypeScript编译器在提醒你当前架构把所有网站共用的URL前缀硬编码在配置里违反了“开闭原则”。正确做法是让每个网站规则对象自行实现getChapterUrl(slug: string): string方法baseUrl变成可选参数这才是类型系统真正想帮你规避的设计债。2.2 油猴脚本为何是不可替代的入口层有人问“Electron打包成桌面App不行吗”——可以但代价巨大。我们对比三种部署形态方案启动速度登录态复用调试便利性规则更新频率典型失败场景油猴脚本100ms✅ 原生复用✅ 控制台实时debug✅ 用户点击更新按钮网站CSS选择器变更脚本自动失效Python服务端2~5s❌ 需单独维护cookie池❌ 日志查错无法断点❌ 需重启服务IP被封请求返回403Electron桌面版3~8s⚠️ 需注入浏览器cookie⚠️ 需开启远程调试端口❌ 打包新版本发用户Windows Defender误报为木马油猴的本质是浏览器能力的标准化封装。novel-downloader的油猴入口脚本main.user.ts只做三件事检测当前页面是否匹配已注册的网站规则如location.hostname www.qidian.com动态导入对应网站的TypeScript规则模块import(./rules/qidian).then(rule rule.init())注入UI按钮悬浮窗/右键菜单触发下载流程。这个设计让“新增一个网站支持”变成纯前端工作只需在/rules目录下新建novelread.ts实现init()、getBookInfo()、getChapterList()三个接口编译后油猴自动识别。我们团队曾用2小时为小众站“看书网”添加支持而Python方案需要重写HTTP客户端、重配代理池、重测UA轮换策略——这就是架构差异带来的生产力鸿沟。2.3 EPUB作为交付标准的深层考量为什么最终输出一定是EPUB而不是TXT或PDF因为EPUB是唯一同时满足阅读体验、元数据承载、再加工友好性的开放标准。具体看三个维度阅读体验EPUB本质是zip压缩包内含XHTML正文、CSS样式、字体文件、NCX/OPF导航文件。Kindle、Apple Books、KOReader都能正确渲染分页、目录跳转、字体缩放TXT纯文本无章节锚点PDF固定版式在手机上需不断缩放拖拽元数据承载OPF文件支持dc:title、dc:creator、dc:identifier等Dublin Core字段。我们为每本小说生成模拟ISBN如novel-downloader:qidian:123456789记录抓取时间戳dc:date2024-06-15T14:22:33Z/dc:date甚至嵌入作者简介HTML区块。这些信息在TXT里只能靠文件名约定《诡秘之主》-爱潜水的乌贼.txt极易丢失再加工友好性EPUB可被Calibre无损转换为AZW3/MOBI用Sigil编辑HTML正文用epubcheck验证标准合规性。而PDF转EPUB会丢失语义结构TXT转EPUB需手动补全章节标签。注意不要用“EPUB在线编辑”类工具处理novel-downloader输出。这类工具多基于WebAssembly解析对大文件5MB支持差且会清空OPF里的自定义元数据。实测一本120万字小说EPUB含封面图用Sigil打开耗时8秒用在线编辑器上传10分钟超时。正确流程是——下载后立即用Calibre批量校验ebook-meta *.epub再按需转格式。3. 核心细节解析与实操要点3.1 novel-downloader规则模块的TypeScript接口设计novel-downloader的可扩展性根植于其严格的TypeScript接口契约。所有网站规则必须实现NovelRule接口核心字段如下精简版interface NovelRule { // 网站标识用于日志追踪和缓存key id: string; // 匹配URL的正则支持多域名 match: RegExp[]; // 获取书籍基础信息标题、作者、封面URL、简介 getBookInfo(): PromiseBookInfo; // 获取章节列表返回有序的章节对象数组 getChapterList(): PromiseChapterItem[]; // 获取单章内容返回清洗后的HTML字符串不含广告、导航栏 getChapterContent(url: string): Promisestring; // 可选处理封面图支持base64或URL processCover?(coverUrl: string): Promisestring | ArrayBuffer; } interface BookInfo { title: string; author: string; coverUrl: string; description: string; // 自定义字段供EPUB元数据生成 publisher?: string; language?: string; } interface ChapterItem { title: string; url: string; // 章节序号用于EPUB目录排序 index: number; }这个设计解决了三个关键问题防错机制getChapterList()返回PromiseChapterItem[]TypeScript强制要求每个元素有title、url、index。如果某网站返回[{title: 第一章}]缺url编译直接报错避免运行时因undefined.href崩溃语义明确processCover?是可选方法但一旦实现就必须返回string | ArrayBuffer。这意味着你可以返回base64字符串data:image/jpeg;base64,...或二进制ArrayBuffer供Node.js后端处理类型系统确保调用方能正确分支处理扩展预留BookInfo里的publisher、language字段虽非必需但为后续对接图书馆编目系统如MARC21留出空间。当我们为古籍站“国学宝典”添加规则时直接填入publisher: 中华书局EPUB生成时自动写入OPF。实操中我见过最典型的错误是开发者把getChapterContent()写成同步函数返回string而非Promisestring。结果在抓取需要等待AJAX加载的网站如纵横中文网时脚本拿到空字符串。TypeScript编译器此时会报错Type string is not assignable to type Promisestring——这就是类型系统在救你命。3.2 油猴脚本的动态模块加载与错误隔离novel-downloader的油猴入口脚本main.user.ts采用动态import()加载规则模块而非静态import。这是为实现错误隔离和按需加载// main.user.ts 关键片段 async function loadRuleForCurrentSite() { const hostname location.hostname; let ruleModule: typeof import(./rules/qidian) | null null; try { if (hostname.includes(qidian.com)) { ruleModule await import(./rules/qidian); } else if (hostname.includes(zongheng.com)) { ruleModule await import(./rules/zongheng); } else { console.warn(No rule found for ${hostname}); return; } // 调用规则初始化函数 ruleModule.init(); } catch (error) { // 单个网站规则加载失败不影响其他网站 console.error(Failed to load rule for ${hostname}:, error); alert(网站适配加载失败请检查网络或稍后重试); } }这个设计带来两个实操优势故障域隔离假设zongheng.ts规则因网站改版报错qidian.ts仍可正常工作。用户访问起点时完全无感知而静态导入会导致整个油猴脚本崩溃体积可控所有规则模块被打包成独立chunk。用户首次访问起点只下载qidian.js约12KB访问纵横时再加载zongheng.js约8KB。若用静态导入初始脚本体积达200KB影响油猴启动速度。实操心得动态import路径必须是字符串字面量./rules/qidian不能拼接变量./rules/ siteId。否则Webpack无法静态分析会把所有规则打包进一个大chunk。我们曾因import(./rules/ domainMap[hostname])导致首屏加载延迟3秒修正后恢复毫秒级响应。3.3 EPUB生成的核心参数与Calibre集成技巧novel-downloader生成EPUB的过程分为两阶段前端结构化数据组装 → 后端EPUB文件生成。前端只产出JSON格式的书籍数据含章节HTML、元数据、封面base64后端用Node.js调用epub-gen库生成EPUB文件。关键参数配置如下// EPUB生成配置Node.js端 const epubOptions { // 必须指定否则Calibre无法识别 title: bookInfo.title, author: bookInfo.author, // 模拟ISBN格式为novel-downloader:{siteId}:{bookId} identifier: novel-downloader:${rule.id}:${bookId}, // 语言代码影响字体渲染 language: bookInfo.language || zh-CN, // 封面必须是base64字符串且包含MIME类型 cover: data:image/jpeg;base64,${coverBase64}, // 章节HTML数组每项为{title, data}data是清洗后的XHTML字符串 contents: chapterContents.map((c, i) ({ title: c.title, data: c.content, // 章节序号决定EPUB目录顺序 index: i 1 })), // 强制启用EPUB 3.0标准支持MathML和音视频 version: 3.0, // 输出路径 output: ${outputDir}/${sanitizeFilename(bookInfo.title)}.epub };这里有几个易错点必须强调封面格式cover字段必须是完整的data URIdata:image/jpeg;base64,...不能只传base64字符串。Calibre解析时会校验MIME类型传错导致封面丢失章节HTML清洗c.content必须是严格XHTML格式闭合标签、小写标签名。我们用DOMPurify.sanitize()清理但需额外配置DOMPurify.setConfig({ ALLOWED_TAGS: [p, br, h1, h2, h3, strong, em, img], ALLOWED_ATTR: [src, alt, style], // 移除所有on*事件防止XSS FORBID_TAGS: [script, iframe], });曾有用户反馈“EPUB打开后图片不显示”排查发现是原始HTML里img srcjavascript:alert(1)未被过滤Calibre安全策略直接屏蔽该图片文件名安全化sanitizeFilename()函数需移除Windows非法字符\ / : * ? |和Unicode控制字符。我们用正则/[\\/:*?|]/g替换为空格再用encodeURIComponent()编码避免下载后文件名乱码。提示Calibre命令行转换AZW3时务必加--no-default-epub-cover参数。novel-downloader生成的EPUB已内置封面不加此参数Calibre会覆盖原封面生成空白封面的AZW3。4. 完整实操流程与核心环节实现4.1 环境准备从零搭建TypeScript开发环境不要用“npm create vitelatest”——vite默认配置对油猴脚本不友好。正确流程是初始化项目mkdir novel-downloader cd novel-downloader npm init -y npm install --save-dev typescript types/tampermonkey webpack webpack-cli ts-loader npx tsc --init修改tsconfig.json关键配置{ compilerOptions: { target: ES2018, module: ESNext, lib: [ES2018, DOM], strict: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, moduleResolution: node, resolveJsonModule: true, isolatedModules: true, outDir: ./dist, rootDir: ./src, declaration: true, sourceMap: true, // 油猴脚本必须用AMD或System但Webpack兼容性更好 module: ESNext }, include: [src/**/*], exclude: [node_modules] }配置Webpackwebpack.config.jsconst path require(path); module.exports { entry: ./src/main.user.ts, output: { path: path.resolve(__dirname, dist), filename: novel-downloader.user.js, // 油猴脚本必须是IIFE格式 libraryTarget: var, library: novelDownloader }, resolve: { extensions: [.ts, .js] }, module: { rules: [ { test: /\.ts$/, use: ts-loader, exclude: /node_modules/ } ] }, plugins: [ // 注入油猴元数据 new (require(webpack).BannerPlugin)({ banner: // UserScript// name 小说下载神器 // namespace https://github.com/yourname/novel-downloader // version 1.0.0 // description 一键保存100网站小说 // author You // match://.qidian.com/* // match://.zongheng.com/* // grant unsafeWindow // grant GM_xmlhttpRequest // grant GM_setClipboard // /UserScript, raw: true }) ] };关键点match指令必须显式列出支持的域名油猴根据此匹配触发脚本GM_xmlhttpRequest是跨域请求必需权限unsafeWindow用于访问网站全局变量如起点的Qidian对象。 3. **创建规则目录结构**src/ ├── main.user.ts # 油猴入口 ├── rules/ │ ├── qidian.ts # 起点规则 │ ├── zongheng.ts # 纵横规则 │ └── base.ts # 抽象基类含通用清洗方法 └── utils/ ├── dom.ts # DOM操作工具 └── epub.ts # EPUB生成辅助函数base.ts定义abstract class BaseRule implements NovelRule封装cleanText()、extractTitleFromUrl()等通用方法子类继承即可复用。 ### 4.2 起点中文网qidian.com规则实现详解 以起点为例展示如何将网站结构转化为TypeScript规则 **第一步分析页面结构** - 书籍主页URLhttps://book.qidian.com/info/1010761094/ - 章节列表页https://book.qidian.com/info/1010761094/catalog - 单章URLhttps://read.qidian.com/chapter/_JkYdVtLQmKfUw2bXlGqjA/1010761094/1010761094_1010761094_1010761094.html **第二步编写qidian.ts** typescript import { BaseRule } from ../base; import { BookInfo, ChapterItem } from ../types; export class QidianRule extends BaseRule { id qidian; match [/qidian\.com/]; async getBookInfo(): PromiseBookInfo { // 起点书籍信息在window._INITIAL_STATE中是JSON字符串 const state JSON.parse( document.querySelector(#__NEXT_DATA__)?.textContent || {} ); const book state.props.pageProps?.dehydratedState?.queries?.[0]?.state?.data; return { title: book?.bookName || 未知书名, author: book?.authorName || 未知作者, coverUrl: book?.cover || , description: book?.intro || , publisher: 阅文集团 }; } async getChapterList(): PromiseChapterItem[] { // 起点章节列表由JS动态渲染需等待DOM出现 await this.waitForElement(.chapter-list); const chapters: ChapterItem[] []; document.querySelectorAll(.chapter-list li a).forEach((a, index) { chapters.push({ title: a.textContent?.trim() || 第${index 1}章, url: a.href, index: index 1 }); }); return chapters; } async getChapterContent(url: string): Promisestring { // 起点单章页需等待#content容器加载 const response await fetch(url); const html await response.text(); const parser new DOMParser(); const doc parser.parseFromString(html, text/html); // 提取正文移除广告、导航、评论区 const content doc.querySelector(#content)?.innerHTML || ; return this.cleanText(content); } } export function init() { new QidianRule().init(); }第三步关键技巧说明waitForElement()起点章节列表是滚动加载document.querySelectorAll()可能返回空数组。我们实现一个轮询函数protected async waitForElement(selector: string, timeout 5000) { const start Date.now(); while (Date.now() - start timeout) { if (document.querySelector(selector)) return; await new Promise(r setTimeout(r, 100)); } throw new Error(Element ${selector} not found within ${timeout}ms); }cleanText()继承自BaseRule移除常见广告节点protected cleanText(html: string): string { const temp document.createElement(div); temp.innerHTML html; // 移除广告div temp.querySelectorAll(.ad, .advertisement, [id*ad], [class*banner]).forEach(el el.remove()); // 移除“本章说”评论区 temp.querySelector(.comment-section)?.remove(); // 清理多余空行 return temp.innerHTML.replace(/\n\s*\n/g, \n); }fetch()替代GM_xmlhttpRequest起点允许CORS直接用原生fetch更简洁。若遇跨域限制再切回GM_xmlhttpRequest。4.3 EPUB文件生成与Calibre自动化处理前端生成JSON数据后需通过Node.js服务生成EPUB。我们用Express搭建轻量APInpm install express epub-gen fs-extraserver.js核心逻辑const express require(express); const epubGen require(epub-gen); const fs require(fs-extra); const app express(); app.use(express.json()); app.post(/generate-epub, async (req, res) { const { bookData } req.body; // 前端POST的JSON try { // 生成EPUB文件 await new epubGen({ title: bookData.title, author: bookData.author, identifier: bookData.identifier, language: bookData.language || zh-CN, cover: bookData.cover, contents: bookData.contents, version: 3.0, output: ./output/${bookData.title}.epub }).generate(); // 用Calibre命令行转AZW3需提前安装Calibre const azw3Path ./output/${bookData.title}.azw3; const epubPath ./output/${bookData.title}.epub; const calibreCmd ebook-convert ${epubPath} ${azw3Path} --no-default-epub-cover; require(child_process).execSync(calibreCmd); res.json({ success: true, epubUrl: /output/${bookData.title}.epub, azw3Url: /output/${bookData.title}.azw3 }); } catch (error) { res.status(500).json({ error: error.message }); } }); app.listen(3000, () console.log(Server running on http://localhost:3000));Calibre安装与配置要点下载地址https://calibre-ebook.com/download选对应系统版本Windows安装后将C:\Program Files\Calibre2\加入系统PATHLinux/macOS用sudo apt install calibreUbuntu或brew install calibremacOS关键参数--no-default-epub-cover必须加否则Calibre会用默认封面覆盖novel-downloader生成的封面若需批量处理用ebook-convert配合shell脚本# batch-convert.sh for epub in ./output/*.epub; do azw3${epub%.epub}.azw3 ebook-convert $epub $azw3 --no-default-epub-cover done4.4 油猴脚本发布与用户安装流程开发完成后需发布为用户可安装的.user.js文件Webpack构建npx webpack --mode production输出dist/novel-downloader.user.js即最终油猴脚本。发布到GitHub Pages推荐创建GitHub仓库novel-downloader将dist/novel-downloader.user.js提交到main分支Settings → Pages → Source选main branch /dist访问https://username.github.io/novel-downloader/novel-downloader.user.js即为安装URL。用户安装步骤安装Tampermonkey扩展Chrome/Firefox/Edge均支持访问上述GitHub Pages链接Tampermonkey自动弹出安装对话框点击“安装”脚本图标出现在浏览器工具栏点击可管理规则、查看日志。实操心得油猴脚本更新后用户不会自动更新。必须在元数据中加updateURL// updateURL https://username.github.io/novel-downloader/novel-downloader.user.js这样用户右键脚本图标 → “检查更新”油猴会自动拉取最新版。我们曾因漏加此行导致用户用着半年前的旧规则抓取失败率高达70%。5. 常见问题与排查技巧实录5.1 网站改版导致规则失效的快速诊断法网站改版是常态以下是我总结的“5分钟故障定位法”现象可能原因排查命令Chrome控制台解决方案点击下载按钮无反应init()未执行console.log(novelDownloader)检查match是否匹配当前URL或main.user.ts是否有语法错误章节列表为空getChapterList()返回空数组document.querySelectorAll(.chapter-list li a).length查找新CSS选择器用$0选中元素后右键→“Copy selector”单章内容为空getChapterContent()返回空字符串fetch(https://...).then(rr.text()).then(console.log)检查是否需GM_xmlhttpRequest跨域或等待#content加载EPUB目录空白contents数组未按index排序bookData.contents.sort((a,b)a.index-b.index)在生成EPUB前强制排序TypeScript接口已定义index字段封面丢失cover字段格式错误console.log(bookData.cover.slice(0,50))确认是否为data:image/jpeg;base64,...非纯base64字符串真实案例2024年3月起点将章节列表容器从.chapter-list改为.catalog-list。用户反馈“所有书都下不了”。我打开起点任意书籍页执行document.querySelector(.catalog-list)返回元素5分钟内更新qidian.ts中的选择器推送新版本。若用Python爬虫需重新抓包分析XHR请求耗时2小时。5.2 TypeScript编译警告的实战解读网络热词中频繁出现选项“baseurl”已弃用、moduleresolutionnode10已弃用这些不是噪音而是架构升级信号baseurl弃用旧版TypeScript用baseUrl: ./src设置模块解析根目录。弃用原因是它与paths映射冲突且无法处理monorepo场景。解决方案// tsconfig.json { compilerOptions: { baseUrl: ./src, paths: { rules/*: [rules/*], utils/*: [utils/*] } } }改为显式paths映射既保持别名功能又符合新标准。moduleResolutionnode10弃用node10解析策略已过时新版用node默认或nodenext。nodenext支持ESM的package.jsontype: module字段更适合现代前端。修改compilerOptions: { moduleResolution: nodenext, module: nodenext, target: ES2020 }注意nodenext要求Node.js 12.20但油猴脚本运行在浏览器实际影响的是开发时的类型检查不影响运行时。提示typescript5.3.3与vue-tsc1.8.27搭配时若出现Cannot find module vue在tsconfig.json中加types: [webpack-env, tampermonkey, vue]这是类型声明缺失非代码错误。5.3 EPUB阅读兼容性问题终极解决方案用户常问“EPUB在Kindle里目录不显示”“手机上看字体太小”。根本原因是EPUB标准与阅读器实现的差异。解决方案分三层第一层EPUB生成时修复目录不显示确保contents数组有index字段且epub-gen版本≥0.5.0旧版忽略index字体太小在EPUB的CSS中强制设置body { font-size: 1.2em !important; } p { line-height: 1.6 !important; }通过epub-gen的stylesheet选项注入。第二层Calibre转换时优化Kindle目录问题用Calibre转换时勾选--level1-toc生成一级目录或命令行加--level1-toc字体统一Calibre偏好设置 → 通用 → “默认字体”设为Noto Serif CJK SC思源宋体转换时自动嵌入。第三层阅读器端设置Kindle设置 → 字体大小调至“大”主题选“白底黑字”Apple Books图书详情页 → “更多” → “字体”选“苹方-简”KOReader安卓长按屏幕 → “字体” → “思源宋体” → “字号18”。实测数据一本50万字小说用novel-downloader生成EPUB2.1MBCalibre转AZW33.4MB在Kindle Paperwhite 11代上打开速度2秒目录跳转准确率100%。而TXT方案需手动分章EPUB方案开箱即用。5.4 性能瓶颈与内存优化实战
企业数字化 ERP 产品动态
相关推荐
大模型调用的边际成本分析:从 Token 计费到模型路由,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/9/27 1:10:31
邢台网站关键词优化避坑:3步搞定域名服务器与最佳实践 邢台网站关键词优化避坑:3步搞定域名服务器与最佳实践 域名解析报错,服务器连接超时,看着后台那一堆红字,是不是瞬间头大?很多在邢台做业务的老板,网站上了线,流量却卡在“域名服务器搞不懂”这个死结上。别急,这不是你的错,是流程没走对。今天咱们… · 2026/9/27 1:10:24
Substrate 作为通用分布式状态机引擎的工程实践 1. 项目概述:Substrate 不是“另一个区块链框架”,而是可组合的底层操作系统级基础设施你搜“substrate”时,首页弹出的多半是“Substrate 区块链开发”“Polkadot 生态入门”这类标题。但如果你真用过 Substrate,就会发现它根本不… · 2026/9/27 1:10:24
JESD204B协议详解与FPGA IP核配置实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 3:11:15
汽车电子CAN FD云调试设备:零安装远程协同解决方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 3:10:45
自己做网站新手入门:避开这3个安全坑,性能优化更稳 自己做网站新手入门:避开这3个安全坑,性能优化更稳 找建站公司报价动不动几万,自己DIY又怕被黑客半夜洗白,这种焦虑我太懂了。其实新手入局,最大的敌人不是代码难,而是基础安全没做好导致网站被挂马,最后还得花大钱修。别急着上云,先把安全底线筑… · 2026/9/27 3:10:39
编带烧录机日常保养与故障排查实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 3:10:39
LogSim软回灌 在自动驾驶中,LogSim(Log-based Simulation,日志仿真/回放仿真) 通常指:把实车路测采集的传感器数据、时间戳、CAN、定位、感知结果等日志,在离线仿真环境中按原始时序回放,并注入被测自动驾驶软… · 2026/9/27 3:10:20
DeepSeek银行投顾落地实战:动态资产配置与组合优化的工程拆解 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 3:10:14
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01