666489/dsh-nature-papers
A DSH Web plugin that scrapes nature.com in real time for the latest papers on biochemistry / bioinformatics, ranks them with journal impact factor (IF) as the primary quality criterion, and shows 3 papers per day in a floating panel at the bottom-right corner of the page — with direct links to the originals, journal…
これは DeepSeek Harness(DSH)プラグインです。当サイトは GitHub README、インストール情報、メンテナンス状況、公開セキュリティシグナルをまとめています。
dsh-nature-papers
DSH Web 插件:实时爬取 nature.com 上有关生物化学 / 生物信息学的最新论文,以期刊影响因子(IF)为第一优先排序,每天在页面右下角展示 3 篇优质论文并提供原文直达链接、期刊 IF、内容简介;所有历史推荐按日期(年月日)存档,可随时回查;面板底部提供一键「退出 Harness」。
本文档为完整技术实现说明(开源文档),包含架构、每个功能的实现细节、Nature 反爬自动退化机制、数据格式与接口规范。
目录
- 功能一览
- 目录结构
- 架构总览:DSH 插件模型
- 功能实现细节
- 4.1 实时爬取(nature.com 搜索页)
- 4.2 Nature 反爬自动退化(Client Challenge → PubMed)
- 4.3 影响因子优先排序
- 4.4 内容简介(摘要抓取链)
- 4.5 每日 3 篇与缓存
- 4.6 历史记录与去重
- 4.7 实时刷新
- 4.8 右下角面板(客户端)
- 4.9 退出 Harness
- HTTP 接口规范
- 配置项
- 存储格式
- 限速、容错与反爬规避
- 已知限制与局限
- 运维:安装 / 更新 / 启停 / 卸载
功能一览
- 🧬 每日 3 篇:每天自动更新(跨天自动重新爬取),同一天内缓存固定,不反复变化。
- 🏆 影响因子优先:内置 ~90 种期刊的近似 JCR 影响因子表,候选按「IF 降序 → 发表时间新序」排序,卡片标注
IF x.x。 - 🔗 链接直达:标题直达 nature.com 原文(新标签页),附 DOI。
- 📄 内容简介:优先抓取文章页 Abstract,失败退回搜索页摘要;支持展开/收起。
- 📜 历史记录:按
YYYY-MM-DD分组存档,可展开回查任意一天。 - 🔄 实时刷新:按钮触发立即重爬并换选(优先未推荐过的论文)。
- ⏻ 退出 Harness:二次确认后优雅关闭 DSH 服务器进程。
- 🛡 反爬自动退化:nature.com 下发 Client Challenge 时,自动切换 PubMed E-utilities 镜像(仍限定 Nature Portfolio 期刊,链接仍指向 nature.com)。
目录结构
nature-papers/
├── package.json # 包声明:dsh.client 元数据、exports 映射
├── README.md # 英文文档
├── README_zh.md # 中文文档(本文档)
├── LICENSE # MIT 许可证
├── .gitignore
├── install.ps1 # 安装/更新/卸载脚本
├── start-dsh.ps1 # 启动 dsh web 脚本
├── stop-dsh.ps1 # 关闭 dsh web 脚本
├── restart-dsh.ps1 # 重启 dsh web 脚本
├── test-scraper.mjs # 爬虫核心独立测试
└── lib/
├── index.js # 宿主端(Cordis 插件):路由 / 存储 / 每日调度 / 退出
├── scraper.js # 爬取核心(纯 Node,可独立测试):双数据源、解析、排序
├── if-data.js # 期刊影响因子参考表 + 名称归一化
└── client.js # 浏览器端 bundle:右下角面板(shell.overlay 槽位)
配套脚本位于仓库根目录(下文命令均在仓库根目录下执行):
| 脚本 | 用途 |
|---|---|
install.ps1 | 同步代码到安装目录 + 写入 cordis.patch.yml 条目(支持 -Uninstall) |
start-dsh.ps1 | 后台启动 dsh web(隐藏窗口,日志落盘) |
stop-dsh.ps1 | 按端口精确关闭 dsh web 进程 |
restart-dsh.ps1 | 杀旧进程 → 确认端口释放 → 启动新进程 → 自检插件接口 |
test-scraper.mjs | 爬虫核心独立测试(不依赖 Cordis) |
架构总览:DSH 插件模型
DSH Web 是一个 Cordis 插件树:web profile 的空根配置 cordis.yml 由多层 patch 组合(@deepseek-ai/dsh-base、@deepseek-ai/dsh-web-app 两个 bundle 层 + 用户层 cordis.patch.yml)。挂载插件 = 在组合树中插入一个 loader 条目,并让它被加载激活。
挂载链路(宿主端)
-
放置包:把本目录复制到
$DSH_HOME\profiles\node_modules\dsh-nature-papers(真实目录)。DSH 服务器按 Node 裸模块解析规则从 profile 目录向上查找,因此import('dsh-nature-papers')与依赖@deepseek-ai/schemastery都能解析。 -
注册条目:
cordis.patch.yml写入(install.ps1 自动维护):- insert: - id: nature-papers name: dsh-nature-papers config: query: 'bioinformatics biochemistry' count: 3 requestDelayMs: 1000 storageFile: !!js dshHomePath('storages/nature-papers.json')insertpatch 把条目追加进组合树;!!js表达式在条目激活时求值(eval 作用域由boot()注入dshHomePath)。 -
激活:loader 按名字
import()模块 →unwrapExports取默认导出{ name, inject, Config, apply }→ Cordis 解析inject: ['webServer'](等待服务就绪)→ 用 schemasteryConfig校验 config → 执行apply(ctx, config):注册 5 条 HTTP 路由、装载/落盘历史存储、启动每日轮换定时器。条目被 stop/update 时,ctx.effect的 disposer 会注销路由并清理定时器。
挂载链路(客户端)
lib/client.js 是浏览器端 bundle,通过包声明 dsh.client(platform: web)与 exports["./client"] 被识别:
dsh-client-modules(宿主端)扫描 loader 树中声明dsh.client的包 → 把 bundle 内容哈希进window.__DSH_BOOT__清单 → 挂到/plugins/dsh-nature-papers/client.js。- 页面加载时 shell 读取
__DSH_BOOT__→ 加载 bundle 脚本 → bundle 调用window.__ModuleLoader__.load({ id, factory })注册工厂(惰性 CJS:注册 ≠ 执行)。 - Cordis 客户端 loader 物化工厂(
factory(require),require('react')可用)→ 得到插件对象{ name, inject: ['slots'], apply }。 apply()通过ctx.slots.inject('shell.overlay', ...)把面板组件注册进shell.overlay槽位——该槽位由ui-layout的 AppFrame 覆盖层(position:absolute; inset:0)渲染,面板用position:absolute; right:16px; bottom:16px定位,即页面右下角。
数据流
浏览器面板 ──fetch──▶ /plugins/dsh-nature-papers/* ──▶ 宿主端路由
│
┌─────────────┴──────────────┐
数据源 A: nature.com 搜索页 数据源 B: PubMed E-utilities
└─────────────┬──────────────┘
▼
IF 排序 → 去重 → 摘要 enrich
▼
$DSH_HOME/storages/nature-papers.json
功能实现细节
4.1 实时爬取(nature.com 搜索页)
请求构造(scrapeNatureSearch):
- URL:
https://www.nature.com/search?q=<query>&order=date_desc,query为空格分隔的关键词(AND 语义),默认bioinformatics biochemistry。 - 分页:最多翻 3 页(
&page=2/3);当一页行数 < 10 或候选数达标(maxCandidates,默认 90)时提前停止;页间延迟requestDelayMs。 - 请求头:浏览器 UA +
accept: text/html,...;超时requestTimeoutMs;失败重试 1 次(间隔 1.2s)。
行解析:按 <li class="app-article-list-row__item"> 切分每条结果,正则提取字段:
| 字段 | 定位标记 | 说明 |
|---|---|---|
| 标题 | <h3 class="c-card__title">…<a href="/articles/<id>"> | 去标签、解实体 |
| 链接 | href="(/articles/[a-zA-Z0-9-]+)" | 拼为 https://www.nature.com/articles/<id> |
| 类型 | data-test="article.type">…<span class="c-meta__type"> | 命中黑名单(news/comment/editorial/news & views/…)则丢弃 |
| 期刊 | data-test="journal-title-and-link"> | 纯文本 |
| 日期 | <time … datetime="YYYY-MM-DD"> | ISO 日期,用于次级排序 |
| 摘要 | data-test="article-description">…<p>…</p> | 搜索页自带 1~2 句简介(简介兜底) |
| 开放获取 | 行内含 u-color-open-access | 布尔标记 |
| DOI | 由文章 id 推导:/^s\d+-\d+/ → 10.1038/<id> | 非该格式(如 BMC 期刊)为 null |
4.2 Nature 反爬自动退化(Client Challenge → PubMed)
背景:nature.com 对无 JS 的爬虫会下发一个 JS 挑战页("Client Challenge",约 3KB 的 HTML,内含一段加载脚本的 loadScript 代码),纯 HTTP 客户端无法通过(需要真实浏览器执行 JS 换取 cookie)。
检测(两处):
- 搜索页:
html.includes('Client Challenge') || !html.includes('app-article-list-row')→ 抛nature.com 触发了反爬校验(Client Challenge),已切换备用源。 - 文章页:命中
Client Challenge时该篇摘要视为不可得,返回null,走简介兜底。
退化流程(generateEntry):
-
先试数据源 A(nature.com 实时爬取);
-
抛错后自动转数据源 B(PubMed E-utilities),不中断用户请求;
-
数据源 B 的查询构造:
- esearch:
term = ("Nature"[ta] OR "Nature Communications"[ta] OR …) AND (bioinformatics[tiab] OR biochemistry[tiab] OR "computational biology"[tiab] OR …),sort=date&retmax=90—— 用[ta]期刊字段把检索范围钉死在 ~55 个 Nature Portfolio 期刊上,用[tiab]主题词保证话题相关性; - esummary:一次取全部命中(最多 90 条)的标题、期刊全名、发布日期、DOI;
- 新闻过滤:Nature 新闻稿的 DOI 形如
10.1038/d41586-…,doi.startsWith('10.1038/d4')直接丢弃,只保留研究类论文; - 链接还原:DOI 前缀
10.1038/的映射回https://www.nature.com/articles/<suffix>(链接仍直达 nature.com),其余前缀走https://doi.org/<doi>; - 摘要:对排序后 TOP
count*2篇的 PMID 一次性efetch(retmode=xml&rettype=abstract),正则切<PubmedArticle>块提取<ArticleTitle>/<AbstractText>(多段拼接)/<Journal><Title>/<PubDate>/<ELocationID EIdType="doi">。
- esearch:
-
面板会标注本次来源(「来源:Nature 实时」/「来源:PubMed 镜像」),并把源切换原因写入响应(
sourceError)展示在面板顶部提示条。
规避反爬的主动手段:浏览器 UA、请求间隔(默认 1.5s)、单次请求超时、失败重试 + 退避、翻页上限、仅在简介不足时抓文章页(减少请求量)、每日数据缓存(同一天不重复抓取)。
4.3 影响因子优先排序
数据(if-data.js):~90 种期刊的近似 JCR 影响因子(主要 2023 版),覆盖 Nature 旗舰刊、Nature 研究子刊、Nature Reviews 系、npj 系,以及 nature.com 托管的 Springer Nature/BMC 高 IF 期刊(Signal Transduction and Targeted Therapy 40.8、Cell Research 28.1、Molecular Cancer 27.7、Genome Biology 10 等)。
名称归一化:lowercase → 去非字母数字(含 & 与空格)→ 去开头 "the"。因此 nature.com 的 Communications Biology 与 PubMed 的 Nature reviews. Molecular cell biology 都能命中同一表项。
排序规则(rankRows):
sort((a, b) =>
(b.journalIf - a.journalIf) || // ① IF 降序("影响因子为优先")
b.pubDate.localeCompare(a.pubDate) || // ② 同 IF 按发表时间新序
a.title.localeCompare(b.title)) // ③ 稳定兜底
未收录期刊 IF 记为 0(排在所有已知期刊之后);卡片上 IF 未知时显示 IF —。数值仅作排序与展示,非官方授权数据。
4.4 内容简介(摘要抓取链)
- 数据源 A:先取搜索页自带 snippet;当 snippet 缺失或长度 < 60 字符时,才去抓文章页 Abstract——定位
id="Abs1-content"开标签之后的内容,截到</section>前,stripTags去标签、折叠空白;长度 < 40 视为无效。抓取按requestDelayMs间隔串行(礼貌限速)。 - 数据源 B:
efetch一次取回 TOP 候选的全文摘要(PubMed 摘要覆盖率 ~100%)。 - 最终兜底:
abstract || snippet || '(暂无简介,请点击标题查看原文)'。 - 客户端对超过 140 字的摘要做三行截断 + 「展开简介 / 收起简介」切换。
4.5 每日 3 篇与缓存
- 存储:单 JSON 文件(
config.storageFile,默认$DSH_HOME/storages/nature-papers.json),结构见存储格式。写入为原子操作(先写.tmp再 rename);解析失败时旧文件改名.bak后重建。 - 生成时机:
GET /today首次请求当天数据时(惰性生成,含真实爬取);- 每日轮换定时器(每 30 分钟检查一次本地日期是否变化,跨天且当天无数据时后台预生成);
- 用户点「刷新」强制生成。
- 幂等:同一天内
/today恒定返回当天条目(缓存优先),不会因页面刷新而变;跨天自动换新。 - 并发合并:
state.generating持有进行中的生成 Promise,并发请求共享同一次生成,不会重复爬取。
4.6 历史记录与去重
- 每次成功生成后,条目插入历史头部(按日期倒序),同日条目替换,上限
historyCap(400 天)。 - 去重(永不重复推荐):候选池过滤掉历史中所有已推荐 URL;若过滤后不足
count篇 → 放宽为仅排除近 30 天;仍不足 → 允许重复(取当前最优)。 - 客户端「历史」视图按日期分组,点击日期展开当天卡片(TOP 编号、期刊、IF、标题链接、简介)。
4.7 实时刷新
POST /refresh→generate(force=true):立即重新爬取,且把当天已有条目也计入排除(换选一批新论文)。- 失败返回
502 { ok:false, error },客户端展示错误 + 重试按钮。
4.8 右下角面板(客户端)
- 挂载:
shell.overlay槽位(list 型、root 作用域),注册 idnature-papers,order: 100。 - 状态机:
collapsed(收起为胶囊,localStorage 记忆)/view(今日 ↔ 历史)/loading / error / entry / history/expanded(摘要展开)/exitState(退出确认)。 - 每日自愈:窗口
focus事件 + 每 10 分钟定时器检查「本地日期 ≠ 面板日期」→ 自动重新拉取/today。 - 样式:全部走应用主题 CSS 变量(
--dsw-alias-*)+ 兜底值,适配亮/暗色;样式以<style data-plugin="dsh-nature-papers">注入,插件卸载时由 HMR 机制清理。
4.9 退出 Harness
- 宿主端:
POST /shutdown→ 先返回200 {ok:true}(让面板来得及显示最终状态),200ms 后调用 DSH 提供的appExit服务(ctx.get('appExit'),由 launcher 在启动时注入,走优雅关闭流程:dispose 插件树、关闭 HTTP 服务器、退出进程);appExit不可用时回退process.exit(0)。 - 客户端:底部「⏻ 退出 Harness」→ 第一次点击进入「⚠ 确认退出?」(4 秒未确认自动还原)→ 第二次点击
POST /shutdown并显示「正在退出…」。
HTTP 接口规范
统一前缀 /plugins/dsh-nature-papers;响应 { ok: boolean, data?: any, error?: string }。
| 方法 | 路径 | 说明 | 错误码 |
|---|---|---|---|
| GET | /today | 今日推荐(当天首次请求触发实时爬取) | 502 上游失败;405 方法不符 |
| GET | /history | 全部历史(日期倒序) | 405 |
| POST | /refresh | 强制重爬并换选今日推荐 | 502;405 |
| GET | /info | 运行状态:日期、历史天数、数据源、最近生成时间、最近错误 | 405 |
| POST | /shutdown | 优雅关闭服务器进程 | 405 |
/today 响应示例:
{
"ok": true,
"data": {
"date": "2026-08-14",
"papers": [
{
"rank": 1,
"title": "Acquired resistance to the RAS(ON) multi-selective inhibitor …",
"url": "https://www.nature.com/articles/s41591-026-04537-w",
"journal": "Nature Medicine",
"journalIf": 58.7,
"journalIfKnown": true,
"pubDate": "2026-08-11",
"doi": "10.1038/s41591-026-04537-w",
"summary": "Circulating tumor DNA analyses in 44 patients …",
"openAccess": true,
"source": "nature"
}
],
"sourceError": null
}
}
配置项
| 字段 | 默认 | 说明 |
|---|---|---|
query | bioinformatics biochemistry | Nature 搜索关键词(空格 = AND) |
count | 3 | 每天推荐篇数(1~10) |
storageFile | $DSH_HOME/storages/nature-papers.json | 历史存储路径 |
requestDelayMs | 1500 | 抓取间隔(限速) |
requestTimeoutMs | 25000 | 单次请求超时 |
maxCandidates | 90 | 候选池大小(最新 N 篇中按 IF 择优) |
historyCap | 400 | 历史保留天数 |
存储格式
{
"history": [
{
"date": "2026-08-14",
"papers": [
{
"rank": 1,
"title": "…",
"url": "https://www.nature.com/articles/…",
"journal": "Nature Medicine",
"journalIf": 58.7,
"journalIfKnown": true,
"pubDate": "2026-08-11",
"doi": "10.1038/…",
"summary": "…",
"openAccess": true,
"source": "nature"
}
]
}
]
}
限速、容错与反爬规避
| 机制 | 实现 |
|---|---|
| 请求间隔 | 翻页、文章页抓取间延迟 requestDelayMs |
| 超时 | 每请求 AbortSignal.timeout(requestTimeoutMs) |
| 重试 | 网络失败重试 1 次,退避 1.2s |
| 反爬检测 | Client Challenge 特征串 + 结果行缺失双重判定 |
| 双源容灾 | nature.com 失败 → PubMed;双源都失败 → 502 + 明确错误文案 |
| 请求量控制 | 同一天缓存;仅当 snippet 不足才抓文章页;PubMed 摘要一次 efetch 批量获取 |
| 存储安全 | 原子写(tmp+rename);损坏自动 .bak 重建 |
| 并发 | 生成 Promise 合并(in-flight 共享) |
| 生命周期 | 所有路由/定时器注册在 ctx.effect 内,插件卸载自动清理 |
已知限制与局限
- 影响因子为近似 JCR 参考值(主要 2023 版),仅用于排序与展示,非官方授权数据;数值会随时间过时。
- nature.com 可能对高频访问下发 Client Challenge,此时自动使用 PubMed 镜像(见 4.2),面板会标注来源。
- 接口仅在本地回环暴露、无鉴权(与 DSH 其他
/plugins路由一致)。 - 代码热更新受限:
cordis.patch.yml由watchUserPatches监视,但该热加载在实测中不稳定(BOM 编码、文件删除、chokidar 精确文件监视失效等都会导致其停止响应),因此插件代码(lib/*.js)修改后必须重启dsh web(见运维一节);条目级配置改动同样建议重启后生效。 - 面板为固定位置浮层(不可拖拽);与其它注册到
shell.overlay的浮层按注册顺序叠加。
运维:安装 / 更新 / 启停 / 卸载
以下命令均在仓库根目录下执行(
powershell -ExecutionPolicy Bypass -File .\xxx.ps1)。 脚本自动解析$DSH_HOME(环境变量,缺省~/.dsh);dsh启动器自动探测(PATH 或 npx 缓存,可用-DshBin指定);端口可用-Port指定(默认 3080)。不含任何本机硬编码路径。
# 安装 / 更新(同步代码 + 写入 patch 条目;服务器运行中会自动改为原地覆盖)
powershell -ExecutionPolicy Bypass -File .\install.ps1
# 卸载
powershell -ExecutionPolicy Bypass -File .\install.ps1 -Uninstall
# 启停(代码变更后需重启生效)
powershell -ExecutionPolicy Bypass -File .\stop-dsh.ps1
powershell -ExecutionPolicy Bypass -File .\start-dsh.ps1
powershell -ExecutionPolicy Bypass -File .\restart-dsh.ps1 # 杀旧→确认端口→起新→自检
# 面板「⏻ 退出 Harness」等价于 stop-dsh.ps1(走优雅退出)
诊断:$DSH_HOME\restart-result.txt(重启自检)、$DSH_HOME\web-server.log / web-server.err.log(服务器日志)、GET /plugins/dsh-nature-papers/info(运行状态)。
二次开发建议:爬虫核心 scraper.js 不依赖 Cordis,可用 node test-scraper.mjs 独立验证;改完 lib/* 后运行 install.ps1 并重启即可。
セキュリティとインストールエビデンス
このスコアは公開リポジトリメタデータと当サイトに登録されたインストールエビデンスのみに基づくもので、コードセキュリティ監査とは異なります。
公開プラグインカタログから取得し、公開 GitHub リポジトリにリンクしています。
リポジトリは MIT ライセンスを宣言しています。
過去180日以内にコードの更新があります。
再現可能な正確なインストールメタデータはまだ登録されていません。リポジトリの説明に従って手動で確認してください。
確認したパッケージメタデータにインストールライフサイクルスクリプトは宣言されていません。