在学术研究与文献综述工作中,知网(CNKI)作为国内最核心的学术文献数据库,其文献数据的采集与分析是研究工作的重要基础。手动逐条下载、整理文献信息不仅效率低下,也难以实现规模化的数据分析。本文将系统介绍如何基于 Python 实现知网文献的批量采集,并通过可视化手段对采集到的文献数据进行多维度分析,帮助研究者快速挖掘文献背后的研究趋势、关键词分布等核心信息。
一、技术原理与环境准备
1.1 核心技术栈说明
本次实现基于 Python 3.8 + 版本,核心依赖库包括:
- Selenium:模拟浏览器操作,解决知网动态加载和反爬机制问题
- BeautifulSoup4:解析 HTML 页面,提取文献核心信息
- Pandas:数据清洗与结构化存储
- Matplotlib/Seaborn:数据可视化展示
- WordCloud:生成关键词词云,直观呈现研究热点
1.2 环境配置
安装依赖包,同时需要下载对应浏览器的驱动(如 ChromeDriver),并配置系统环境变量,确保 Selenium 能正常调用浏览器。
二、知网文献批量采集实现
2.1 核心采集逻辑
知网文献采集的核心难点在于其反爬机制和动态页面加载特性。本文采用 Selenium 模拟真人浏览行为,通过以下步骤实现批量采集:
- 模拟登录知网(可选,部分文献需登录后查看)
- 构造搜索请求,定位文献列表页面
- 解析页面提取文献元数据(标题、作者、发表时间、关键词、摘要、被引量等)
- 分页遍历,批量存储数据
- 数据清洗,去除重复和无效记录
2.2 完整采集代码实现
python
运行
import time
import pandas as pd
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
import warnings
warnings.filterwarnings('ignore')
class CNKICrawler:
def __init__(self, chrome_driver_path=None):
"""初始化爬虫对象"""
# 配置Chrome浏览器选项
self.options = webdriver.ChromeOptions()
# 无头模式,不显示浏览器窗口(调试时可注释)
self.options.add_argument('--headless')
self.options.add_argument('--disable-blink-features=AutomationControlled')
self.options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 初始化浏览器驱动
if chrome_driver_path:
self.driver = webdriver.Chrome(executable_path=chrome_driver_path, options=self.options)
else:
self.driver = webdriver.Chrome(options=self.options)
# 设置隐式等待时间
self.driver.implicitly_wait(10)
# 存储文献数据
self.literature_data = []
def login_cnki(self, username=None, password=None):
"""
登录知网(可选)
:param username: 账号
:param password: 密码
"""
if not username or not password:
print("未提供账号密码,将以游客模式访问")
return
try:
self.driver.get('https://www.cnki.net/')
# 点击登录按钮
login_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.ID, 'J_Quick2Login'))
)
login_btn.click()
# 切换到账号密码登录(根据知网页面结构调整)
self.driver.find_element(By.CLASS_NAME, 'login-tab-account').click()
# 输入账号密码
self.driver.find_element(By.ID, 'allUserName').send_keys(username)
self.driver.find_element(By.ID, 'allPassword').send_keys(password)
# 点击登录
self.driver.find_element(By.ID, 'btn_login').click()
time.sleep(3)
print("登录成功")
except Exception as e:
print(f"登录失败:{str(e)}")
def search_literature(self, keyword, page_num=5):
"""
搜索并采集文献数据
:param keyword: 搜索关键词
:param page_num: 要采集的页数
"""
try:
# 访问知网搜索页面
self.driver.get('https://www.cnki.net/')
# 定位搜索框并输入关键词
search_box = self.driver.find_element(By.ID, 'kw')
search_box.clear()
search_box.send_keys(keyword)
# 点击搜索按钮
self.driver.find_element(By.CLASS_NAME, 'search-btn').click()
time.sleep(3)
# 分页采集
for page in range(1, page_num + 1):
print(f"正在采集第{page}页数据...")
# 等待页面加载完成
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'result-list'))
)
# 解析当前页文献数据
self._parse_literature_page()
# 跳转到下一页(最后一页则退出)
if page < page_num:
try:
next_btn = self.driver.find_element(By.CLASS_NAME, 'next-page')
if 'disabled' in next_btn.get_attribute('class'):
print("已到最后一页,停止采集")
break
next_btn.click()
time.sleep(3)
except Exception as e:
print(f"翻页失败:{str(e)}")
break
print(f"采集完成,共获取{len(self.literature_data)}条文献数据")
except Exception as e:
print(f"搜索采集失败:{str(e)}")
def _parse_literature_page(self):
"""解析单页文献数据"""
soup = BeautifulSoup(self.driver.page_source, 'lxml')
# 定位所有文献条目
literature_items = soup.find_all('div', class_='result-item')
for item in literature_items:
try:
# 提取核心信息
literature_info = {
'title': item.find('a', class_='fz14').get_text(strip=True) if item.find('a', class_='fz14') else '',
'authors': item.find('span', class_='author').get_text(strip=True) if item.find('span', class_='author') else '',
'source': item.find('span', class_='source').get_text(strip=True) if item.find('span', class_='source') else '',
'publish_time': item.find('span', class_='date').get_text(strip=True) if item.find('span', class_='date') else '',
'keywords': item.find('div', class_='keywords').get_text(strip=True).replace('关键词:', '') if item.find('div', class_='keywords') else '',
'abstract': item.find('div', class_='abstract').get_text(strip=True).replace('摘要:', '') if item.find('div', class_='abstract') else '',
'citation': item.find('span', class_='cite').get_text(strip=True).replace('被引量:', '') if item.find('span', class_='cite') else '0'
}
self.literature_data.append(literature_info)
except Exception as e:
print(f"解析单条文献失败:{str(e)}")
continue
def save_data(self, file_path='cnki_literature.csv'):
"""将采集的数据保存为CSV文件"""
if not self.literature_data:
print("无采集数据,无需保存")
return
df = pd.DataFrame(self.literature_data)
# 数据清洗:去除空标题记录
df = df[df['title'] != '']
# 去重
df = df.drop_duplicates(subset=['title'])
# 保存
df.to_csv(file_path, index=False, encoding='utf-8-sig')
print(f"数据已保存至:{file_path}")
return df
def close(self):
"""关闭浏览器"""
self.driver.quit()
# 主程序执行
if __name__ == '__main__':
# 初始化爬虫
crawler = CNKICrawler()
# 可选:登录知网(需替换为自己的账号密码)
# crawler.login_cnki(username='your_username', password='your_password')
# 搜索关键词为"人工智能"的文献,采集5页数据
crawler.search_literature(keyword='人工智能', page_num=5)
# 保存数据
df = crawler.save_data('cnki_ai_literature.csv')
# 关闭浏览器
crawler.close()
# 打印数据预览
print("数据预览:")
print(df.head())
2.3 代码关键说明
- 反爬规避:通过禁用自动化特征、添加隐式等待等方式模拟真人操作,降低被知网反爬机制识别的概率;
- 模块化设计:将登录、搜索、解析、保存等功能拆分为独立方法,便于维护和扩展;
- 异常处理:关键步骤添加异常捕获,避免单个文献解析失败导致整个程序中断;
- 数据清洗:自动去除空标题和重复记录,保证数据质量。
三、文献数据可视化分析
采集到文献数据后,我们需要通过可视化手段挖掘数据价值。以下基于采集到的 CSV 文件,实现多维度可视化分析。
3.1 可视化分析代码实现
python
运行
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from wordcloud import WordCloud
import re
from collections import Counter
# 设置中文字体(解决中文显示乱码问题)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
class CNKIVisualizer:
def __init__(self, data_path):
"""初始化可视化对象"""
# 读取采集的数据
self.df = pd.read_csv(data_path, encoding='utf-8-sig')
# 数据预处理
self._preprocess_data()
def _preprocess_data(self):
"""数据预处理"""
# 将发表时间转换为datetime格式
self.df['publish_time'] = pd.to_datetime(self.df['publish_time'], errors='coerce')
# 提取发表年份
self.df['publish_year'] = self.df['publish_time'].dt.year
# 将被引量转换为数值型
self.df['citation'] = pd.to_numeric(self.df['citation'], errors='coerce').fillna(0)
def plot_year_distribution(self):
"""绘制文献发表年份分布柱状图"""
plt.figure(figsize=(12, 6))
year_counts = self.df['publish_year'].value_counts().sort_index()
sns.barplot(x=year_counts.index, y=year_counts.values, palette='viridis')
plt.title('知网文献发表年份分布', fontsize=14)
plt.xlabel('发表年份', fontsize=12)
plt.ylabel('文献数量', fontsize=12)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('year_distribution.png', dpi=300)
plt.show()
def plot_citation_top10(self):
"""绘制被引量Top10文献条形图"""
top10_citation = self.df.nlargest(10, 'citation')
plt.figure(figsize=(14, 8))
sns.barplot(x='citation', y='title', data=top10_citation, palette='rocket')
plt.title('知网文献被引量Top10', fontsize=14)
plt.xlabel('被引量', fontsize=12)
plt.ylabel('文献标题', fontsize=12)
plt.tight_layout()
plt.savefig('citation_top10.png', dpi=300)
plt.show()
def generate_keyword_wordcloud(self):
"""生成关键词词云"""
# 合并所有关键词
all_keywords = ' '.join(self.df['keywords'].dropna())
# 去除特殊字符
all_keywords = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', all_keywords)
# 生成词云
wordcloud = WordCloud(
font_path='simhei.ttf', # 需确保字体文件存在
width=1000,
height=600,
background_color='white',
max_words=200,
max_font_size=100,
random_state=42
).generate(all_keywords)
# 绘制词云
plt.figure(figsize=(12, 8))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.title('知网文献关键词词云', fontsize=14)
plt.tight_layout()
plt.savefig('keyword_wordcloud.png', dpi=300)
plt.show()
def plot_source_top10(self):
"""绘制发表来源Top10饼图"""
top10_source = self.df['source'].value_counts().head(10)
plt.figure(figsize=(10, 10))
plt.pie(top10_source.values, labels=top10_source.index, autopct='%1.1f%%', startangle=90)
plt.title('知网文献发表来源Top10', fontsize=14)
plt.axis('equal')
plt.tight_layout()
plt.savefig('source_top10.png', dpi=300)
plt.show()
# 主程序执行
if __name__ == '__main__':
# 初始化可视化对象
visualizer = CNKIVisualizer('cnki_ai_literature.csv')
# 绘制发表年份分布
visualizer.plot_year_distribution()
# 绘制被引量Top10
visualizer.plot_citation_top10()
# 生成关键词词云
visualizer.generate_keyword_wordcloud()
# 绘制发表来源Top10饼图
visualizer.plot_source_top10()
3.2 可视化结果解读
- 年份分布:可直观看到研究主题的时间发展趋势,判断该领域的研究热度变化;
- 被引量 Top10:快速识别该领域的高影响力文献,为核心文献研读提供方向;
- 关键词词云:核心关键词的字体大小反映出现频次,可快速定位研究热点;
- 发表来源分布:了解该领域文献的主要发表期刊 / 会议,辅助期刊选择和投稿决策。
四、注意事项与优化方向
4.1 合规性说明
知网数据受版权保护,本文提供的代码仅用于学术研究和个人学习,禁止用于商业用途。采集过程中应遵守知网的用户协议,控制采集频率,避免对服务器造成压力。
4.2 优化方向
- 反爬策略升级:可添加 IP 代理池(推荐亿牛云隧道转发)、随机请求间隔等,进一步降低被封禁风险;
- 数据维度扩展:可增加下载全文、提取参考文献、作者机构分析等功能;
- 可视化增强:可结合 Plotly 实现交互式可视化,提升数据探索体验;
- 效率优化:可采用多线程 / 异步方式提升采集速度,同时避免触发反爬机制。
总结
- 基于 Python 的 Selenium+BeautifulSoup 组合可有效解决知网文献批量采集问题,通过模拟浏览器操作规避反爬机制;
- Pandas+Matplotlib/WordCloud 可实现文献数据的多维度可视化分析,快速挖掘研究热点、高影响力文献等核心信息;
- 知网数据采集需遵守版权协议和网站规则,在学术研究范围内合理使用,同时可通过 IP 代理、随机间隔等方式优化采集策略。