当前位置: 首页 > 编程日记 > 正文

如何用Python批量提取PDF文本内容?

本文为你展示,如何用Python把许多PDF文件的文本内容批量提取出来,并且整理存储到数据框中,以便于后续的数据分析。

问题

最近,读者们在后台的留言,愈发五花八门了。

写了几篇关于自然语言处理的文章后,一种呼声渐强:

老师,pdf中的文本内容,有没有什么方便的方法提取出来呢?

我能体会到读者的心情。

我展示的例子中,文本数据都是直接可以读入数据框工具做处理的。它们可能来自开放数据集合、网站API,或者爬虫。

但是,有的时候,你会遇到需要处理指定格式数据的问题。

例如pdf。

许多的学术论文、研究报告,甚至是资料分享,都采用这种格式发布。

这时候,已经掌握了诸多自然语言分析工具的你,会颇有“拔剑四顾心茫然”的感觉——明明知道如何处理其中的文本信息,但就是隔着一个格式转换的问题,做不来。

怎么办?

办法自然是有的,例如专用工具、在线转换服务网站,甚至还可以手动复制粘贴嘛。

但是,咱们是看重效率的,对不对?

上述办法,有的需要在网上传输大量内容,花费时间较多,而且可能带来安全和隐私问题;有的需要专门花钱购买;有的干脆就不现实。

怎么办?

好消息是,Python就可以帮助你高效、快速地批量提取pdf文本内容,而且和数据整理分析工具无缝衔接,为你后续的分析处理做好基础服务工作。

本文给你详细展示这一过程。

想不想试试?

数据

为了更好地说明流程,我为你准备好了一个压缩包。

里面包括本教程的代码,以及我们要用到的数据。

请你到 这个网址 下载本教程配套的压缩包。

下载后解压,你会在生成的目录(下称“演示目录”)里面看到以下内容。

演示目录里面包含:

  • Pipfile: pipenv 配置文件,用来准备咱们变成需要用到的依赖包。后文会讲解使用方法;
  • pdf_extractor.py: 利用pdfminer.six编写的辅助函数。有了它你就可以直接调用pdfminer提供的pdf文本内容抽取功能,而不必考虑一大堆恼人的参数;
  • demo.ipynb: 已经为你写好的本教程 Python 源代码 (Jupyter Notebook格式)。

另外,演示目录中还包括了2个文件夹。

这两个文件夹里面,都是中文pdf文件,用来给你展示pdf内容抽取。它们都是我几年前发表的中文核心期刊论文。

这里做2点说明:

  1. 使用我自己的论文做示例,是因为我怕用别人的论文做文本抽取,会与论文作者及数据库运营商之间有知识产权的纠纷;
  2. 分成2个文件夹,是为了向你展示添加新的pdf文件时,抽取工具会如何处理。

pdf文件夹内容如下:

newpdf文件夹内容如下:

数据准备好了,下面我们来部署代码运行环境。

环境

要安装Python,比较省事的办法是装Anaconda套装。

请到 这个网址 下载Anaconda的最新版本。

请选择左侧的 Python 3.6 版本下载安装。

如果你需要具体的步骤指导,或者想知道Windows平台如何安装并运行Anaconda命令,请参考我为你准备的 视频教程 。

安装好Anaconda之后,打开终端,用cd命令进入演示目录

如果你不了解具体使用方法,也可以参考 视频教程 。

我们需要安装一些环境依赖包。

首先执行:

pip install pipenv
复制代码

这里安装的,是一个优秀的 Python 软件包管理工具 pipenv 。 安装后,请执行:

pipenv install --skip-lock
复制代码

pipenv 工具会依照Pipfile,自动为我们安装所需要的全部依赖软件包。

终端里面会有进度条,提示所需安装软件数量和实际进度。

装好后,根据提示我们执行:

pipenv shell
复制代码

这样,我们就进入本教程专属的虚拟运行环境了。

注意一定要执行下面这句:

python -m ipykernel install --user --name=py36
复制代码

只有这样,当前的Python环境才会作为核心(kernel)在系统中注册,并且命名为py36。

此处请确认你的电脑上已经安装了 Google Chrome 浏览器。

我们执行:

jupyter notebook
复制代码

默认浏览器(Google Chrome)会开启,并启动 Jupyter 笔记本界面:

你可以直接点击文件列表中的第一项ipynb文件,可以看到本教程的全部示例代码。

你可以一边看教程的讲解,一边依次执行这些代码。

但是,我建议的方法,是回到主界面下,新建一个新的空白 Python 3 笔记本(显示名称为 py36 的那个)。

请跟着教程,一个个字符输入相应的内容。这可以帮助你更为深刻地理解代码的含义,更高效地把技能内化。

当你在编写代码中遇到困难的时候,可以返回参照 demo.ipynb 文件。

准备工作结束,下面我们开始正式输入代码。

代码

首先,我们读入一些模块,以进行文件操作。

import glob
import os
复制代码

前文提到过,演示目录下,有两个文件夹,分别是pdf和newpdf。

我们指定 pdf 文件所在路径为其中的pdf文件夹。

pdf_path = "pdf/"
复制代码

我们希望获得所有 pdf 文件的路径。用glob,一条命令就能完成这个功能。

pdfs = glob.glob("{}/*.pdf".format(pdf_path))
复制代码

看看我们获得的 pdf 文件路径是否正确。

pdfs
复制代码
['pdf/复杂系统仿真的微博客虚假信息扩散模型研究.pdf','pdf/面向影子分析的社交媒体竞争情报搜集.pdf','pdf/面向人机协同的移动互联网政务门户探析.pdf']
复制代码

经验证。准确无误。

下面我们利用 pdfminer 来从 pdf 文件中抽取内容。我们需要从辅助 Python 文件 pdf_extractor.py 中读入函数 extract_pdf_content

from pdf_extractor import extract_pdf_content
复制代码

用这个函数,我们尝试从 pdf 文件列表中的第一篇里,抽取内容,并且把文本保存在 content 变量里。

content = extract_pdf_content(pdfs[0])
复制代码

我们看看 content 里都有什么:

content
复制代码

显然,内容抽取并不完美,页眉页脚等信息都混了进来。

不过,对于我们的许多文本分析用途来说,这无关紧要。

你会看到 content 的内容里面有许多的 \n,这是什么呢?

我们用 print 函数,来显示 content 的内容。

print(content)
复制代码

可以清楚看到,那些 \n 是换行符。

通过一个 pdf 文件的抽取测试,我们建立了信心。

下面,我们该建立辞典,批量抽取和存储内容了。

mydict = {}
复制代码

我们遍历 pdfs 列表,把文件名称(不包含目录)作为键值。这样,我们可以很容易看到,哪些pdf文件已经被抽取过了,哪些还没有抽取。

为了让这个过程更为清晰,我们让Python输出正在抽取的 pdf 文件名。

for pdf in pdfs:key = pdf.split('/')[-1]if not key in mydict:print("Extracting content from {} ...".format(pdf))mydict[key] = extract_pdf_content(pdf)
复制代码

抽取过程中,你会看到这些输出信息:

Extracting content from pdf/复杂系统仿真的微博客虚假信息扩散模型研究.pdf ...
Extracting content from pdf/面向影子分析的社交媒体竞争情报搜集.pdf ...
Extracting content from pdf/面向人机协同的移动互联网政务门户探析.pdf ...
复制代码

看看此时字典中的键值都有哪些:

mydict.keys()
复制代码
dict_keys(['复杂系统仿真的微博客虚假信息扩散模型研究.pdf', '面向影子分析的社交媒体竞争情报搜集.pdf', '面向人机协同的移动互联网政务门户探析.pdf'])
复制代码

一切正常。

下面我们调用pandas,把字典变成数据框,以利于分析。

import pandas as pd
复制代码

下面这条语句,就可以把字典转换成数据框了。注意后面的reset_index()把原先字典键值生成的索引也转换成了普通的列。

df = pd.DataFrame.from_dict(mydict, orient='index').reset_index()
复制代码

然后我们重新命名列,以便于后续使用。

df.columns = ["path", "content"]
复制代码

此时的数据框内容如下:

df
复制代码

可以看到,我们的数据框拥有了pdf文件信息和全部文本内容。这样你就可以使用关键词抽取、情感分析、相似度计算等等诸多分析工具了。

篇幅所限,我们这里只用一个字符数量统计的例子来展示基本分析功能。

我们让 Python 帮我们统计抽取内容的长度。

df["length"] = df.content.apply(lambda x: len(x))
复制代码

此时的数据框内容发生以下变化:

df
复制代码

多出的一列,就是 pdf 文本内容的字符数量。

为了在 Jupyter Notebook 里面正确展示绘图结果,我们需要使用以下语句:

%matplotlib inline
复制代码

下面,我们让 Pandas 把字符长度一列的信息用柱状图标示出来。为了显示的美观,我们设置了图片的长宽比例,并且把对应的pdf文件名称以倾斜45度来展示。

import matplotlib.pyplot as plt
plt.figure(figsize=(14, 6))
df.set_index('path').length.plot(kind='bar')
plt.xticks(rotation=45)
复制代码

可视化分析完成。

下面我们把刚才的分析流程整理成函数,以便于将来更方便地调用。

我们先整合pdf内容提取到字典的模块:

def get_mydict_from_pdf_path(mydict, pdf_path):pdfs = glob.glob("{}/*.pdf".format(pdf_path))for pdf in pdfs:key = pdf.split('/')[-1]if not key in mydict:print("Extracting content from {} ...".format(pdf))mydict[key] = extract_pdf_content(pdf)return mydict
复制代码

这里输入是已有词典和pdf文件夹路径。输出为新的词典。

你可能会纳闷为何还要输入“已有词典”。别着急,一会儿我用实际例子展示给你看。

下面这个函数非常直白——就是把词典转换成数据框。

def make_df_from_mydict(mydict):df = pd.DataFrame.from_dict(mydict, orient='index').reset_index()df.columns = ["path", "content"]return df
复制代码

最后一个函数,用于绘制统计出来的字符数量。

def draw_df(df):df["length"] = df.content.apply(lambda x: len(x))plt.figure(figsize=(14, 6))df.set_index('path').length.plot(kind='bar')plt.xticks(rotation=45)
复制代码

函数已经编好,下面我们来尝试一下。

还记得演示目录下有个子目录,叫做newpdf对吧?

我们把其中的2个pdf文件,移动到pdf目录下面。

这样pdf目录下面,就有了5个文件:

我们执行新整理出的3个函数。

首先输入已有的词典(注意此时里面已有3条记录),pdf文件夹路径没变化。输出是新的词典。

mydict = get_mydict_from_pdf_path(mydict, pdf_path)
复制代码
Extracting content from pdf/微博客 Twitter 的企业竞争情报搜集.pdf ...
Extracting content from pdf/移动社交媒体用户隐私保护对策研究.pdf ...
复制代码

注意这里的提示,原先的3个pdf文件没有被再次抽取,只有2个新pdf文件被抽取。

咱们这里一共只有5个文件,所以你直观上可能无法感受出显著的区别。

但是,假设你原先已经用几个小时,抽取了成百上千个pdf文件信息,结果你的老板又丢给你3个新的pdf文件……

如果你必须从头抽取信息,恐怕会很崩溃吧。

这时候,使用咱们的函数,你可以在1分钟之内把新的文件内容追加进去。

这差别,不小吧?

下面我们用新的词典,构建数据框。

df = make_df_from_mydict(mydict)
复制代码

我们绘制新的数据框里,pdf抽取文本字符数量。结果如下:

draw_df(df)
复制代码

至此,代码展示完毕。

小结

总结一下,本文为你介绍了以下知识点:

  • 如何用glob批量读取目录下指定格式的文件路径;
  • 如何用pdfminer从pdf文件中抽取文本信息;
  • 如何构建词典,存储与键值(本文中为文件名)对应的内容,并且避免重复处理数据;
  • 如何将词典数据结构轻松转换为Pandas数据框,以便于后续数据分析。
  • 如何用matplotlib和pandas自带的绘图函数轻松绘制柱状统计图形。

讨论

你之前做的数据分析工作中,遇到过需要从pdf文件抽取文本的任务吗?你是如何处理的?有没有更好的工具与方法?欢迎留言,把你的经验和思考分享给大家,我们一起交流讨论。

喜欢请点赞。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)。

如果你对数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

相关文章:

常用的3种注入方式

构造注入 指通过构造函数来传入具体类的对象设值注入(Setter注入) 指通过Setter方法来传入具体类的对象接口注入 指通过在接口中声明的业务方法传入具体类的对象

WebService客户端添加SOAPHeader信息

WebService客户端添加SOAPHeader信息 通过JAXBContext创建Marshaller对头信息进行解析为dom,获取WSBindingProvider,使用Headers.creat()创建soap的Header元素; 另外就是:将user转换为dom是为了不在有wsdl生成的LicenceInfo类中增…

MATLAB 向量

MATLAB 向量: 1、MATLAB 行向量: 创建行向量括在方括号中的元素的集合,用空格或逗号分隔的元素。 2、MATLAB 列向量: 创建列向量括在方括号中的元素的集合,使用分号来分隔的元素。 3、

Wiki为什么会流行

我来开题,老段补充一下,嘿嘿。。。Wiki的优点:版本管理和版本比较多作者,多编辑的协作简洁Wiki的缺点(或者第一次用不是很习惯的地方): 非所见即所得需要学习一下简单的语法文章的分类好像不是很地道所有的链接基本是自…

hung-yi lee_p18_图神经网络(cont.)

文章目录1. 简介2. 怎么把图喂到神经网络里面呢3. 为什么需要GNN4. 训练GNN遇到的问题5. 后面的内容安排6. GNN要做的事,常用数据集和基准7. 第一种实现GNN的方法(Spatial-based GNN)7.1 NN4G7.2 DCNN7.3 DGC7.4 MoNET番外:聚集的几种方法7.5 GAT7.5 GIN…

SQL Server2008附加数据库之后显示为只读

SQL Server2008附加数据库之后显示为只读时解决方法 啰嗦的话就不多说了,直入主题吧! 方案一: 碰到这中情况一般是使用的sa账户登录的,只要改为Windows身份验证,再附加数据库即可搞定。 方案二: 使用sa登录…

java maven项目使用sonar审核代码

为什么80%的码农都做不了架构师&#xff1f;>>> 一、pom增加插件 <plugin><groupId>org.sonarsource.scanner.maven</groupId><artifactId>sonar-maven-plugin</artifactId><version>3.1.1</version> </plugin> 二…

ERP成功全球实施十大成功案例

、美铝公司&#xff08;Alcoa&#xff09; 公司简介 美铝公司创办于19世纪80年代中期&#xff0c;目前是世界最大的氧化铝、电解铝和铝加工产品的生产商&#xff0c;活跃于包括基础研究和开发、技术及回收利用等铝工业的所有主要领域。美铝产品应用于航空航天、汽车、包装、建筑…

CentOS安装Oracle全过程

1.准备工作 &#xff08;1&#xff09;安装Java环境 &#xff08;2&#xff09;增大SWAP空间 1.1 切换为root用户 1.2 dd if/dev/zero of/home/swap bs1024 count204800 1.3 /sbin/mkswap /home/swap 1.4 /sbin/swapon /home/swap 1.5 free -m 1.6 vi /etc/fstab 添加 /home/…

Hide the common top menu in Ubuntu 12.04

隐藏&#xff1a;1、sudo apt-get autoremove appmenu-gtk appmenu-gtk3 appmenu-qt2、reboot恢复&#xff1a;1、sudo apt-get install appmenu-gtk appmenu-gtk3 appmenu-qt2、reboot转载于:https://www.cnblogs.com/wiessharling/p/3569616.html

在IBatisNet中使用存储过程

其实在IBatisNet中使用存储过程应该很简单了&#xff0c;应为IBatisNet本来就是基于Sql Mapping的。想着Npetshop中应该有例子看一下就行了&#xff0c;可是查了查map文件&#xff0c;发现没有&#xff0c;只要自己动手搞搞了。 在建立的测试数据库中建立一个简单的存储过程del…

人工智能时代,教育如何做人工智能的“弄潮儿”?

汇新杯新兴科技互联网创新大赛报名火热进行中 汇新杯大赛报名入口&#xff1a;https://www.chuangcheng.org.cn/4552016年人工智能火了&#xff0c;它被行业公认为是继互联网、移动互联网之后的又一重大机遇和挑战&#xff0c;并将成为各个领域的“水电煤”&#xff0c;成为行业…

centOS安装Java环境全过程

1.通过ssh将安装包从本地传到/usr/local文件夹下 2.解压安装包 tar -zxvf /usr/local/jdk-**** 3.给解压后的文件夹改名 mv /usr/local/jdk1.8.0_271 /usr/local/java 4.修改配置文件&#xff0c;配置环境变量 vi /etc/profile export JAVA_HOME/usr/local/java export PAT…

vue组件的传参练习

为什么80%的码农都做不了架构师&#xff1f;>>> 首先是父组件与子组件沟通 父组件告诉子组件&#xff0c;“嘿&#xff0c;孩子&#xff0c;我有话和你说” 组件A代码 <template><section><h1>这是组件</h1><test-B :chile-name"u…

Avayaの初体验

这个题目是不是给了你一种无法抗拒的吸引力并引起了你无限的幻想呢&#xff1f;一个名为Avaya的清纯少女……Stop&#xff01;今天我要说的是电话交换机。说来惭愧&#xff0c;本来是个学通信的&#xff0c;到后来却不务正业搞了软件&#xff0c;到现在突然组织又需要我去搞Ava…

Git fetch和git pull的区别

原文&#xff1a;http://www.tech126.com/git-fetch-pull/ Git中从远程的分支获取最新的版本到本地有这样2个命令&#xff1a;1. git fetch&#xff1a;相当于是从远程获取最新版本到本地&#xff0c;不会自动merge git fetch origin master git log -p master..origin/mas…

powerdesigner中类图的内部类画法

点击一个已有类 点击1看到2 选择3 输入类名 右键类名选择Properties设置内部类的方法和参数 结果 如果在第二步里面找不到inner classfiers怎么办 自己添上

js获取页面属性

一些常用的页面属性获取&#xff1a; 网页可见区域宽&#xff1a;document.body.clientWidth;网页可见区域高&#xff1a;document.body.clientHeight; 类似视口宽度 网页正文全文宽&#xff1a;document.body.scrollWidth;网页正文全文高&#xff1a;document.body.scrollHeig…

jquery-12 折叠面板如何实现(两种方法)

jquery-12 折叠面板如何实现&#xff08;两种方法&#xff09; 一、总结 一句话总结&#xff1a;1、根据点击次数来判断显示还是隐藏&#xff0c;用data方法保证每个元素一个点击次数&#xff1b;2、找到元素的下一个&#xff0c;然后toggle实现显示隐藏。 1、toggle的两种用法…

上传图片并生成缩略图

前台<form id"Form1" method"post" runat"server" enctype"multipart/form-data"> <table id"Table1" cellpadding"1" cellspacing"1" width568 border"1"> …

物联网与互联网的6大区别

物联网互联网通过自动方式获取数据通过人工方式获取数据物联网是虚拟与现实的结合互联网构造了网络虚拟世界物联网是将计算机"装到"一切事务中在互联网时代把一切交给计算机去做物联网提供行业性服务互联网提供全球性公共信息服务物联网实现了信息世界与物理世界的融…

centoros 环境安装

1. nginx rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm yun install nignx 2.php yum install php yum install php-fpm 3. java yum list java* yum install java-1.7.0-openjdk.x86_64 转载于:https://www.cnblogs…

最优保险(三公子)

最优保险&#xff08;三公子&#xff09; 2018-06-29 1、消费型重疾险文章《6款消费型重疾险对比分析》、对标康惠保的一款良心产品 2、少儿重疾险文章《2款少儿重疾险对比分析》 3、定期寿险文章《4款定期寿险的对比分析》 4、医疗险文章《5款百万医疗险的对比分析》 5、意外险…

Love Java , Love IBM , Love Sun ( SunJiHai )

找java资源,找javabean,找spring,找ajax.东找西找,最后发现还是IBM牛!什么都有(Sun 当然也有),文档又好看,不得不佩服一下了.努力!!这个是IBM的DW上的JavaBean的入门:http://www-128.ibm.com/developerworks/cn/java/ejbIntro/ Sun里面的J2EE 5 的Tutorial(现在不叫J2EE了,叫 J…

RFID系统的基本工作原理

文章目录1. RFID的定义2. RFID与条码技术相比有哪些优势3. RFID标签的分类4. RFID系统的基本工作原理4.1 RFID系统的组成4.2 RFID标签的组成4.3 RFID读写器的组成4.4 RFID天线4.5 RFID基本交互原理4.6 被动式RFID标签工作原理4.7 主动式RFID标签工作原理4.8 半主动RFID标签工作…

对WEB前端的几段思考(一)——界面设计和性能优化(整理中)

尽管我并非艺术出生&#xff0c;既没有任何设计基础&#xff0c;又没有较高艺术涵养&#xff0c;也深谙在短时间内创造一定艺术造诣并非易事&#xff0c;但是既然当初选择从事网站前端开发&#xff0c;我的目光不能仅停留在前端代码上。作为一名志向在前端领域发展的人员&#…

CS研究笔记-缓存 (转)

CS中缓存对性能的优化起了非常大的作用&#xff0c;今天做一次深入的研究。经过大致的代码浏览发现CS中的缓存分为2种&#xff1a;一种采用System.Web.Caching&#xff0c;另一种采用HttpContext.Items&#xff08;由于CS大量的采用服务器端控件没有使用页面级的缓存&#xff0…

阿里云弹性计算-图形工作站(公测)发布

产品介绍&#xff1a; 阿里云图形工作站&#xff0c;基于GPU 实例&#xff0c;采用AMD 专业GPU&#xff0c;集成了高性能远程桌面功能&#xff0c;非线编软件以及数据存储系统在内的一套完整图形图像处理流程&#xff0c;旨在满足一些高端用户在使用阿里云GPU可视计算实例时的极…

软件测试:黑盒白盒与动态静态之间有必然联系吗

区分黑白盒&#xff1a;看有没有查看源码 区分动静态&#xff1a;看有没有运行程序 情况类型运行程序&#xff0c;只看输入输出动态黑盒运行程序&#xff0c;分析代码结构动态白盒不运行程序&#xff0c;只查看界面静态黑盒不运行程序&#xff0c;查看代码静态白盒

最短路径 - dijkstra

dijkstra是单源点最短路算法。 借图&#xff1a; 其基本思想是&#xff0c;设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。 初始时&#xff0c;S中仅含有源。设u是G的某一个顶点&#xff0c;把从源到u且中间只经过…