资讯动态

Python爬虫实战:Web of Science论文数据自动化采集与导出指南

发布时间:2026/9/1 21:30:50 来源:尧图企业网站定制
简介这是一套面向科研人员与Python初学者的Web of Science学术数据采集工具集解决人工检索效率低、批量获取论文元数据如标题、作者、摘要、年份、引用数困难的问题。资源共21个文件包含7个核心Python脚本如cl2_crawl_data.py用于主爬取、citaton.py处理引文关系、cl_deal_data2.py实现数据清洗与结构化、6个XML配置/工程文件、2个PYC编译缓存及README.md项目说明文档等整体仅100KB轻量易部署。已有4125人学习下载体现其在文献计量分析、研究趋势挖掘和知识图谱构建等场景中的实用价值。用户可直接复用爬虫逻辑适配其他学术平台结合citation0.xls样例数据与city_code.txt地理编码支持快速开展实证分析代码模块分工明确含爬取、清洗、存储、引用解析全流程具备教学示范性与工程延展性。 做科研的人大概率都经历过这种痛苦在Web of Science上查文献辛辛苦苦筛了几百篇论文结果只能一页一页手动复制标题、作者、期刊、被引次数到Excel里。点开一篇、复制、切窗口、粘贴、再点开下一篇……两个小时过去手都快抽筋了数据还没整理完。如果只是几十篇还好可一旦涉及文献计量分析或综述写作几千条记录靠手工整理根本不现实。我前阵子就接了个这样的需求用Python写一个Web of Science论文爬虫程序把检索出来的论文元数据成批量抓下来直接落成结构化表格。这篇文章把整个实现过程完整梳理一遍包含思路拆解、核心代码、翻车记录和排查方法给有同样需求的朋友作参考。无论你是还在摸索的Python新手还是写过很多爬虫但第一次碰WoS的老手这篇都能帮你少走不少弯路。1. 先把方案想清楚为什么不能直接抓HTML页面1.1 新版WoS的数据藏在接口里不在页面源码里很多人一上来就用requests把Web of Science的页面拉下来然后甩给BeautifulSoup去解析结果发现HTML里全是JavaScript框架的加载壳子论文数据一个都没有。这不是你代码写错了而是新版WoSwos.woscc已经换成了前后端分离的架构。页面上的标题、作者、被引次数全都是通过浏览器里的XHR请求向后端要数据拿到JSON之后再由JS动态渲染出来的。你直接抓HTML抓到的是空壳。所以正确的做法是先打开浏览器的开发者工具把Network面板里的XHR请求从头到尾捋一遍找到真正返回论文数据的那个接口然后用Python直接模拟这个请求。我以主流的WoS检索流程为例一个大致的请求链路是这样的打开检索页拿到一个初始的CSRF Token防止跨站请求伪造的随机令牌构造检索式比如TS(python AND crawler)带上Token和Cookie发搜索请求后端返回一段JSON里面包含总记录数、当前页的论文列表、排序信息等翻页时再带着同样的Token请求下一页拿到后续数据你把这条链路摸清楚爬虫就成功了一大半。1.2 技术选型requests JSON解析就够没必要上重型框架我在选技术方案时对比过几种常见做法这里直接给你结论方案优点缺点适合场景requests 直接解析JSON轻量、快、可控需要手动处理Token、翻页逻辑个人批量采集WoS数据首选Scrapy框架调度器成熟、能处理大规模并发学习成本高WoS接口需要自定义Pipeline杀鸡用牛刀大规模采集任务或分布式抓取Selenium / Playwright模拟真实浏览器能绕过复杂前端校验启动慢、内存占用高、容易触发反爬风控WoS改版没适配完之前的临时方案我最终选了requests直接调JSON接口。核心原因是WoS的检索逻辑是确定性的请求头固定、参数固定、返回也是固定的JSON结构不需要像应付反爬特别强的站点那样上浏览器渲染。requests一条Session走到底处理Token和Cookie更直接。另外提醒一句网上有些老教程还在带你用Selenium点页面、拖滚动条、截图识别验证码这些方案在新版WoS上基本不用考虑又慢又容易被限制。把时间花在分析接口上收益高得多。2. 核心细节解析Token、会话和字段结构是三大命门2.1 CSRF Token怎么拿、怎么用WoS的接口请求里有一个很关键的安全机制CSRF Token。简单理解这是服务器发给你的一张“一次性通行证”。你第一次访问页面时服务器会通过Cookie下发一个XSRF-TOKEN后续每次发搜索请求时必须把这个Token放在请求头里而且请求头里的字段名一般是X-XSRF-TOKEN。为什么Web of Science要搞这么一套因为Token可以防止别人伪造请求地址诱导用户提交数据。但对于咱们写爬虫的人来说这意味着流程变成两步先用无Token的GET请求访问检索页收集Cookie里的XSRF-TOKEN后续POST请求带上这个Token让服务器认为你是正常的浏览器会话如果跳过第一步直接发POST大概率会收到401或者403错误。这是我第一次跑通时踩的坑后面会详细说排查方法。2.2 Session会话保持是爬虫的“记忆”WoS的登录态靠Cookie和Session维持。如果你所在机构没有开IP免登录那你就得先用自己的账号登录一次让requests的Session对象把登录后的Cookie保存下来后续所有检索请求都会自动附带上这些Cookie。这就很像你去图书馆借书第一次进门时门口保安给你贴了个手环之后你每次在里面走动都不用再重新解释你是谁。Session就是帮你把这个“手环”一直戴着让后续请求都处于已登录状态。实际操作中我的做法是import requests session requests.Session() # 先访问检索页让服务器下发Token和Cookie init_resp session.get(https://www.webofscience.com/wos/woscc/basic-search) print(init_resp.status_code) # 从Cookie里取出Token xsrf_token session.cookies.get(XSRF-TOKEN)这里有个细节很多Cookie里的Token是URL编码过的取出来后可能需要先做一次unquote再放进请求头否则服务器比对不上会报Token不匹配。2.3 返回的JSON里到底有哪些字段把搜索接口拿到手之后你会看到一大坨嵌套JSON。第一次看容易晕但其实主要就这几层metadata.totalRecords这个检索式命中的总记录数records[]当前页的论文数组每条record里又有title、authors、sourceTitle、pubYear、volume、issue、pages、doi、citationCount等字段我这里直接列一个常用字段映射方便你后续解析时对照JSON字段含义用途uidWoS平台内部唯一标识去重、关联title论文标题核心字段authors作者列表可能是字符串数组拼接作者注意分隔符sourceTitle期刊或会议名称分析发文期刊pubYear出版年份年代趋势分析doi数字对象标识符跳转全文、查重citationCount被引次数影响力分析abstract摘要文本有时为空文本挖掘、关键词提取一个容易出问题的点是作者字段在不同检索式下有时返回的格式不一样。有的接口直接给你一个数组有的给你一个逗号分隔的字符串。我的建议是解析时统一做一次兼容处理不管它是数组还是字符串都先转成统一的列表格式避免后面写Excel时出现一行挤了十几个作者的情况。3. 实操过程与核心代码实现3.1 环境准备这个项目不需要太多第三方库核心就三个requests负责发HTTP请求pandas负责数据清洗和导出Excel/CSVlxml或BeautifulSoup备用解析工具偶尔碰到HTML格式的摘要时需要用到安装命令pip install requests pandas beautifulsoup4 lxml如果你用的是Anaconda也可以直接conda install requests pandas beautifulsoup4 lxmlPython版本建议3.8以上低于3.8的话有些类型注解和语法糖用不了倒不是不能跑但没必要跟自己过不去。3.2 建立Session并获取Token第一步是需要一个能保持登录态的Session同时拿到CSRF Token。import requests from urllib.parse import unquote BASE_URL https://www.webofscience.com/wos/woscc/basic-search def create_session(): session requests.Session() # 模拟浏览器的User-Agent降低被服务端拒绝的概率 session.headers.update({ 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, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9,en;q0.8, }) # 访问检索页服务器会下发Cookie和XSRF-TOKEN resp session.get(BASE_URL, timeout30) resp.raise_for_status() # 取出Token注意解码 token session.cookies.get(XSRF-TOKEN) if token: token unquote(token) session.headers.update({ X-XSRF-TOKEN: token }) return session这一步为什么必须单独抽一个函数因为后面每次翻页、每次换检索式你都需要带着同一个Session和Token去请求。Token过期或者丢失程序就会从能稳定跑变成时好时坏非常折磨人。3.3 执行检索并解析数据接下来是核心检索函数。我以一个常见的检索式TS(python AND crawler)为例模拟搜索“主题包含python和crawler”的论文。import json import pandas as pd import time def search_wos(session, query, page1, page_size50): # 这里以WoS搜索接口为例具体路径请以实际抓包为准 search_url https://www.webofscience.com/wos/woscc/api/search/records params { q: query, page: page, page_size: page_size, } resp session.post(search_url, jsonparams, timeout30) resp.raise_for_status() data resp.json() records data.get(records, []) total data.get(metadata, {}).get(totalRecords, 0) return total, records这里有个地方要特别注意WoS的检索式语法和数据库字段相关。TS表示主题检索常用的还有AU作者检索、TI标题检索、SO来源出版物检索。想查某个主题的文献最常用的就是TS(关键词 AND 关键词2)这种组合。这个语法不是Python逻辑而是WoS数据库自己的检索语言写错了会导致查到的结果跟预期差很多。解析记录时我把每条论文的数据清洗成扁平结构方便存表格def parse_records(records): rows [] for rec in records: row { 标题: rec.get(title, ), 期刊: rec.get(sourceTitle, ), 出版年: rec.get(pubYear, ), DOI: rec.get(doi, ), 被引次数: rec.get(citationCount, 0), } # 处理作者字段兼容数组和字符串两种格式 authors rec.get(authors, []) if isinstance(authors, str): authors authors.split(;) row[作者] ; .join([a.strip() for a in authors if a.strip()]) rows.append(row) return rows3.4 翻页逻辑与总记录数控制WoS的检索结果动辄几千条但单次请求只能拿一页。所以必须有个循环把每一页的数据都串起来。这里有一个经验参数我一般把每页条数设为50条。因为WoS接口对单次请求返回的记录数有限制设太大容易触发服务端限制设太小请求次数太多又容易暴露爬虫特征50是一个比较平衡的值。翻页逻辑参考这个模板def fetch_all(session, query, max_records1000): all_rows [] page 1 page_size 50 while True: total, records search_wos(session, query, pagepage, page_sizepage_size) rows parse_records(records) all_rows.extend(rows) print(f正在抓取第{page}页累计{len(all_rows)}条全部共{total}条) # 抓够了或者已经超过总数就停止 if len(all_rows) min(total, max_records): break page 1 # 控制请求频率不要太暴力 time.sleep(1.5) return all_rows这个time.sleep(1.5)值是我试出来的一个相对安全的间隔。太短了容易触发限流太长了又浪费时间。如果你想更保守一点可以改成time.sleep(random.uniform(1.2, 2.5))让请求间隔不那么规律看起来更像人工操作。3.5 导出CSV数据抓下来之后导出其实是最简单的环节但有一个极其常见的坑直接用to_csv写文件Excel打开之后中文全变成乱码。原因是CSV的默认编码是UTF-8而Excel默认按GBK解析。解决办法是导出时用utf-8-sig编码这个编码会在文件开头加上BOM头Excel看到BOM就能正确识别为UTF-8。import pandas as pd def export_to_csv(rows, filenamewos_papers.csv): df pd.DataFrame(rows) df.to_csv(filename, indexFalse, encodingutf-8-sig) print(f已导出{len(rows)}条数据到{filename})如果你想要Excel格式也可以用df.to_excel(wos_papers.xlsx, indexFalse)但需要额外安装openpyxl库pip install openpyxl3.6 一个小demo检索“python爬虫”相关论文把上面的函数串起来跑一个完整示例if __name__ __main__: session create_session() query TS(python AND crawler) rows fetch_all(session, query, max_records200) export_to_csv(rows, python_crawler_wos.csv)运行结束后你会得到一个200行的CSV文件每行包含标题、期刊、出版年、DOI、被引次数和作者列表。把这些数据导入Excel后直接做个数据透视表就能看出这个领域的发文趋势和核心期刊这些后续分析工作会在本文第5节展开聊。4. 常见问题与排查技巧实录4.1 请求报403 Forbidden这是我调试时遇到最多的错误基本就两种原因一是Token没带上或者Token是URL编码后的状态。解决办法是回到create_session()里检查是否对XSRF-TOKEN做了unquote解码。二是请求头里缺少浏览器特征。WoS会校验User-Agent和Accept字段如果你用的还是requests默认的python-requests/2.x很容易被识别。直接把代码里那段浏览器UA复制过去就行。4.2 抓到的数据是空的如果请求成功返回200但records数组是空的优先检查两件事第一检索式是否真的能查到数据。先在浏览器里手动搜一下相同的关键词如果浏览器也搜不出来说明是检索式的问题不是爬虫的问题。第二分页参数是不是从0开始或者从1开始。有的接口页码从0开始计数有的从1开始这个差别会导致你永远只能抓到第一页或者永远抓到空。我一般先打印每次请求的URL和响应体前几百个字符一眼就能看出是不是参数对不上。4.3 请求太频繁被限流WoS对请求频率比较敏感如果连续请求几百次不休息很容易被临时限制访问。表现是明明前面还能正常返回突然开始报429错误。排查方法很简单看响应头的Retry-After字段它会告诉你需要等多久才能继续。我的处理方式是捕获异常后等待一个随机时间再重试import time import random def safe_request(session, url, **kwargs): for attempt in range(5): try: resp session.post(url, **kwargs) if resp.status_code 429: wait_time random.uniform(30, 60) print(f触发限流等待{wait_time:.1f}秒后重试) time.sleep(wait_time) continue resp.raise_for_status() return resp except requests.RequestException as e: print(f第{attempt 1}次请求失败: {e}) time.sleep(2) raise RuntimeError(请求多次失败)别小看这个重试机制。没有它你的脚本可能在跑到第800条的时候挂掉前面所有数据都得重新抓。有了它至少能保证脚本在大多数情况下坚持跑完全程。4.4 CSV打开乱码这个前面已经说了导出时用utf-8-sig编码即可。还有一个相关细节如果你要把CSV重新读回pandas最好也指定编码df pd.read_csv(wos_papers.csv, encodingutf-8-sig)要不然下次读入时第一列标题会带一个看不见的\ufeff字符干扰后续数据分析。4.5 部分论文缺少作者或摘要WoS里不是每条记录都完整。有些会议论文没有摘要有些很老的记录作者信息不全。如果你在解析时直接访问rec[authors]遇到不存在的键就会抛KeyError。一个稳妥的习惯是统一用rec.get(authors, [])并判断类型。我的parse_records函数里已经处理了这种情况但如果你后续自己加字段记得也按这个思路写容错。做爬虫解析宁可多写几行防御性代码也不要赌每一条数据都完整。4.6 常见问题速查表这里把上面提到的坑整理成一张表方便你排查时对照异常现象可能原因解决办法403 ForbiddenToken缺失或编码错误检查XSRF-TOKEN是否unquote请求头是否带X-XSRF-TOKEN返回200但records为空检索式无结果或页码参数不对先用浏览器验证检索式再检查page参数从0还是1开始429 Too Many Requests请求频率过高加随机延时捕获429后等待30-60秒重试Excel打开CSV乱码编码不是utf-8-sig导出时指定encodingutf-8-sigKeyError: authors单条记录字段缺失用rec.get(authors, [])做容错5. 合规边界与后续扩展玩法5.1 关于爬取WoS的合规建议必须放在前面Web of Science是商业数据库不是完全开放的数据源。使用爬虫程序采集数据的前提是你所在的机构学校、研究所、公司购买了合法的访问权限并且你的采集行为限于个人学术研究或工作需求。换句话说不要用这个程序去海量下载所有论文数据再公开传播也不要绕过机构订阅直接硬闯付费墙。我的习惯是只采集自己检索式范围内的数据不搞全库遍历请求频率保持克制不给对方服务器造成压力采集到的数据仅用于个人分析不公开发布原始数据集如果只是需要少数论文的元数据优先考虑官方导出的Excel功能关于官方功能补充一句WoS本身支持导出文献直接导出最多可以选500条再点击“添加到标记结果列表”还能凑更多。如果你的数据量很小用官方功能就够了。爬虫解决的痛点是什么是你需要对大量检索式反复筛选、需要把数据自动汇总成自定义格式、需要跑本地批量分析。这种情况下写爬虫才值得。5.2 后续扩展从数据采集到文献计量分析数据抓下来只是第一步真正的价值在后续分析。这里分享几个我常用到的扩展方向你可以顺着这个思路继续玩下去一是发文趋势分析。把“出版年”列做个透视表看看某个研究方向的论文数量在近十年的变化曲线可以很直观地判断这个领域是处在上升期、成熟期还是衰退期。二是期刊偏好分析。统计不同期刊的发文量再结合影响因子能帮你确认哪些期刊是该领域的重点阵地对投稿选刊很有参考意义。三是关键词词频分析。从每条记录的标题接出来清洗掉停用词之后做词频统计就能快速得出这个领域的核心研究主题分布。再进一步可以用VOSviewer把高共现的词做成网络图一图看出研究热点之间的关联。四是高被引论文筛选。按“被引次数”降序排列直接找出领域内的经典文献对文献综述写作收益极大。最初我把这个项目定位成“能跑就行”但真正做完之后发现这个爬虫程序更像是给文献计量分析建立了一条高效的数据管道。以前整理一百条文献要一下午现在跑一遍程序一杯咖啡还没凉数据表格就已经摆在桌面上了这个效率提升是实打实的。5.3 给新手的最后提醒如果你之前没怎么写过爬虫我的建议是不要一上来就把所有功能都写进一个大脚本。先把Session建起来、把Token拿到手用代码打印出第一条论文数据确认能通之后再一步一步加翻页、加解析、加导出。每一步都验证通过再往前走排查起来会轻松很多。爬虫程序最忌讳的就是写一个几百行的“火箭代码”然后一次性运行。出了错都不知道从哪查起。分阶段推进慢就是快这个经验适用于所有数据采集类项目。我现在自己用这套脚本已经把好几个研究方向的数据都跑完了。每次换一个新的检索式改一行query重新运行几十秒就能拿到一份结构化的论文数据表。Web of Science论文爬虫这个需求看起来是个具体的小项目实际上它是把“检索—采集—清洗—分析”这条科研工作流打通的关键一环。希望这篇文章能帮你省下几个小时的调试时间把精力真正投入到文献本身。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价