ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python爬虫实战:Web of Science文献批量导出与结构化处理

Python爬虫实战:Web of Science文献批量导出与结构化处理 1. 文献批量导出这件事到底卡在哪儿做科研的人对 Web of Science 这个平台肯定不陌生文献检索、引文分析、影响因子查询基本都绕不开它。但真正让人头疼的往往不是找不到文献而是找到了之后怎么把数据弄出来。Web of Science 的导出功能一次最多只能选 500 条记录格式还限定在纯文本、BibTeX、RIS 等几种想批量拿到自己想要的字段比如标题、作者、期刊、年份、DOI、摘要、被引次数就得一遍遍手动操作。如果检索结果有几千条甚至上万条这个工作量足以让人崩溃。我最早接触这个需求是帮一个做文献计量学的朋友处理数据。他需要把某个领域近十年的文献全部导出然后做关键词共现分析和引文网络图谱。当时他的做法是手动分批次导出每次 500 条导出后再用 Excel 合并。结果折腾了一整天还因为中途漏了几批导致数据对不上。后来我帮他写了一套 Python 脚本把整个流程自动化从检索结果页面抓取文献列表到逐条解析字段再到输出成结构化的 Excel 和 CSV 文件全程不到 5 分钟。这套方案的核心工具就是requests负责请求、BeautifulSoup负责解析 HTML、pandas负责数据清洗和导出。这篇文章面向的是有一定 Python 基础、但还没怎么接触过爬虫实战的科研人员或数据爱好者。我会把整个项目的设计思路、关键代码、踩过的坑和排查技巧都讲清楚让你看完就能自己动手复现。需要说明的是Web of Science 是付费数据库下面的操作前提是你所在机构已经购买了访问权限并且你是在合规范围内使用自己的账号进行数据导出。这一点很重要后面我会专门讲注意事项。2. 整体方案设计与技术选型思路2.1 为什么选择 requests BeautifulSoup 而不是 Scrapy很多人一提到爬虫就想到 Scrapy觉得框架化、分布式、性能强。但对于 Web of Science 这种需要登录态、页面结构相对固定、数据量在几千到几万条的场景Scrapy 反而显得笨重。原因有几个第一Scrapy 的学习曲线陡峭光是理解 Item、Pipeline、Middleware 这些概念就要花不少时间而我们的目标用户是科研人员不是专业爬虫工程师第二Web of Science 的页面是典型的服务端渲染HTML 结构清晰用 BeautifulSoup 解析完全够用不需要处理复杂的 JavaScript 渲染第三requests BeautifulSoup 的组合更灵活调试方便出问题了直接打印响应内容就能定位。当然如果你需要处理的是动态加载的页面比如某些新版界面用 AJAX 异步加载数据那可能就需要考虑 Playwright 或 Selenium 这类工具。但根据我的实测Web of Science 的检索结果页面和导出页面目前仍然是服务端渲染为主requests 直接请求就能拿到完整的 HTML。这一点在后面的实操环节我会再验证。2.2 数据流设计从检索到结构化表格整个项目的数据流可以拆成四个阶段。第一阶段是会话建立用 requests.Session() 保持登录态把浏览器里的 Cookie 和请求头复制过来确保后续请求都被识别为已登录用户。第二阶段是列表抓取根据检索条件构造 URL逐页请求检索结果页面解析出每条文献的标题、作者、期刊、年份等基础信息同时记录每条文献的详情页链接或导出链接。第三阶段是详情解析对需要额外字段的文献请求其详情页或导出接口解析出摘要、DOI、被引次数、参考文献等更完整的信息。第四阶段是数据清洗与导出用 pandas 把抓取到的数据整理成 DataFrame处理缺失值、去重、类型转换最后输出成 Excel 和 CSV 文件。这个设计的好处是模块化每个阶段可以独立调试。比如列表抓取出了问题你只需要检查 URL 构造和 HTML 解析逻辑不用管后面的数据清洗。另外我建议在第二阶段和第三阶段之间加一个本地缓存机制把每次请求的 HTML 保存到本地文件这样调试解析逻辑时就不用反复请求服务器既节省时间又降低被封的风险。2.3 合规性与风险控制这里必须重点强调合规问题。Web of Science 的服务条款对自动化访问有明确限制频繁请求可能触发反爬机制导致账号被临时封禁。我的做法是第一严格控制请求频率每次请求之间加 2 到 3 秒的随机延迟第二优先使用平台自带的导出功能能批量导出就不逐条抓取第三只在必要时请求详情页列表页能拿到的字段就不要多请求一次第四把请求头里的 User-Agent 设置成真实浏览器的值避免被识别为脚本。这些措施不能保证百分之百安全但能显著降低风险。另外抓取到的数据只能用于个人学术研究不能用于商业用途或大规模分发。这一点在学术圈是有共识的我在帮朋友做这个项目时也反复提醒他注意。3. 核心细节解析与实操要点3.1 登录态获取与 Session 配置Web of Science 的检索结果页面需要登录才能访问所以第一步是把浏览器的登录态搬到 Python 里。具体操作是在浏览器里正常登录 Web of Science打开开发者工具切换到 Network 标签页刷新页面找到任意一个请求复制它的 Cookie 和 User-Agent。然后在 Python 里这样配置import requests session requests.Session() 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, Cookie: 你的Cookie字符串, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, })这里有个细节Cookie 是有有效期的通常几个小时到几天不等。如果脚本跑着跑着突然返回登录页面大概率是 Cookie 过期了重新复制一次就行。我建议把 Cookie 单独存在一个配置文件里不要硬编码在代码中方便更新。注意不要把自己的 Cookie 分享给别人也不要把带 Cookie 的代码上传到公开仓库这相当于把账号密码交出去了。3.2 检索结果页面的 URL 构造与分页逻辑Web of Science 的检索结果 URL 通常包含几个关键参数查询字符串、数据库范围、每页显示数量、当前页码。以经典检索为例URL 结构大概是这样的https://www.webofscience.com/wos/woscc/summary/xxxxx/relevance/1其中最后的数字是页码。每页默认显示 50 条可以通过参数调整到 100 条。分页的逻辑就是递增这个页码直到页面返回空结果或达到总记录数。我通常会在第一页解析出总记录数然后计算总页数用一个循环逐页抓取。解析总记录数的方法是找到页面上显示“找到 X 条结果”的元素用 BeautifulSoup 提取文本再用正则表达式抽出数字。这个数字决定了循环的终止条件避免无限请求。3.3 列表页字段解析标题、作者、期刊、年份列表页的 HTML 结构相对规整每条文献通常包裹在一个带有特定 class 的 div 或 li 标签里。用 BeautifulSoup 的 find_all 方法定位所有文献条目然后逐个提取字段。以标题为例通常是一个 a 标签class 里包含“title”字样。作者信息可能在一个 span 或 div 里多个作者用分号或逗号分隔。期刊名和年份可能在同一个文本块里需要用正则表达式拆分。这里有个坑不同学科的文献作者姓名的格式可能不一样有的显示全名有的只显示缩写。我的处理方式是统一按字符串存储后续在 pandas 里再做标准化。另外有些文献没有作者信息或者作者字段为空解析时要加 try-except 防止报错。3.4 详情页与导出接口的选择策略列表页能拿到的字段有限通常只有标题、作者、期刊、年份、被引次数。如果需要摘要、DOI、参考文献、基金信息等就得进入详情页或调用导出接口。我的策略是先用列表页数据做一轮筛选只对真正需要的文献请求详情页。比如你只关心被引次数超过 10 的文献那就先按这个条件过滤再抓详情能省下大量请求。Web of Science 的导出接口支持多种格式其中纯文本格式包含的字段最全而且解析起来比 HTML 简单。如果平台允许批量导出优先走导出接口把导出的文本文件下载到本地再用 Python 解析。这种方式比逐条抓详情页效率高得多也更合规。4. 完整实操流程与关键代码实现4.1 环境准备与依赖安装先确保你的 Python 环境是 3.8 以上版本。如果你还没装 Python去官网下载安装包安装时勾选“Add Python to PATH”。然后打开命令行安装必要的库pip install requests beautifulsoup4 pandas openpyxl lxml这里解释一下每个库的作用requests 负责发送 HTTP 请求beautifulsoup4 负责解析 HTMLpandas 负责数据处理和导出openpyxl 是 pandas 写 Excel 文件的引擎lxml 是 BeautifulSoup 的解析器比默认的 html.parser 更快更稳。如果你用的是 PyCharm 或 VS Code也可以在 IDE 的终端里执行这条命令。提示如果 pip 下载速度慢可以加国内镜像源比如-i https://pypi.tuna.tsinghua.edu.cn/simple。4.2 第一步抓取检索结果列表假设你已经登录 Web of Science并且手动检索出了一批文献。现在把浏览器地址栏的 URL 复制下来作为脚本的起始 URL。下面是一个抓取列表页并解析基础字段的示例代码import requests from bs4 import BeautifulSoup import time import random import re import pandas as pd session requests.Session() session.headers.update({ User-Agent: 你的User-Agent, Cookie: 你的Cookie, }) def parse_list_page(html): soup BeautifulSoup(html, lxml) records [] items soup.find_all(div, class_re.compile(summary-record)) for item in items: record {} title_tag item.find(a, class_re.compile(title)) record[title] title_tag.get_text(stripTrue) if title_tag else authors_tag item.find(span, class_re.compile(authors)) record[authors] authors_tag.get_text(stripTrue) if authors_tag else source_tag item.find(span, class_re.compile(source)) record[source] source_tag.get_text(stripTrue) if source_tag else records.append(record) return records all_records [] base_url 你的检索结果URL for page in range(1, 11): url base_url f/{page} resp session.get(url) if resp.status_code ! 200: print(f第 {page} 页请求失败状态码 {resp.status_code}) break page_records parse_list_page(resp.text) if not page_records: print(f第 {page} 页没有数据停止抓取) break all_records.extend(page_records) print(f第 {page} 页抓取完成共 {len(page_records)} 条) time.sleep(random.uniform(2, 3)) df pd.DataFrame(all_records) print(df.head())这段代码的核心逻辑是循环请求每一页解析出文献条目累积到列表里最后转成 DataFrame。注意 class 名称我用的是正则匹配因为 Web of Science 的 class 名可能带有随机后缀精确匹配容易失效。实际使用时你需要根据当前页面的 HTML 结构调整正则表达式。4.3 第二步解析详情页补充字段列表页拿到的字段不够用的话就需要请求详情页。详情页的 URL 通常藏在列表页的标题链接里提取 href 属性即可。下面是一个解析详情页的示例def parse_detail_page(html): soup BeautifulSoup(html, lxml) detail {} abstract_tag soup.find(div, class_re.compile(abstract)) detail[abstract] abstract_tag.get_text(stripTrue) if abstract_tag else doi_tag soup.find(a, stringre.compile(10\\.)) detail[doi] doi_tag.get_text(stripTrue) if doi_tag else return detail实际使用时你需要先检查详情页的 HTML 结构找到对应字段的标签和 class。不同学科的详情页布局可能略有差异建议先用一两条文献做测试确认解析逻辑正确后再批量跑。4.4 第三步数据清洗与 Excel 导出抓取到的数据往往有缺失值、重复项、格式不统一的问题。用 pandas 处理这些非常方便df.drop_duplicates(subset[title], inplaceTrue) df[year] df[source].str.extract(r(\\d{4})) df[abstract] df[abstract].fillna() df.to_excel(wos_literature.xlsx, indexFalse) df.to_csv(wos_literature.csv, indexFalse, encodingutf-8-sig)这里有几个细节去重时按标题去重因为同一篇文献可能在不同页面重复出现年份用正则从来源字段里提取导出 CSV 时用 utf-8-sig 编码避免 Excel 打开中文乱码。如果你需要把数据导入到其他分析工具CSV 格式通用性更好如果只是自己看Excel 更直观。4.5 参数计算与请求频率控制请求频率的控制很关键。我的经验是每次请求间隔 2 到 3 秒这个时间足够让服务器认为你是正常人类操作又不会太慢。如果抓取 1000 条文献每页 50 条共 20 页加上详情页请求总请求数可能在 100 次左右按 2.5 秒间隔算总耗时约 4 分钟。这个速度是可以接受的。如果你需要抓取的数据量特别大比如上万条建议分批次进行每次抓几百条就停下来隔几个小时再继续。这样既能降低风险又不会因为一次请求太多导致 IP 被限制。5. 常见问题与排查技巧实录5.1 请求返回 403 或登录页面这是最常见的问题原因通常是 Cookie 过期或请求头不完整。排查步骤第一检查 Cookie 是否还是最新的重新从浏览器复制一次第二检查 User-Agent 是否设置正确不要用默认的 python-requests第三检查是否有其他必要的请求头比如 Referer。如果还是不行可能是账号权限问题确认你所在机构是否订阅了该数据库。5.2 解析结果为空或字段错位页面结构变化是爬虫的天敌。如果之前能正常解析突然拿不到数据了大概率是 Web of Science 更新了页面布局。排查方法是把请求到的 HTML 保存到本地用浏览器打开手动检查目标字段的标签和 class 是否变了。然后相应调整 BeautifulSoup 的查找条件。我建议在代码里加一个日志把每次解析到的记录数打印出来一旦发现某页记录数为零就能快速定位问题。5.3 中文乱码与编码问题Web of Science 的页面编码通常是 UTF-8但偶尔也会遇到 GBK 或其他编码。如果发现中文显示乱码可以在请求后手动设置编码resp.encoding utf-8。导出 CSV 时用 utf-8-sigExcel 打开就不会乱码。如果还是有问题可以先用 pandas 读入再重新导出pandas 会自动处理编码转换。5.4 数据去重与缺失值处理文献数据里重复记录很常见尤其是同一篇文献在不同检索条件下都出现。去重时不能只按标题因为有些标题可能完全相同但作者不同。我的做法是组合标题和第一作者作为唯一键这样准确率更高。缺失值方面摘要字段经常为空可以用空字符串填充后续分析时再决定是否剔除。问题类型典型表现排查思路解决方案登录失效返回登录页 HTML检查 Cookie 有效期重新复制 Cookie请求被拒403 状态码检查请求头和频率加延迟、换 User-Agent解析为空记录数为零对比页面结构调整 class 匹配规则编码乱码中文显示异常检查响应编码手动设置 encoding数据重复同一文献多条检查去重键组合标题和作者去重5.5 实操心得与避坑建议最后分享几个我在实际项目中总结的经验。第一先小规模测试再批量跑用 10 条数据验证整个流程确认没问题再扩大规模。第二保存中间结果每抓完一页就把数据存到本地万一中途出错不用从头再来。第三不要贪心能通过官方导出功能拿到的数据就不要爬既省事又安全。第四定期更新代码Web of Science 的页面结构可能几个月就变一次保持关注及时调整解析逻辑。这套方案我前后用了两年多帮好几个做文献计量的朋友处理过数据稳定性还是可以的。核心思路就是用最朴素的工具解决最实际的问题不追求技术炫技只追求能跑通、能复现、能省时间。如果你在实操过程中遇到其他问题欢迎在评论区交流我看到后会尽量回复。
返回列表