# Cnki Exp Search Automation

> CNKI（中国知网）高级搜索自动化技能。使用浏览器自动化技术搜索文献并获取结果列表及摘要信息。建议在有头浏览器环境下使用以便于处理反机器人验证。

- Skill: `dvcrn/cnki-exp-search-automation` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add dvcrn/cnki-exp-search-automation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dvcrn/cnki-exp-search-automation/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: dvcrn (https://skillmd.com/u/dvcrn)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/dvcrn/cnki-exp-search-automation

---


# CNKI 高级搜索自动化技能

## 技能名称
cnki-exp-search-automation

## 技能描述
CNKI（中国知网）高级搜索自动化技能。使用浏览器自动化技术，从 CNKI 专业检索页面搜索文献并获取结果列表及摘要信息。技能会自动处理反机器人验证问题，支持批量导出 CSV 数据。

## 使用场景
- 学术文献调研
- 批量获取 CNKI 文献元数据
- 提取文献摘要信息

## 前置要求

### 环境要求
- OpenClaw 环境（已配置浏览器工具）
- 可访问 CNKI 网站（https://kns.cnki.net）

### 安装依赖
无额外依赖，使用 OpenClaw 内置浏览器工具

---

## 反机器人验证解决方案

### 验证问题识别
CNKI 可能会出现以下验证：
1. **滑块验证** - 需要拖动滑块完成拼图
2. **点选验证** - 需要点击指定图片
3. **验证码输入** - 需要输入数字/字母验证码

### 自动处理策略
```python
# 策略1：等待验证自动出现
# 策略2：使用 JavaScript 绕过检测
# 策略3：降低请求频率避免触发验证
```

---

## 功能1：搜索并获取所有结果

### 功能描述
在 CNKI 高级搜索页面执行专业检索，获取所有搜索结果并提取文献元数据。

### 输入参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| query | string | 是 | 搜索关键词（使用 CNKI 专业检索语法，如 SU='结膜松弛' and SU='治疗'） |
| max_pages | number | 否 | 最大页数（默认10页，每页20条） |

### 输出
返回所有搜索结果的 JSON 数组，每条记录包含：
- title: 文献标题
- authors: 作者列表（分号分隔）
- date: 发表日期
- cites: 引用次数
- source: 来源期刊/数据库

### 使用示例

**搜索命令：**
```
在 CNKI 上搜索 "结膜松弛的治疗" 相关文献，提取所有结果
```

**执行步骤：**

1. **导航到 CNKI 高级搜索页面**
   - URL: https://kns.cnki.net/kns8s/AdvSearch?type=expert

2. **输入搜索关键词**
   - 在搜索框输入：SU='结膜松弛' and SU='治疗'
   - 可选择性地勾选"中英文扩展"

3. **点击搜索按钮**

4. **等待结果加载**
   - 等待表格元素出现：table.resulttable

5. **提取当前页数据**
   ```javascript
   const data = []
   const table = document.querySelector('table')
   const rows = table.querySelectorAll('tr')
   rows.forEach(row => {
     const cells = row.querySelectorAll('td')
     if (cells.length >= 5) {
       const titleEl = cells[1].querySelector('a')
       data.push({
         title: titleEl?.textContent.trim() || '',
         authors: cells[2]?.textContent.trim() || '',
         date: cells[4]?.textContent.trim() || '',
         cites: cells[6]?.textContent.trim() || '0'
       })
     }
   })
   ```

6. **翻页处理**
   - 使用键盘右键 → 翻到下一页（避免点击被检测）
   - 每页提取后等待1-2秒

7. **保存数据**
   - 将数据保存为 JSON 或 CSV 文件

### 数据示例
```json
[
  {
    "title": "射频微创治疗结膜松弛引起溢泪的临床观察",
    "authors": "郑璇;杨晓钊;杨华;张懿;王博",
    "date": "2026-02-25",
    "cites": "0",
    "source": "国际眼科杂志"
  }
]
```

---

## 功能2：获取文章摘要信息

### 功能描述
根据文章 URL 导航到详情页，提取文章的完整元数据信息。

### 输入参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| article_url | string | 是 | CNKI 文章详情页 URL |

### 输出
返回文章详细信息 JSON 对象，包含：
- title: 标题
- authors: 作者数组
- institution: 机构
- journal: 期刊名称
- publish_date: 发表日期
- abstract: 摘要（如果有）
- keywords: 关键词数组
- pages: 页码
- volume: 卷期
- doi: DOI
- cites: 引用次数
- downloads: 下载次数

### 使用示例

**获取文章摘要命令：**
```
获取这篇文章的摘要信息：https://kns.cnki.net/kcms2/article/abstract?v=xxx
```

**执行步骤：**

1. **导航到文章详情页**
   - 使用 browser.navigate() 直接打开 URL

2. **等待页面加载**
   - 等待主要元素加载完成

3. **提取文章元数据**
   ```javascript
   const metadata = {
     title: document.querySelector('h1')?.textContent.trim(),
     authors: Array.from(document.querySelectorAll('.author-list a'))
       .map(a => a.textContent.trim()),
     institution: document.querySelector('[class*="institution"]')?.textContent.trim(),
     journal: document.querySelector('.journal-name')?.textContent.trim(),
     publish_date: document.querySelector('.publish-date')?.textContent.trim(),
     abstract: document.querySelector('[class*="abstract"]')?.textContent.trim() 
              || document.querySelector('.summary')?.textContent.trim(),
     keywords: Array.from(document.querySelectorAll('.keywords a'))
       .map(a => a.textContent.trim()),
     pages: document.querySelector('.pages')?.textContent.trim(),
     volume: document.querySelector('.volume')?.textContent.trim()
   }
   ```

4. **处理摘要不存在的情况**
   - 某些较老的文献可能没有摘要
   - 返回 metadata 时标注 abstract: null

### 数据示例
```json
{
  "title": "新月形结膜切除联合结膜巩膜固定术治疗结膜松弛症",
  "authors": ["武耀红", "何敏"],
  "institution": "山西医科大学第二医院",
  "journal": "临床医药实践",
  "publish_date": "2006-04-25",
  "abstract": null,
  "keywords": ["结膜松弛症", "新月形切除", "巩膜固定术"],
  "pages": "293-294",
  "volume": "2006(04)",
  "cites": 0,
  "downloads": 45
}
```

---

## 数据字段说明

### 搜索结果字段
| 字段名 | 类型 | 说明 |
|--------|------|------|
| title | string | 文献标题 |
| authors | string | 作者列表（分号分隔） |
| date | string | 发表日期（YYYY-MM-DD） |
| cites | string | 引用次数 |
| source | string | 来源期刊/数据库 |

### 文章详情字段
| 字段名 | 类型 | 说明 |
|--------|------|------|
| title | string | 标题 |
| authors | array | 作者数组 |
| institution | string | 机构 |
| journal | string | 期刊名称 |
| publish_date | string | 发表日期 |
| abstract | string \| null | 摘要（可能为空） |
| keywords | array | 关键词数组 |
| pages | string | 页码（如 "100-105"） |
| volume | string | 卷期（如 "2024年第1期"） |
| doi | string | DOI |
| cites | number | 引用次数 |
| downloads | number | 下载次数 |

---

## 核心实现代码

### 搜索并获取所有结果

```javascript
// 1. 导航到 CNKI 高级搜索页面
await browser.navigate('https://kns.cnki.net/kns8s/AdvSearch?type=expert')

// 2. 等待页面加载并输入搜索词
await browser.wait_for_load()
await browser.fill('input[type="text"]', "SU='结膜松弛' and SU='治疗'")

// 3. 点击搜索按钮
await browser.click('button.primary')

// 4. 等待结果加载
await browser.wait_for_selector('table', { timeout: 10000 })

// 5. 提取数据（翻页处理）
let allData = []
let currentPage = 1
const maxPages = 10

while (currentPage <= maxPages) {
  // 提取当前页数据
  const pageData = await browser.evaluate(() => {
    const data = []
    const table = document.querySelector('table')
    if (!table) return data
    
    const rows = table.querySelectorAll('tr')
    rows.forEach(row => {
      const cells = row.querySelectorAll('td')
      if (cells.length >= 7) {
        const titleEl = cells[1]?.querySelector('a')
        const title = titleEl?.textContent.trim()
        if (title) {
          data.push({
            title: title,
            authors: cells[2]?.textContent.trim() || '',
            date: cells[4]?.textContent.trim() || '',
            cites: cells[6]?.textContent.trim() || '0'
          })
        }
      }
    })
    return data
  })
  
  allData.push(...pageData)
  console.log(`Page ${currentPage}: ${pageData.length} records`)
  
  // 翻到下一页
  if (currentPage < maxPages) {
    await browser.press('ArrowRight')
    await browser.wait(2000) // 等待加载
  }
  
  currentPage++
}

// 6. 保存数据
await browser.download(allData, 'cnki_results.json')
```

### 获取文章摘要信息

```javascript
// 1. 导航到文章详情页
const articleUrl = 'https://kns.cnki.net/kcms2/article/abstract?v=xxx'
await browser.navigate(articleUrl)

// 2. 等待页面加载
await browser.wait_for_load()

// 3. 提取元数据
const metadata = await browser.evaluate(() => {
  const getText = (selector) => {
    const el = document.querySelector(selector)
    return el?.textContent.trim() || ''
  }
  
  return {
    title: getText('h1'),
    authors: Array.from(document.querySelectorAll('.author-list a'))
      .map(a => a.textContent.trim()),
    institution: getText('[class*="institution"]'),
    journal: getText('.journal-name'),
    publish_date: getText('.publish-date'),
    abstract: getText('[class*="abstract"]') || null,
    keywords: Array.from(document.querySelectorAll('.keywords a'))
      .map(a => a.textContent.trim()),
    pages: getText('.pages'),
    volume: getText('.volume')
  }
})

// 4. 返回结果
return metadata
```

---

## 注意事项

### 反机器人策略
1. **降低请求频率**：每页提取后等待1-2秒
2. **使用键盘翻页**：避免点击分页按钮被检测
3. **处理验证失败**：出现验证时等待并重试
4. **保持会话**：复用同一浏览器实例

### 数据完整性
1. 部分文献可能没有摘要（特别是较老的文献）
2. 引用次数可能不准确，以 CNKI 实际显示为准
3. 外文文献可能有不同的字段结构
4. 某些文献需要登录/付费才能查看摘要

### 性能优化
- 批量处理时将每页数据保存到文件
- 避免一次性提取过多数据
- 使用增量更新而非全量更新

---

## 错误处理

### 常见错误
1. **验证超时**：等待60秒后重试
2. **页面加载失败**：刷新页面后重试
3. **数据提取失败**：使用备选选择器
4. **网络中断**：保存已获取数据后重试

### 日志记录
建议记录：
- 搜索关键词和参数
- 每页提取的数据条数
- 错误信息和重试次数
- 总耗时和成功率

---

## 使用限制
- 仅供学术研究使用
- 遵守 CNKI 服务条款
- 合理控制请求频率
- 尊重知识产权

---

## 更新日志

### 2026-03-05
- 初始版本
- 支持搜索结果获取
- 支持文章摘要提取
