实战分享Python爬虫,如何实现高效解析 Web of Science 文献数据并导出 CSV

在科研信息分析、选题调研、竞品技术追踪、论文计量研究中,Web of Science(下文简称 WoS)一直是高价值数据来源。它收录规范、元数据结构完整,尤其适合做文献统计分析:作者、机构、关键词、被引、出版年、研究方向等字段都比较标准化。

但很多同学在实际操作时会遇到一个问题:如何高效、稳定、合规地提取文献数据,并整理成可分析的 CSV?

本文给你一套完整的 Python 实战方案,目标是:

  1. 明确 WoS 数据抓取的合规边界;
  2. 设计可维护的爬虫与解析流程;
  3. 实现从页面/接口响应中抽取核心文献字段;
  4. 处理分页、重试、去重、反爬、编码、异常;
  5. 最终导出高质量 CSV,便于后续用 pandas、Excel、R、SPSS、VOSviewer 做分析。

重要说明:本文仅用于合法合规的数据处理与科研用途。请严格遵守 WoS 平台服务条款、机构订阅协议、robots 规范及当地法律。若平台提供官方 API,优先使用官方 API。


一、先讲清楚:技术可行 ≠ 合规可用

在写任何爬虫前,先做三件事:

  • 阅读目标平台使用条款(Terms of Use)
  • 确认是否允许自动化采集
  • 优先评估是否有官方 API 或导出功能

对于 WoS 这类商业数据库,很多学校/机构是通过订阅访问,通常会对批量抓取有限制。

因此实践建议是:

  1. 优先官方导出/API:最稳、最合规。
  2. 若做自动化采集,只在授权范围内,控制频率,避免高并发冲击。
  3. 不绕过登录安全机制,不规避访问控制,不抓取无授权内容。

二、需求拆解:我们到底要抓哪些字段?

为了后续结构化分析,通常建议最少抓以下字段:

  • title(标题)
  • authors(作者)
  • source(期刊/会议)
  • year(年份)
  • doi
  • abstract(摘要)
  • keywords(关键词)
  • affiliations(机构)
  • times_cited(被引次数)
  • document_type
  • wos_id / accession_number
  • url(详情页链接)

CSV 输出建议一行一条文献,复杂字段(作者、关键词)用分号拼接。


三、技术路线设计:页面抓取还是接口抓取?

常见有两条路线:

路线A:抓网页 HTML 再解析

  • 工具:requests + BeautifulSoup/lxml
  • 优点:直观、好理解
  • 缺点:页面结构改版就容易失效;动态加载场景麻烦

路线B:抓浏览器真实请求的 JSON 接口(推荐)

  • 工具:开发者工具 Network + requests/httpx
  • 优点:数据结构稳定、解析简单、效率高
  • 缺点:可能涉及鉴权 token、cookie、header 维护

在现代站点里,很多"页面内容"本质由接口返回 JSON 渲染。
实战优先选择"接口层数据",可大幅提升稳定性。


四、项目结构建议(可维护)

建议把代码拆成 6 个模块,而不是全写一个文件:

text

wos_spider/ ├── config.py # 配置:关键词、页数、延迟、输出路径 ├── session.py # 会话初始化、headers、cookie管理 ├── fetcher.py # 请求发送、重试、限速 ├── parser.py # JSON/HTML字段解析 ├── pipeline.py # 去重、清洗、导出CSV └── main.py # 主流程编排

这样的好处:后续换接口、加字段、改导出格式都更容易。


五、实战代码:从请求到导出 CSV(示例模板)

说明:以下代码是"通用结构示例",接口 URL 和参数需按你实际授权环境抓包确认后替换。不要用于未授权抓取。

1)安装依赖

bash

pip install requests pandas tenacity tqdm

2)配置文件 config.py

python

QUERY = "machine learning" START_PAGE = 1 END_PAGE = 20 PAGE_SIZE = 50 SLEEP_SECONDS = 1.5 OUTPUT_CSV = "wos_records.csv" BASE_URL = "https://example.wos.api/search" # 替换为实际接口 HEADERS = { "User-Agent": "Mozilla/5.0", "Accept": "application/json", }

3)请求与重试 fetcher.py

python

import time import requests from tenacity import retry, stop_after_attempt, wait_fixed class WosFetcher: def init(self, headers=None, sleep_seconds=1.0): self.s = requests.Session() if headers: self.s.headers.update(headers) self.sleep_seconds = sleep_seconds @retry(stop=stop_after_attempt(3), wait=wait_fixed(2)) def get_page(self, url, params): resp = self.s.get(url, params=params, timeout=20) if resp.status_code != 200: raise Exception(f"HTTP {resp.status_code}") time.sleep(self.sleep_seconds) return resp.json()

4)解析模块 parser.py

python

def safe_get(d, path, default=None): cur = d for p in path: if isinstance(cur, dict) and p in cur: cur = curp else: return default return cur def parse_record(item): title = safe_get(item, "title", "") authors = safe_get(item, "authors", \[\]) or \[\] authors = "; ".join(a.get("name", "") for a in authors if isinstance(a, dict)) keywords = safe_get(item, "keywords", \[\]) or \[\] if isinstance(keywords, list): keywords = "; ".join(str(k) for k in keywords) affiliations = safe_get(item, "affiliations", \[\]) or \[\] if isinstance(affiliations, list): affiliations = "; ".join( aff.get("org", "") if isinstance(aff, dict) else str(aff) for aff in affiliations ) return { "wos_id": safe_get(item, "id", ""), "title": title, "authors": authors, "source": safe_get(item, "source", ""), "year": safe_get(item, "year", ""), "doi": safe_get(item, "doi", ""), "abstract": safe_get(item, "abstract", ""), "keywords": keywords, "affiliations": affiliations, "times_cited": safe_get(item, "times_cited", 0), "document_type": safe_get(item, "doc_type", ""), "url": safe_get(item, "url", ""), }

5)数据清洗与导出 pipeline.py

python

import pandas as pd def clean_and_export(records, output_csv): df = pd.DataFrame(records) # 基础清洗 for col in df.columns: dfcol = dfcol.astype(str).str.replace(r"\s+", " ", regex=True).str.strip() # 去重:优先 DOI,其次标题 if "doi" in df.columns: df = df.sort_values("doi").drop_duplicates(subset="doi", keep="first") df = df.drop_duplicates(subset="title", keep="first") # 年份与被引转数值(失败置空) if "year" in df.columns: df"year" = pd.to_numeric(df"year", errors="coerce") if "times_cited" in df.columns: df"times_cited" = pd.to_numeric(df"times_cited", errors="coerce").fillna(0).astype(int) df.to_csv(output_csv, index=False, encoding="utf-8-sig") return df

6)主程序 main.py

python

from tqdm import tqdm from config import QUERY, START_PAGE, END_PAGE, PAGE_SIZE, BASE_URL, HEADERS, OUTPUT_CSV, SLEEP_SECONDS from fetcher import WosFetcher from parser import parse_record from pipeline import clean_and_export def run(): fetcher = WosFetcher(headers=HEADERS, sleep_seconds=SLEEP_SECONDS) all_records = \[\] for page in tqdm(range(START_PAGE, END_PAGE + 1), desc="Fetching"): params = { "q": QUERY, "page": page, "size": PAGE_SIZE } data = fetcher.get_page(BASE_URL, params=params) items = data.get("records", \[\]) # 按实际返回结构调整 for item in items: try: all_records.append(parse_record(item)) except Exception as e: print(f"ParseError page={page}, err={e}") df = clean_and_export(all_records, OUTPUT_CSV) print(f"Done. rows={len(df)}, file={OUTPUT_CSV}") if name == "main": run()


六、关键实战技巧:让爬虫"高效且稳定"

1)限速与随机延迟

不要连续高频请求。建议基础延时 1~3 秒,并加入随机抖动,降低触发风控概率。

2)失败重试与断点续跑

网络波动是常态。

  • 对 429/5xx 做重试
  • 每抓一页就落盘(JSONL/CSV 临时文件)
  • 中断后从最后页继续

3)会话保持

很多站点依赖会话态(cookie、token)。

用 requests.Session() 统一管理,必要时定期刷新 token。

4)字段容错

真实数据会有缺项。解析时不要假设字段一定存在,统一使用 safe_get。

5)编码统一

导出 CSV 用 utf-8-sig,在 Windows Excel 打开中文更稳。


七、常见问题与解决方案

问题1:返回 403/401

  • 可能缺少鉴权头、cookie 失效、权限不足
  • 检查登录态与请求头是否完整
  • 优先走官方 API 鉴权方式

问题2:抓到的是空数据

  • 参数名不对(如 q、page、size 实际不同)
  • 返回结构层级变化(records 不在顶层)
  • 查询条件在 URL 编码后失真

问题3:速度慢

  • 减少不必要字段请求
  • 用接口 JSON 而非 HTML 解析
  • 合规前提下做小规模并发(谨慎)

问题4:CSV 打开乱码

  • 改为 encoding="utf-8-sig"
  • 或用支持 UTF-8 的工具(VSCode、LibreOffice、pandas)

问题5:重复数据多

  • 先按 doi 去重,再按 title + year 辅助去重
  • 标题统一大小写、去多余空格后再比对

八、数据质量提升:从"能用"到"好用"

采集完成只是第一步,分析前建议做标准化:

  1. 作者名标准化(如 Zhang, Wei vs Wei Zhang)
  2. 机构名归一化(同一机构多写法合并)
  3. 关键词清洗(单复数、缩写归并)
  4. 年份异常值处理(空值、未来年份)
  5. DOI 格式校验(正则匹配)

可以额外输出两份文件:

  • raw_wos.csv(原始留档)
  • clean_wos.csv(清洗后分析用)

九、进阶:异步抓取与批量分析(可选)

当数据量较大时,可以考虑:

  • httpx + asyncio 异步请求(仍需限速)
  • 增加代理池(合规前提)
  • 落地 SQLite/PostgreSQL 而非一次性 CSV
  • 用 Airflow/Prefect 定时任务化
  • 结合 NLP 做主题建模、关键词共现网络

但请注意:规模越大,越要先确认授权边界


十、官方 API 优先策略(强烈建议)

如果你的机构有 WoS API 使用权限,建议直接走官方路线:

  • 字段文档清晰
  • 请求稳定可预期
  • 版本升级可追踪
  • 合规风险更低
  • 方便持续化数据管道建设

你可以把本文的代码结构几乎原样迁移到 API,只需替换请求地址和解析器字段映射。

编程语言C++https://github.com/aidehualeo/tech-bjwmfu/blob/main/README.md ++c语言的魅力

编程语言C++https://github.com/aidehualeo/tech-vrstri/blob/main/README.md ++c语言的魅力

编程语言C++https://github.com/aidehualeo/tech-uelhoa/blob/main/README.md ++c语言的魅力

"Python 爬虫 + 文献数据"真正的专业能力,不是写出一个能跑的脚本,而是做到这四点:

  1. 合规:尊重平台规则与授权边界;
  2. 稳定:可重试、可恢复、可维护;
  3. 结构化:字段清晰、编码统一、可直接分析;
  4. 可扩展:后续可接数据库、可做自动化任务。

围绕这些原则,你就能把 WoS 文献采集从"临时脚本"升级为"科研数据工程流程"。

最终产出的 CSV,不只是一个表格,而是你后续做计量分析、热点挖掘、知识图谱构建的高质量起点。

相关推荐
山川湖海3 小时前
AI时代快速学编程语言的陷阱(以Python为例)
大数据·人工智能·python
H Journey3 小时前
Supervisor 进程管理工具介绍
python·supervisor·linux 运维
掘金013 小时前
EmbedPDF Vue 版 完整正文文档 全网首发
前端
OpenTiny社区3 小时前
操作ArkTS页面跳转及路由相关心得
前端·typescript·web·opentiny
xiaohua0708day3 小时前
Lodash库
前端·javascript·vue.js
huakoh3 小时前
Claude Code 从零到上手指南:国产工具链复现80% Agent能力,DeepSeek+LangChain实战
前端
Ankkaya3 小时前
浏览器插件接入 Google 登录
前端
Asmewill3 小时前
DeepAgents学习笔记一(构建深度多智能体)
前端
万物皆对象6663 小时前
切换路由时页面空白问题(vue3)
前端·vue.js·typescript
突然好热3 小时前
TS 调试技巧
前端·javascript·typescript