这个学期如期开课了,虽然是在家里。这学期我导开了一门《高等教育管理专题研究》,一口气给了11个专题。为了对这11个专题的文献分布情况有一个粗略的印象,我觉得都得找相关的文献来看看,但是11个专题都要重新检索一遍,重复性工作让人头秃……于是,我写了个python脚本,自动生成各个主题的关键词和引文分布情况的报告,效果如下图。
看到点击“运行”不到3秒钟就自动生成了6000字左右“像模像样”的报告,不禁流下激动的泪水。
要是有一天能写个程序能将我的想法整理成论文就好了[狗头]。为了留存这个美好的幻想,我把报告生成的思路记录下来,权当为以后积累点技术基础。编程思路包括:提取PDF内容、CSSCI文献题录数据的格式处理、关键词词频计算、引文数据统计、关键词共被引网络生成、word的自动编写与插入(包括样式、表格等)。
一、报告结构的拟定
写报告的思路比较固定,我个人的体会是:叙述要求、描述数据和展示结果。那么每个一级标题下,先附上老师的要求,然后描述数据在哪里下载的,最后将结果数据的图表展示出来。按照这个思路走下来了,将每个部分的模板编写为如下这样。
确定了报告框架之后,下面的事情便变得简单,把内容填上去,在写个循环语句,报告就完成了,下面说的都是如何填空的事情。
二、PDF内容提取
我导给了一个PDF,需要把PDF的一级标题和二级标题的内容提取出来,填充themeName和themeKeyword部分。
在python中,提取PDF内容的包有pdfminer、tabula、pdfplumber等(引自:Python:解析PDF文本及表格——pdfminer、tabula、pdfplumber 的用法及对比),都大同小异。经过对比选择了pdfplumber,没安装的话需要先安装一下。
pip install pdfplumber
主要代码如下。
pdfPath = 'Auto_data/course_text/2019-2020春季学期《高等教育管理专题研究》拟研究专题.pdf'
pdf = pdfplumber.open(pdfPath)
for p in pdf.pages:
test = p.extract_text().split('\n')
通过上述代码就可以提取PDF的所有内容。但是想要正确提取内容还有其他的工作,使用正则表达式根据标题的特点提取一级和二级标题内容、编程将全角标点变成半角标点、去掉标题中的中文和英文标点等、将一级标题和二级标题内容建立对应关系等。非主干代码占了所有代码量的80%以上,真特么的符合“二八法则”。
三、文献题录数据下载与提取
在一个主题词下载之前,先确定该主题的文献检索方式。把下图的检索方式使用代码进行整理,便可以得到searchWord和searchWay。
themeKeyword = '、'.join(themes_keypoints[ii])
searchWord, searchWay = search_type(ii, searchWord_group)
def wordM(word):
word = "“" + word + "”"
return word
def search_type(i, searchWord_group):
word_group = searchWord_group[i]
slen = len(word_group)
if (slen > 1):
endLen = slen - 1
searchWay = ',检索方式为:'+ word_group[endLen]
searchWord = '、'.join(word_group[:endLen])
else:
searchWord = word_group[0]
searchWay = ''
return searchWord, searchWay
随后,在CSSCI官网,下载文献题录数据。
下载下来的数据是这样的。
对了,原来以为有数据规模会很大(并不大),所以使用如下代码生成了一个Windows批处理文件,可以达到一键生成文件夹目录的效果。
file_name = 'Auto_data/lite_data/themes_list.bat'
with open(file_name, "w") as f:
index = 0
for i in range(len(themes_group)):
index = int(index) + 1
if index < 10: # 记得加0,不然不按顺序
index = '0' + str(index)
else:
index = str(index)
contant = 'md '+ index + '_' + themes_group[i] + ' '
f.writelines(contant)
f.writelines('\n')
在下载得到数据后,需要对数据信息进行提取,提取的信息包括:标题、第一作者、关键词、发表时间、参考文献。标识符如下。
matching_columns = ['【来源篇名】','【第一作者】','【关 键 词】','【年代卷期】','【参考文献】']
separator = '-----------------------------------------------------------------------'
使用正则表达式匹配相关信息,使用.replace函数去掉标识符,再进行其他操作,将信息放入字典中,便完成了信息提取工作。核心代码如下:
for i in range(len(matching_columns)):
m = matching_columns[i]
isMatching = re.match(m, curData) #把标题、作者等信息放入字典中
if isMatching:
if (i == 0):
data_dict['article'] = curData.replace(m,'')
elif (i == 1):
data_dict['author'] = curData.replace(m,'')
elif (i == 2):
data_dict['keyword'] = curData.replace(m,'')
elif (i == 3):
data_dict['year'] = curData.replace(m,'').split(',')[0]
elif (i == 4):
isReference = 1
isSeparator = re.match(separator, curData)
在完成信息提取之后,生成了这一个主题的题录信息字典data_dict,通过len(data_dict)可以知道这一个主题一共有多少条文献题录数据。
searchNum = len(data_dict)
四、关键词与引文数据统计
关键词的词频计算使用的是类似Excel的数据透视表的方法写成的,这一点在《如何用python表白》一文中已经讲到,计算函数如下。其中,words_count.head(10)是输出到word中的排名前10的关键词,len(set(all_words))是关键词的数量,对应word中的词频分布表和关键词个数keywordNum。
def all_words(df):
all_words = []
for w in df['keyword']:
w_group = w.split('/')
all_words.extend(w_group)
print('关键词次数:',len(all_words))
print('关键词个数:',len(set(all_words))) #使用set方法
print('\n')
df_word = pd.DataFrame({'word': all_words})
words_count = df_word.groupby(by=['word'])['word'].count()
words_count = words_count.to_frame()
words_count.columns = ['count']
words_count = words_count.sort_values(by='count', ascending=False)
print(words_count.head(10))
return all_words, words_count, len(set(all_words))
引文数据的提取编程思路类似, references_count.head(10)是输出到word中的被引量排名前10的参考文献。
def all_references(df):
all_references = []
for r in df['reference']:
r_group = r.split('/')
all_references.extend(r_group)
print('\n')
print('引用文献次数:',len(all_references))
print('引用文献篇数:',len(set(all_references))) #使用set方法
df_references = pd.DataFrame({'reference': all_references})
references_count = df_references.groupby(by=['reference'])['reference'].count()
references_count = references_count.to_frame()
references_count.columns = ['count']
references_count = references_count.sort_values(by='count', ascending=False)
for index in list(references_count.index):
if not isRef(index):
references_count.drop(index)
print(references_count.head(10))
return references_count
五、关键词共现网络的生成
由于需要分析,关键词共现网络以截图的方式放入报告中,有一些结果还是很好的,可以说很好看了,比如下面这张,很容易给像我这样的博士研究生以思考的启发,比如可以这么说:“创新创业教育的生长,根基在于创新创业活动的开展,课程体系是连接两者的关键因素。此外,创新创业教育还涉及教学改革、教学组织模式、高校人才培养、教学策略的运用等多方面的内容……”
其他的也有好看的,比如“产教融合”的图,中心词、核心词和外围词都十分完整,十分便于分析。
六、word的自动编写
当我们所有的内容都准备好之后,就可以考虑word的自动生成了。我使用的模块是python-docx,没安装的话需要事先安装一下。
pip install python-docx
安装完成后,导入自己已经调好样式的空白word页面。
document = Document('Auto_data/result/input.docx')
先将我们的内容以字符串的形式存储在几个变量中。
first_para = '专题下的思考方向包括:'+ themeKeyword + '。'
second_para = '使用CSSCI进行检索,检索词为:'+ str(searchWord) + str(searchWay) + '。共下载文献题录数据' + str(searchNum) + '条,所有文献一共涉及关键词' + str(keywordNum) + '个,词频分布如下。'
third_para = '关键词共现网络如下。'
fourth_para = '被引用次数排名前10的文献如下。'
使用document.add_paragraph方法就可以将我们的内容以段落的形式插入word中,style可以选择样式,例如'Heading 2'是指二级标题样式,'Normal'是指正文样式。
t1 = document.add_paragraph(themeName, style = 'Heading 2')
p1 = document.add_paragraph(first_para, style = 'Normal')
p2 = document.add_paragraph(second_para, style = 'Normal')
表格的插入比较麻烦。先使用document.add_table生成题头。然后使用.add_row().cells方法增加行,使用for循环进行对表格单元格进行赋值,完成表格的生成。
table1 = document.add_table(rows=1, cols=4, style='网格型1')
hdr_cells = table1.rows[0].cells
hdr_cells[0].text = '关键词'
hdr_cells[1].text = '词频'
hdr_cells[2].text = '关键词'
hdr_cells[3].text = '词频'
table_size = int(maxKeyword/2)
for i in range(table_size):
another_i = i + table_size
row_cells = table1.add_row().cells
row_cells[0].text = t1_data.index[i]
row_cells[1].text = str(t1_data['count'][i])
row_cells[2].text = t1_data.index[another_i]
row_cells[3].text = str(t1_data['count'][another_i])
之后,使用for循环,将11个专题的内容都放入word中,使用document.save保存word,这样,我们6000字的报告就生成啦!
document.save('Auto_data/result/11个专题下的文献计量分析.docx')
写在后面
在思考编程过程的时候,决定这并不是什么难事,下载数据、提取信息、生成word就完事了。
但是……想法一时爽,编程编断手,虽然报告的生成仅仅需要不到3秒钟,但是累计编程时间超过9个小时,中间涉及正则表达式、字符的unicode编码等新知识点,以及许多大大小小的bug,太占用专业学习的时间。这种报告只是信息的简单叠加,并不具备自主分析的效果,论文还是得自己写[微笑]。能实现一个想法总还是一件开心的事情,程序最大的特点就是复用性,以后就当做写文献综述前的预处理流程吧。大家如果对报告或者源码感兴趣,欢迎在后台留言与我交流哇!