摘要

在科研工作中,批量获取文献元数据与全文是文献综述、数据挖掘的核心前置环节。本文针对知网(CNKI)、万方、Google Scholar 三大主流学术平台,从反爬机制分析、核心流程设计、可运行代码实现三个维度,完整讲解批量文献爬虫的落地方法,同时明确技术实现的合规边界与风险规避方案,为科研人员与开发者提供可复用的实战参考。

郑重声明:本文所有技术内容仅用于个人科研学习与学术研究,任何爬虫行为均需遵守平台《用户协议》、robots.txt 规则与《著作权法》,禁止大规模爬取、商用传播受版权保护的文献内容,违规操作需自行承担相应法律责任。

一、前置准备与环境搭建

1.1 技术栈选型

本次实战以 Python 为核心开发语言,兼顾轻量化与通用性,核心依赖库如下:

  • 网络请求:requestsaiohttp(异步请求)
  • 页面解析:BeautifulSoup4lxml
  • 浏览器自动化:selenium(处理登录、动态渲染与验证码)
  • 谷歌学术专用:scholarly(封装好的 Google Scholar 元数据接口)
  • 反爬辅助:fake_useragent(UA 轮换)、time/random(请求延时)、ddddocr(简易验证码识别)
  • 数据存储:pandas(元数据导出)、os(文件批量管理)

1.2 权限与环境前置

  1. 账号权限:知网、万方的全文下载依赖有效账号权限,优先使用机构 IP(高校 / 科研院所校园网)或付费个人账号,无权限账号仅能爬取文献元数据(标题、作者、摘要、DOI 等)。
  2. 网络环境:Google Scholar 需可正常访问谷歌服务的网络环境,建议搭配稳定的代理池使用。
  3. 基础配置:安装 Chrome 浏览器与对应版本的 ChromeDriver,用于 Selenium 自动化操作。

二、知网(CNKI)批量下载爬虫实战

2.1 平台反爬机制分析

知网是国内反爬强度最高的学术平台之一,核心拦截机制包括:

  • 强制登录校验:全文下载、高级检索均需有效登录态,Cookie 有效期短且存在行为校验
  • 动态页面渲染:检索结果列表通过 JS 动态加载,静态请求无法直接获取完整数据
  • 频率限制:短时间内高频请求会触发验证码弹窗,甚至临时封禁 IP 与账号
  • 签名校验:下载接口存在动态参数签名,直接拼接 URL 无法触发下载

2.2 整体实现流程

  1. Selenium 模拟登录,获取有效 Cookie
  2. 构造检索关键词与筛选条件,进入检索结果列表页
  3. 分页解析列表,提取每篇文献的标题、作者、DOI、详情页 URL
  4. 批量进入文献详情页,定位 PDF/CAJ 下载链接
  5. 携带 Cookie 发起下载请求,按规范命名保存本地文件
  6. 加入随机延时与失败重试机制,规避反爬

2.3 核心代码实现

python

运行

import time
import random
import os
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup

# 配置项
LOGIN_URL = "https://my.cnki.net/Register/CommonLogin.aspx"
SEARCH_URL = "https://kns.cnki.net/kns8s/defaultresult/index"
SAVE_PATH = "./cnki_papers"
KEYWORD = "自然语言处理 知识图谱"
MAX_PAGE = 3

os.makedirs(SAVE_PATH, exist_ok=True)

# 1. 初始化浏览器并模拟登录
driver = webdriver.Chrome()
driver.get(LOGIN_URL)
# 预留手动登录时间(账号密码/扫码登录,避免明文存储账号)
time.sleep(30)

# 2. 构造检索请求
driver.get(SEARCH_URL)
time.sleep(2)
# 输入关键词并检索
driver.find_element(By.ID, "txt_SearchText").send_keys(KEYWORD)
driver.find_element(By.CLASS_NAME, "search-btn").click()
time.sleep(3)

# 3. 分页爬取文献链接
paper_links = []
for page in range(MAX_PAGE):
    soup = BeautifulSoup(driver.page_source, "lxml")
    # 解析当前页所有文献详情页链接
    items = soup.select(".name a")
    for item in items:
        link = "https://kns.cnki.net" + item["href"]
        paper_links.append(link)
    print(f"第{page+1}页解析完成,累计获取{len(paper_links)}篇文献")
    
    # 点击下一页
    try:
        driver.find_element(By.ID, "PageNext").click()
        time.sleep(random.uniform(3, 6))
    except:
        break

# 4. 批量下载全文
cookies = {item["name"]: item["value"] for item in driver.get_cookies()}
headers = {"User-Agent": driver.execute_script("return navigator.userAgent")}

for idx, url in enumerate(paper_links):
    try:
        res = requests.get(url, cookies=cookies, headers=headers)
        soup = BeautifulSoup(res.text, "lxml")
        # 定位PDF下载链接
        pdf_btn = soup.select_one(".pdfDown")
        if not pdf_btn:
            print(f"第{idx+1}篇无PDF权限,跳过")
            continue
        
        download_url = "https://kns.cnki.net" + pdf_btn["href"]
        paper_title = soup.select_one("h1.title").text.strip().replace("/", "_")
        
        # 下载文件
        pdf_res = requests.get(download_url, cookies=cookies, headers=headers, stream=True)
        file_path = os.path.join(SAVE_PATH, f"{paper_title}.pdf")
        with open(file_path, "wb") as f:
            for chunk in pdf_res.iter_content(chunk_size=1024):
                f.write(chunk)
        print(f"第{idx+1}篇下载完成:{paper_title}")
        time.sleep(random.uniform(5, 10))
    except Exception as e:
        print(f"第{idx+1}篇下载失败:{str(e)}")
        time.sleep(random.uniform(10, 15))

driver.quit()

2.4 优化与踩坑提示

  • 优先使用校园网 IP 访问,可绕过部分登录校验与权限限制
  • 避免使用 CAJ 格式下载,PDF 通用性更强,解析链路更简单
  • 单小时请求量建议控制在 50 次以内,高频下载极易触发账号封禁
  • 若出现图形验证码,可接入 ddddocr 自动识别,或增加人工处理窗口

三、万方数据批量下载爬虫实战

3.1 平台特性与反爬特点

万方的反爬强度略低于知网,核心特点为:

  • 接口相对规整,部分检索接口可直接通过 HTTP 请求调用
  • 全文下载依赖登录 Cookie,存在单 IP 下载频次限制
  • 文献格式以 PDF 为主,下载链接无复杂签名,仅校验登录态与权限

3.2 实现逻辑

  1. 模拟登录获取有效 Cookie(支持账号密码与机构登录)
  2. 调用检索接口,传入关键词、页码、筛选条件,解析返回的文献列表
  3. 提取文献 ID,拼接全文下载 URL
  4. 批量发起下载请求,本地保存文件

3.3 核心代码示例

python

运行

import requests
import random
import time
import os
from bs4 import BeautifulSoup

# 配置项
BASE_URL = "https://www.wanfangdata.com.cn"
SEARCH_API = "https://s.wanfangdata.com.cn/PaperSearch"
SAVE_PATH = "./wanfang_papers"
KEYWORD = "分布式系统 微服务"
PAGE_SIZE = 20
MAX_PAGE = 2

os.makedirs(SAVE_PATH, exist_ok=True)

# 登录后手动复制Cookie填入(避免账号明文存储)
cookies = {
    "SESSION": "你的登录SESSION",
    "WFIP_CHECK": "1"
}
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Referer": "https://www.wanfangdata.com.cn/"
}

# 1. 批量获取文献ID与标题
paper_list = []
for page in range(1, MAX_PAGE+1):
    params = {
        "q": KEYWORD,
        "pageNum": page,
        "pageSize": PAGE_SIZE,
        "resourceType": "periodical"
    }
    res = requests.get(SEARCH_API, params=params, headers=headers, cookies=cookies)
    soup = BeautifulSoup(res.text, "lxml")
    items = soup.select(".result-item")
    for item in items:
        title = item.select_one(".title a").text.strip().replace("/", "_")
        paper_id = item["data-id"]
        paper_list.append({"id": paper_id, "title": title})
    print(f"第{page}页解析完成,累计{len(paper_list)}篇")
    time.sleep(random.uniform(2, 4))

# 2. 批量下载PDF
for idx, paper in enumerate(paper_list):
    try:
        # 拼接下载链接
        download_url = f"{BASE_URL}/peripheral/peripheralDown.do?id={paper['id']}&type=pdf"
        res = requests.get(download_url, headers=headers, cookies=cookies, stream=True, timeout=30)
        
        # 校验是否为PDF文件
        if "application/pdf" not in res.headers.get("Content-Type", ""):
            print(f"第{idx+1}篇无下载权限:{paper['title']}")
            continue
        
        file_path = os.path.join(SAVE_PATH, f"{paper['title']}.pdf")
        with open(file_path, "wb") as f:
            for chunk in res.iter_content(chunk_size=1024):
                f.write(chunk)
        print(f"第{idx+1}篇下载完成:{paper['title']}")
        time.sleep(random.uniform(4, 8))
    except Exception as e:
        print(f"第{idx+1}篇下载失败:{str(e)}")
        time.sleep(random.uniform(8, 12))

3.4 注意事项

  • 万方检索接口参数会随版本更新调整,若解析失败需重新抓包确认接口字段
  • 学位论文、会议论文的下载接口路径与期刊不同,需根据资源类型调整 URL 拼接规则
  • 机构账号存在并发下载限制,避免多线程同时发起下载请求

四、Google Scholar 批量文献获取实战

4.1 平台特点

Google Scholar 无需登录即可获取文献元数据,全文链接多跳转至出版商官网、预印本平台(arXiv 等)或高校仓储,核心难点在于:

  • IP 限制严格,高频请求极易触发人机验证与临时封禁
  • 无官方公开 API,页面结构会不定期调整
  • 全文链接分散,不同来源的下载规则差异较大

4.2 基于 scholarly 的轻量化实现

scholarly是开源的 Google Scholar 第三方封装库,可直接获取文献元数据与 PDF 链接,无需手动解析页面。

python

运行

from scholarly import scholarly
import os
import requests
import random
import time

SAVE_PATH = "./scholar_papers"
KEYWORD = "large language model alignment"
MAX_COUNT = 20

os.makedirs(SAVE_PATH, exist_ok=True)

# 配置代理(按需开启)
# proxy = {"http": "http://127.0.0.1:7890", "https": "http://127.0.0.1:7890"}
# scholarly.use_proxy(**proxy)

# 检索文献
search_results = scholarly.search_pubs(KEYWORD)
count = 0

for paper in search_results:
    if count >= MAX_COUNT:
        break
    
    title = paper["bib"]["title"].replace("/", "_")
    pdf_url = paper.get("eprint_url", None)
    
    if not pdf_url:
        print(f"第{count+1}篇无公开PDF链接:{title}")
        count += 1
        continue
    
    try:
        res = requests.get(pdf_url, stream=True, timeout=20)
        if "application/pdf" in res.headers.get("Content-Type", ""):
            file_path = os.path.join(SAVE_PATH, f"{title}.pdf")
            with open(file_path, "wb") as f:
                for chunk in res.iter_content(chunk_size=1024):
                    f.write(chunk)
            print(f"第{count+1}篇下载完成:{title}")
        else:
            print(f"第{count+1}篇链接非PDF:{title}")
    except Exception as e:
        print(f"第{count+1}篇下载失败:{str(e)}")
    
    count += 1
    time.sleep(random.uniform(8, 15))

4.3 封禁规避方案

  • 搭配住宅代理池使用,轮换 IP 发起请求,避免单 IP 高频访问
  • 降低请求频率,单 IP 每小时请求量建议不超过 30 次
  • 触发人机验证时,可接入 Selenium 手动完成验证,更新 Cookie 后继续运行

五、工程化优化:通用爬虫能力增强

5.1 请求层反爬对抗

  1. 随机延时:每次请求间隔加入 3-10 秒的随机等待,模拟人工浏览行为
  2. UA 轮换:使用fake_useragent库随机切换浏览器 User-Agent,避免固定 UA 被识别
  3. 代理池搭建:针对海外平台与高频爬取场景,接入 HTTP 代理池,自动轮换 IP
  4. 断点续传:下载前校验本地文件是否存在,避免重复下载;大文件支持分片续传

5.2 下载稳定性优化

  • 增加失败重试机制:使用tenacity库对超时、请求失败的任务自动重试,最多重试 3 次
  • 异步并发控制:使用aiohttp实现异步下载,同时通过信号量限制并发数(建议不超过 3)
  • 元数据归档:将标题、作者、DOI、发表期刊、下载状态存入 CSV/Excel,方便后续文献管理

5.3 文件管理规范

  • 统一以「第一作者_年份_标题」格式命名文件,避免重复与乱码
  • 按研究主题分文件夹存储,配套元数据表格,支持快速检索

六、合规边界与风险提示

6.1 著作权合规

学术文献受《著作权法》保护,批量下载的文献仅可用于个人学习、研究,不得进行二次传播、售卖、商用或大规模爬取构建数据库,否则可能构成著作权侵权。

6.2 平台规则风险

知网、万方、Google Scholar 均明确禁止自动化爬虫批量下载行为,违规操作可能导致:

  • 个人 / 机构账号被永久封禁
  • 机构 IP 被平台加入黑名单,影响整个单位的文献访问权限
  • 平台发起法律追责,承担民事赔偿责任

6.3 法律风险

大规模爬虫行为若对平台服务器造成负担,或绕过技术措施获取受限内容,可能触犯《网络安全法》《反不正当竞争法》,情节严重的还可能涉及刑事风险。所有爬虫操作必须控制在合理、低频、个人使用的范围内

七、总结

三大平台的批量爬取各有侧重:知网爬虫的核心难点是登录态维护与反爬规避,万方爬虫更侧重接口解析,Google Scholar 则聚焦于 IP 封禁应对与公开全文链接提取。对于科研人员而言,优先使用平台官方提供的批量导出、文献管理工具是最安全的选择;确需自定义爬虫时,必须严格遵守合规红线,控制爬取频率,避免对平台正常服务造成影响。

技术只是效率工具,学术研究始终要建立在合规与版权尊重的基础之上。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐