# Cnki Export

> Export paper from CNKI and push to Zotero, or save as RIS file. Use when user wants to save a paper to Zotero or export citation data.

- Skill: `wentorai/cnki-export` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add wentorai/cnki-export`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wentorai/cnki-export/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: wentorai (https://skillmd.com/u/wentorai)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/wentorai/cnki-export

---


# CNKI Export & Zotero Integration

> **RC browser tool**: navigate with `browser action=open url="..."`; run the JS in each step via `browser action=act kind=evaluate fn="<the async function shown>"` (the whole function body goes into `fn`). Never pass a `profile` — RC uses its default managed Chrome (CDP 18800).

Export paper citation data from CNKI and push directly to Zotero, or save as RIS file.

## Arguments

- `zotero` (default) — push to Zotero desktop via local API
- `ris` — save as .ris file
- `gb` — output GB/T 7714 citation text
- Optionally include a paper URL

## Mode Selection

Choose the right mode based on context:

| Context | Mode | Tool calls |
|---------|------|-----------|
| On a paper detail page | Single export (Step 1A) | 1 evaluate + 1 bash = **2** |
| On a search results page, save all/selected | **Batch export (Step 1B)** | 1 evaluate + 1 bash = **2** |
| Need to search then save | Use cnki-search first, then batch export | **4 total** |

**Always prefer batch export (1B) when multiple papers need saving.** It avoids navigating to each detail page (saves ~3 calls per paper).

## Steps

### 1A. Single export: from paper detail page

Use `browser action=act kind=evaluate`:

```javascript
async () => {
  const url = document.querySelector('#export-url')?.value;
  const params = document.querySelector('#export-id')?.value;
  const uniplatform = new URLSearchParams(window.location.search).get('uniplatform') || 'NZKPT';
  if (!url || !params) return { error: 'Not on a paper detail page' };

  const resp = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({ filename: params, displaymode: 'GBTREFER,elearning,EndNote', uniplatform })
  });
  const data = await resp.json();
  if (data.code !== 1) return { error: data.msg };

  const result = {};
  for (const item of data.data) {
    result[item.mode] = item.value[0];
  }

  const body = document.body.innerText;
  result.pageUrl = window.location.href;
  result.issn = body.match(/ISSN[：:]\s*(\S+)/)?.[1] || '';
  result.dbcode = document.querySelector('#paramdbcode')?.value || '';
  result.dbname = document.querySelector('#paramdbname')?.value || '';
  result.filename = document.querySelector('#paramfilename')?.value || '';

  return result;
}
```

### 1B. Batch export: from search results page (PREFERRED for multiple papers)

On any CNKI search results page, extract checkbox values and call the export API directly — **no need to navigate to detail pages**.

Key discovery: `input.cbItem` checkbox `value` === detail page `#export-id` (same encrypted ID).

Use `browser action=act kind=evaluate`:

```javascript
async () => {
  const API_URL = 'https://kns.cnki.net/dm8/API/GetExport';

  // Get all checkbox values (= export encrypted IDs)
  const checkboxes = document.querySelectorAll('.result-table-list tbody input.cbItem');
  const rows = document.querySelectorAll('.result-table-list tbody tr');

  if (checkboxes.length === 0) return { error: 'No results on page' };

  const allPapers = [];
  for (let i = 0; i < checkboxes.length; i++) {
    const exportId = checkboxes[i].value;
    const paperUrl = rows[i]?.querySelector('td.name a.fz14')?.href || '';

    const resp = await fetch(API_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({ filename: exportId, displaymode: 'GBTREFER,elearning,EndNote', uniplatform: 'NZKPT' })
    });
    const data = await resp.json();
    if (data.code === 1) {
      const result = {};
      for (const item of data.data) { result[item.mode] = item.value[0]; }
      result.pageUrl = paperUrl;
      // Extract ISSN from ENDNOTE %@ field
      const issnMatch = result.ENDNOTE?.match(/%@\s*([^\s<]+)/);
      result.issn = issnMatch ? issnMatch[1] : '';
      result.dbcode = 'CJFQ';
      result.dbname = '';
      result.filename = '';
      allPapers.push(result);
    }
  }

  return allPapers; // JSON array, directly writable to file for Python script
}
```

**To export only specific papers** (e.g. #1, #3, #5), filter by index:

```javascript
// Replace the for loop condition:
const indices = [0, 2, 4]; // 0-indexed: papers #1, #3, #5
for (let i = 0; i < checkboxes.length; i++) {
  if (!indices.includes(i)) continue;
  // ... rest same
}
```

### 2. Push to Zotero

Save the export data (single object or JSON array) to a temp file, then run the Python script:

```bash
python3 skills/cnki-export/scripts/push_to_zotero.py /tmp/papers.json
```

The Python script handles both single paper `{}` and batch `[{}, {}, ...]` JSON input.

- UTF-8 encoding (avoids Windows encoding issues)
- Parsing ELEARNING format into Zotero item fields
- Calling `POST http://127.0.0.1:23119/connector/saveItems`
- Returns: 201 = success, 500 = error, 0 = Zotero not running

### 3. Report result

Single:
```
已将论文添加到 Zotero:
  标题: {title}
  作者: {authors}
  期刊: {journal}

GB/T 7714 引用: {gbt_citation}
```

Batch:
```
已批量添加 {count} 篇论文到 Zotero:
  1. {title1} ({journal1})
  2. {title2} ({journal2})
  ...
```

## Export API Reference

| Parameter | Value | Source |
|-----------|-------|--------|
| API URL | `https://kns.cnki.net/dm8/API/GetExport` | Fixed, works from any page |
| filename | Encrypted ID | Detail page: `#export-id`; Results page: `input.cbItem` value |
| displaymode | `GBTREFER,elearning,EndNote` | Comma-separated modes |
| uniplatform | `NZKPT` | Required |

## Verified selectors

| Element | Selector | Page |
|---------|----------|------|
| Export URL | `#export-url` | Detail page only |
| Export ID | `#export-id` | Detail page only |
| Checkbox (= export ID) | `input.cbItem` | Search results page |
| Result rows | `.result-table-list tbody tr` | Search results page |
| Title link | `td.name a.fz14` | Search results page |

## Zotero API Reference

```
POST http://127.0.0.1:23119/connector/saveItems
Content-Type: application/json
X-Zotero-Connector-API-Version: 3
```

**Response:** 201 = created, 500 = error
**Collection:** Saves to Zotero's currently selected collection.

Query collections:
```bash
python3 skills/cnki-export/scripts/push_to_zotero.py --list
```

## Important Notes

- **Windows encoding:** Must use Python script, cannot pass Chinese JSON via bash/curl directly
- **Zotero must be running:** `localhost:23119` requires Zotero desktop in background
- **Chinese authors:** Use `name` field (single field, not split), `creatorType: "author"`
- **Batch export saves ~90% tool calls:** 9 papers: 33 calls → 3 calls
- **CNKI Export API:** `filename` must be encrypted ID (`#export-id` or `input.cbItem` value), NOT `#paramfilename`

