mcpField
C
cnki-navigate-pages

cnki-navigate-pages

github.com/cookjohn生产力1.0k332 次安装33 周前更新
代码仓库

关于 cnki-navigate-pages

用于操作 CNKI 结果页分页和排序的浏览器技能。两类操作均通过一次 `evaluate_script` 完成,并检查可见的验证码。

主要功能

  1. 01前往上一页或下一页
  2. 02跳转到指定页码
  3. 03按日期、被引、下载、相关度或综合排序
  4. 04检测可见的验证码

适用场景

  1. 01浏览 CNKI 搜索结果的前后页面
  2. 02直接跳转到已知结果页
  3. 03按发表时间或被引次数重新排列结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cookjohn/cnki-skills --skill cnki-navigate-pages

安装后在 Claude Code 中输入/cnki-navigate-pages确认已加载。

详细介绍

CNKI 结果分页与排序

所有操作均使用单个异步 evaluate_script,无需快照或 wait_for。

参数

$ARGUMENTS 应为以下之一:

  • next / previous / page N — 分页
  • sort by date / sort by citations / sort by downloads / sort by relevance / sort by comprehensive — 排序

分页(单个 evaluate_script)

将 ACTION_HERE 替换为 "next"、"previous" 或 "page 3":

async () => {
  const cap = document.querySelector('#tcaptcha_transform_dy');
  if (cap && cap.getBoundingClientRect().top >= 0) return { error: 'captcha' };

  const action = "ACTION_HERE";
  const pageLinks = document.querySelectorAll('.pages a');
  const prevMark = document.querySelector('.countPageMark')?.innerText;

  if (action === 'next') {
    const next = Array.from(pageLinks).find(a => a.innerText.trim() === '下一页');
    if (!next) return { error: 'no_next_page' };
    next.click();
  } else if (action === 'previous') {
    const prev = Array.from(pageLinks).find(a => a.innerText.trim() === '上一页');
    if (!prev) return { error: 'no_previous_page' };
    prev.click();
  } else {
    const num = action.replace(/\D/g, '');
    const target = Array.from(pageLinks).find(a => a.innerText.trim() === num);
    if (!target) return { error: 'page_not_found', available: Array.from(pageLinks).map(a => a.innerText.trim()) };
    target.click();
  }

  // Wait for page change
  await new Promise((r, j) => {
    let n = 0;
    const c = () => {
      const mark = document.querySelector('.countPageMark')?.innerText;
      if (mark && mark !== prevMark) r();
      else if (++n > 30) j('timeout');
      else setTimeout(c, 500);
    };
    setTimeout(c, 1000);
  });

  const cap2 = document.querySelector('#tcaptcha_transform_dy');
  if (cap2 && cap2.getBoundingClientRect().top >= 0) return { error: 'captcha' };

  return {
    action,
    total: document.querySelector('.pagerTitleCell')?.innerText?.match(/([\d,]+)/)?.[1] || '0',
    page: document.querySelector('.countPageMark')?.innerText || '?',
    url: location.href
  };
}

排序(单个 evaluate_script)

将 SORT_HERE 替换为 "relevance"、"date"、"citations"、"downloads" 或 "comprehensive":

async () => {
  const cap = document.querySelector('#tcaptcha_transform_dy');
  if (cap && cap.getBoundingClientRect().top >= 0) return { error: 'captcha' };

  const sortBy = "SORT_HERE";
  const idMap = {
    'relevance': 'FFD', 'date': 'PT',
    'citations': 'CF', 'downloads': 'DFR', 'comprehensive': 'ZH'
  };

  const liId = idMap[sortBy];
  if (!liId) return { error: 'invalid_sort', valid: Object.keys(idMap) };

  const li = document.querySelector('#orderList li#' + liId);
  if (!li) return { error: 'sort_option_not_found' };

  const prevMark = document.querySelector('.countPageMark')?.innerText;
  li.click();

  // Wait for results to refresh (page resets to 1)
  await new Promise((r, j) => {
    let n = 0;
    const c = () => {
      const mark = document.querySelector('.countPageMark')?.innerText;
      if (mark && mark !== prevMark) r();
      else if (++n > 30) j('timeout');
      else setTimeout(c, 500);
    };
    setTimeout(c, 1000);
  });

  return {
    sortBy,
    total: document.querySelector('.pagerTitleCell')?.innerText?.match(/([\d,]+)/)?.[1] || '0',
    page: document.querySelector('.countPageMark')?.innerText || '?',
    activeLi: document.querySelector('#orderList li.cur')?.innerText?.trim(),
    url: location.href
  };
}

输出

已跳转至第 {page} 页。共 {total} 条结果。 结果现已按 {sortBy} 排序。

工具调用:1 次(仅 evaluate_script)

已验证的选择器

元素选择器备注
页面链接.pages a数字 + 上一页/下一页
当前页.pages a.cur
下一页文本 下一页,类名 pagesnums
页数计数器.countPageMark文本 "1/300"
排序容器#sortList (.order-group)
排序选项#orderList li点击以排序
相关度li#FFDdata-sort="FFD"
发表时间li#PTdata-sort="PT"
被引li#CFdata-sort="CF"
下载li#DFRdata-sort="DFR"
综合li#ZHdata-sort="ZH"
当前排序#orderList li.cur带有类名 cur

验证码检测

检查 #tcaptcha_transform_dy 元素的 getBoundingClientRect().top >= 0。 仅当 top >= 0(可见)时才处于激活状态。预加载的 SDK 位于 top: -1000000px。

评论

支持 Markdown