在学术研究与文献综述工作中,知网(CNKI)作为国内最核心的学术文献数据库,其文献数据的采集与分析是研究工作的重要基础。手动逐条下载、整理文献信息不仅效率低下,也难以实现规模化的数据分析。本文将系统介绍如何基于 Python 实现知网文献的批量采集,并通过可视化手段对采集到的文献数据进行多维度分析,帮助研究者快速挖掘文献背后的研究趋势、关键词分布等核心信息。
一、技术原理与环境准备
1.1 核心技术栈说明
本次实现基于 Python 3.8 + 版本,核心依赖库包括:
- Selenium:模拟浏览器操作,解决知网动态加载和反爬机制问题
- BeautifulSoup4:解析 HTML 页面,提取文献核心信息
- Pandas:数据清洗与结构化存储
- Matplotlib/Seaborn:数据可视化展示
- WordCloud:生成关键词词云,直观呈现研究热点
1.2 环境配置
安装依赖包,同时需要下载对应浏览器的驱动(如 ChromeDriver),并配置系统环境变量,确保 Selenium 能正常调用浏览器。
二、知网文献批量采集实现
2.1 核心采集逻辑
知网文献采集的核心难点在于其反爬机制和动态页面加载特性。本文采用 Selenium 模拟真人浏览行为,通过以下步骤实现批量采集:
- 模拟登录知网(可选,部分文献需登录后查看)
- 构造搜索请求,定位文献列表页面
- 解析页面提取文献元数据(标题、作者、发表时间、关键词、摘要、被引量等)
- 分页遍历,批量存储数据
- 数据清洗,去除重复和无效记录
2.2 完整采集代码实现
python
运行
plain
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
运行
plain
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 代理、随机间隔等方式优化采集策略。