【python实战】获取英文文献pdf中参考文献信息
在看英文文献的时候,有时候想要这篇文献的参考文献的情况,比如,年份分布,作者情况等。
尤其是,当想通过一篇综述或者元分析文献来了解某个领域的情况的时候,根据该文献的参考文献的信息来了解也不失为一种方式。
一般下载的文献都是pdf格式,如果直接复制粘贴来进行整理的话,费时费力,毕竟调整格式、挑选信息都需要花费不少时间。
这时候,就想着能不能直接根据一篇文献pdf,自动获取其中的参考文献并进行整理。网上搜索半天也没有找到直接解决办法,就想着用python自己来试一试。
例如,某文献的参考文献(APA格式)如下所示:
get_paper2.png
最后,将参考文献逐条导出txt:
get_paper3.png以及,逐条导出excel:
get_paper4.png一、思路
整个过程用python实现的,适用于APA格式的参考文献。
思路主要是:
- 获取References下的参考文献的文本内容;
- 对文本内容解析成单条参考文献;
- 从每条参考文献中提取信息;
- 导出结果txt和excel;
二、过程
1. 获取References下的参考文献的文本内容
这部分主要涉及到用python来提取PDF的文本内容。
思路:
(1)提取References所在页面及之后页面的文本内容;
(2)从中提取参考文献的文本内容。
因为不少的期刊文献pdf是两栏排版(如上述例子所示)。
所以,花了一些时间,尝试了几种不同的包的提取效果。其中,
- pdfplumber:pdf内容分栏的话,文本是横向提取出来的,这样的话,完全不知道哪条是哪条文献了……所以,不考虑。
- pdf2htmlEX:想着如果能把pdf转成html再解析会不会更方便些。所以,(1)安装pdf2htmlEX;(2)通过python调用pdf2htmlEX,把相应页面的pdf转为html;(3)再将html中的文字提取出来。结果:发现依然有个问题,文本是可以分栏提取出来,但是,文本被切得细碎,最重要的是换行没有换行符,这样也无法去分割提取信息。所以,也不考虑。
-
pdfminer:想尝试这个包提取效果,但是,始终出现错误:
TypeError: __init__() missing 1 required positional argument: 'parser'
,研究了半天也不知道怎么解决,而且,代码和过程更复杂些,就没继续下去了。 -
pypdf2:以最后一页为例,如图所示,提取出来的文本没有空格!!!更不会考虑了,这咋提取信息……
就在快要失去信心的时候发现,PyMuPDF可以用!
import re
# PyMuPDF
import fitz
import pandas as pd
(1)获取参考文献开始及之后的页面
首先,根据关键词,即标题References(或者REFERENCES,有的居然还是referenCes)获取参考文献开始及之后的页面。
# 获取从references开始及之后的页面内容
def GetRefPages(pdfname):
pdf=fitz.open(pdfname)
pagenum=len(pdf)
ref_list=[]
for num,p in enumerate(pdf):
content=p.getText('blocks')
# print(num)
for pc in content:
# print(pc)
txtblocks=list(pc[4:-2])
txt=''.join(txtblocks)
if 'References' in txt or 'REFERENCES' in txt or 'referenCes' in txt:
refpagenum=[i for i in range(num,pagenum)]
for rpn in refpagenum:
refpage=pdf[rpn]
refcontent=refpage.getText('blocks')
for n,refc in enumerate(refcontent):
txtblocks=list(refc[4:-2])
ref_list.extend(txtblocks)
print(''.join(ref_list))
return ref_list
结果如下图所示,获取了上述例子中那两页文献内容:
pymupdf1.png(2)获取References后的文本内容
接着,就是将上述列表中,关键词,即标题References(或者REFERENCES,有的居然还是referenCes)之后的文本提取出来。
# 获取从references之后的文本内容
def GetRefTxt(ref_list):
refnum=0
for nref,ref in enumerate(ref_list):
if 'References' in ref or 'REFERENCES' in ref or 'referenCes' in ref:
refnum=nref
references_list=ref_list[refnum+1:]
print(''.join(references_list))
return references_list
结果如下,获取了所有的参考文献文本内容:
pymupdf2.png2.对文本内容解析成单条参考文献
在获得了上述所有参考文献的文本内容之后,就需要提取出每条参考文献了。
思路主要是:
(1)将所有文本变成一个字符串;
(2)通过正则表达式拆分;
(3)合并成一条参考文献;
def GetUnitRef(references_list):
# 去除一些不必要的字符
references_list=[i.replace('\n',' ') for i in references_list]
references_list=[re.sub(r'<.*>','',i) for i in references_list]
# 将所有文本变成一个字符串
references=' '.join(references_list).replace('- ','')
references=re.sub(r'\([0-9]{1,3}\)','',references).replace(' ','')
# print(references)
# 根据 作者( 通过正则表达式进行拆分
# 特殊:Taubman Ben-Ari, O.
# 特殊:(in press)
authorspattern=re.compile(r'([A-Za-z]+ ?[A-Za-z]* ?[A-Za-z]*-?[A-Za-z]*, [A-Z]\..*?\([0-9|a-zA-Z])')
reflist=re.split(authorspattern,references)
reflist=list(filter(None,reflist))
# print(reflist)
# 将参考文献导出(略有瑕疵)
allref_list=[]
# 拆分后每2个拼成1条文献,若遇到页眉,则跳过
reflength=len(reflist)
step=2
for i in range(0,reflength,step):
# 若遇到页眉则跳过(页眉的第2条没有.,等符号)
if '.' in reflist[i+1] or '). ' in reflist[i+1]:
unitref=reflist[i:i+step]
unit=''.join(unitref)
allref_list.append(unit)
# 处理书籍参考文献(先前半段和后半段合并进入列表,再删除前半段)
mid_list=[]
for n,a in enumerate(allref_list):
if 'Ed' in a and re.search(r'\([0-9]+\)',a)==None:
mid_list.append(allref_list[n-1]+allref_list[n])
else:
mid_list.append(a)
final_list=[]
for num,i in enumerate(mid_list[:-1]):
if i not in mid_list[num+1]:
final_list.append(i)
final_list.append(mid_list[-1])
return final_list
(1)拼成一个字符串
这个比较好弄,结果如下:
pymupdf3.png(2)根据正则表达式拆分
但是,通过正则表达式拆分和合并的过程,花了不少时间,尝试了几个不同的期刊文献,写出比较通用的表达r'([A-Za-z]+ ?[A-Za-z]* ?[A-Za-z]*-?[A-Za-z]*, [A-Z]\..*?\([0-9|a-zA-Z])'
,这里的正则表达式是适用于APA格式的。
根据作者(进行拆分后的结果:
for n,r in enumerate(reflist):
print(n,r)
pymupdf4.png
(3)合并成一条参考文献
将每两条文献进行合并,如果遇到页眉就跳过,结果:
for n,r in enumerate(allref_list):
print(n,r)
pymupdf5.png
至此,大部分工作就做完了,但是,有的时候,参考文献中还有书籍参考文献。
以另一篇文献中的某条参考文献为例:
Leary, M. R., & Guadagno, J. (2011). The role of hypo-egoic self processes in optimal functioning and subjective well-being. In K. Sheldon, T. B. Kashdan, & M. F. Steger (Eds.), Designing positive psychology: Taking stock and moving forward (pp. 135-146). New York, NY: Oxford University Press. doi:10.1093/acprof:oso/9780195373585.003 .0009
根据正则表达式会拆分成如下,因为Sheldon, T. B. Kashdan, & M. F. Steger (E
也符合正则表达式要求。
Leary, M. R., & Guadagno, J. (2
011). The role of hypo-egoic self processes in optimal functioning and subjective well-being. In K.
Sheldon, T. B. Kashdan, & M. F. Steger (E
ds.), Designing positive psychology: Taking stock and moving forward (pp. 135-146). New York, NY: Oxford University Press. doi:10.1093/acprof:oso/9780195373585.003 .0009
所以,如果遇到这种情况,就做了些处理,这条文献就分来合并,然后,再将前半段加入到后半段,再删除前半段。最后得到结果:
for n,r in enumerate(final_list):
print(n,r)
pymupdf6.png
3. 从每条参考文献中提取信息
有了每条参考文献,就可以根据正则表达式,拆分提取等方式提取作者、年份、标题、期刊、DOI等信息了。
虽然不是十分完美,但绝大部分还是没问题的。
def GetInfo(final_list):
referencelist=[]
authorlist,yearlist,titlelist,journallist=[],[],[],[]
doilist=[]
for f in final_list:
# 全文
referencelist.append(f)
# print(f)
# 作者
try:
author=re.findall(r'([A-Za-z]+ ?[A-Za-z]* ?[A-Za-z]*-?[A-Za-z]*, [A-Z]\..*?\()',f)[0].replace('(','')
except:
author='Null'
authorlist.append(author)
# print(len(author),author)
# 年份
try:
year=re.findall(r'(\([0-9|a-z| ]+\))',f)[0].replace('(','').replace(')','')
except:
year='Null'
yearlist.append(year)
# print(len(year),year)
# 标题
try:
title=f.split('). ')[1].replace('?','.').split('.')[0]
except:
title='Null'
titlelist.append(title)
# print(len(title),title)
# 期刊
try:
# journal=f.split('). ')[1].split('.')[1].split(',')[0]
journal=''.join(re.findall(r'.*?\)\. .*?[\.|?] (.*[,|.]?)',f)).split(',')[0]
except:
journal='Null'
journallist.append(journal)
# print(len(journal),journal)
# DOI
try:
if 'doi.org' in f :
DOI=f.split('org/')[1]
elif 'doi:' in f:
DOI=f.split('doi:')[1]
else:
DOI='Null'
except:
DOI='Null'
doilist.append(DOI)
# print('DOI:',DOI)
# 转为数据框
refdata={
'Author':authorlist,
'Year':yearlist,
'Title':titlelist,
'Journal':journallist,
'DOI':doilist,
'Reference':referencelist
}
refdata=pd.DataFrame(refdata)
print(refdata)
return refdata
结果如下:
pymupdf7.png
4.导出结果txt和excel
最后一步就很简单了,将提取出的每条参考文献文本导入txt,以及相关信息写入excel里。
最后就得到最开始例子的txt和excel内容啦~
# 将参考文献文本写入txt
def write2txt(path,filename,final_list):
txtname=path+'\\'+'[Refs of]'+filename+'.txt'
with open(txtname,"w",encoding='utf-8') as f:
txtcontent='\n'.join(final_list)
f.write('<<'+filename+'>>'+'\n'+txtcontent)
# 将参考文献信息写入excel
def refinfo2excel(path,filename,refdata):
excelname=path+'\\'+'[Refs of]'+filename+'.xlsx'
refdata.to_excel(excelname,index=0)