- 前言:基础的文件读写,掌握PDF文件读取与合并,Word文件转PDF文件。20200710
1 文件基础操作
文件包括 文本文件和二进制文件(声音,图像,视频)。 从存储方式来说,文件在磁盘上的存储方式都是二进制形式,所以,文本文件其实也应该算二进制文件。先从他们的区别来说,虽然都是二进制文件,但是二进制代表的意思不一样。打个比方,一个人,我们可以叫他的大名,也叫他的小名,但其实都是代表这个人。二进制读写是将内存里面的数据直接读写入文本中,而文本呢,则是将数据先转换成了字符串,再写入到文本中。
1.1 读取文件
要以读取文件的模式打开一个文件对象,使用Python内置的 open() 函数,传入文件名和标示符:
f = open('/Users/michael/test.txt', 'r')
标示符’r’表示读,这样,我们就成功地打开了一个文件。
如果文件不存在,open() 函数就会抛出一个 IOError 的错误,并且给出错误码和详细的信息告诉你文件不存在:
f=open('/Users/michael/notfound.txt', 'r')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory:
'/Users/michael/notfound.txt'
如果文件打开成功,接下来,调用 read() 方法可以一次读取文件的全部内容,Python把内容读到内存,用一个 str 对象表示:
f.read()
'Hello, world!'
最后一步是调用 close() 方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:
f.close()
由于文件读写时都有可能产生 IOError,一旦出错,后面的 f.close() 就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用 try … finally 来实现:
try:
f = open('/path/to/file', 'r')
print(f.read())
finally:
if f:
f.close()
但是每次都这么写实在太繁琐,所以,Python引入了 with 语句来自动帮我们调用 close() 方法:
with open('/path/to/file', 'r') as f:
print(f.read())
这和前面的 try … finally 是一样的,但是代码更佳简洁,并且不必调用 f.close() 方法。调用 read() 会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用 read(size) 方法,每次最多读取size个字节的内容。另外,调用 readline() 可以每次读取一行内容,调用 readlines() 一次读取所有内容并按行返回 list 。因此,要根据需要决定怎么调用。如果文件很小,read() 一次性读取最方便;如果不能确定文件大小,反复调用 read(size) 比较保险;如果是配置文件,调用 readlines() 最方便:
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
1.2 文件打开方式
模式 | 描述 |
---|---|
r | 以只读方式打开文件,文件的指针将会放在文件的开头,这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读,文件指针将会放在文件的开头。 |
r+ | 打开一个文件用于读写,文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写,文件指针将会放在文件的开头。 |
w | 打开一个文件只用于写入,如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
1.3 字符编码
要读取非UTF-8编码的文本文件,需要给 open() 函数传入 encoding 参数,例如,读取GBK编码的文件:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
'测试'
f.read()
遇到有些编码不规范的文件,你可能会遇到 UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open() 函数还接收一个 errors 参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
1.4 写文件
写文件和读文件是一样的,唯一区别是调用 open() 函数时,传入标识符 ‘w’ 或者 ‘wb’ 表示写文本文件或写二进制文件:
f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()
你可以反复调用 write() 来写入文件,但是务必要调用 f.close() 来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close() 方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用 close() 的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用 with 语句来得保险:
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')
要写入特定编码的文本文件,请给 open() 函数传入 encoding 参数,将字符串自动转换成指定编码。
注意:以 ‘w’ 模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入 ‘a’ 以追加(append)模式写入。
2 PDF基础操作
安装pdfplumber,PyPDF2,pypiwin32
pip install pdfplumber
pip install PyPDF2
pip install pypiwin32 # 该模块只能win系统使用
如果安装速度特别慢 或者安装最后出现time out的错误 可以使用国内镜像进行安装
pip install xxx -i 国内镜像
常用国内镜像如下
(1)阿里云 http://mirrors.aliyun.com/pypi/simple/
(2)豆瓣 http://pypi.douban.com/simple/
(3)清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
(4)中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
(5)华中科技大学 http://pypi.hustunique.com/
导入本次课所需的框架
from PyPDF2 import PdfFileReader, PdfFileWriter
import pdfplumber
import pandas as pd
import os
定义所需合并的PDF的路径和合并后文件的名称以及存放路径
dir_path = 'C:/Users/Administrator/Desktop/files'
# 此处路径一定要写绝对路径,相对路径会出现问题
new_name = 'all_pdf.pdf'
new_pdf_path = 'C:/Users/Administrator/Desktop/new_files'
pdf_path = os.path.join(dir_path, 'first.pdf')
2.1 读取PDF
2.1.1 读取PDF文字内容
# 创建pdf文件对象
pdf = pdfplumber.open(pdf_path)
# 获得所有的页数
pdf_pages = len(pdf.pages)
pdf_pages
# 获取指定第1页的文字内容
first_page = pdf.pages[0]
first_page
# extract_text()获取当前页面的文本值
first_page.extract_text()
for i in range(0, pdf_pages):
# 遍历当前所有的页面, 输出全部的文本值
print(pdf.pages[i].extract_text())
2.1.2 读取PDF表格内容
# 获取第1页的表格
pdf.pages[0].extract_table()
# 定义一个列表 存储该pdf种所有的表格
all_content = []
# 获取该PDF种的所有表格
for i in range(0, pdf_pages):
# 此处不使用extract_table(),因为如果当前页面没有表格extract_table()会报错
# extract_tables()返回的是[[每一行内容]]这种嵌套格式
# 如果直接添加会把每一个页面的内容作为一个元素添加到all_content,写入Excel时就会出现错乱的问题
# pdf.pages[i].extract_tables()
for j in pdf.pages[i].extract_tables():
# 在读取没有表格的页时,extract_tables()会返回一个带有空字符串的列表,所以我们要判断一下
for k in j:
if '' not in k:
all_content.append(k)
print(k)
# 转换为DataFrame, 方便存到Excel中
data = pd.DataFrame(all_content )
print(data)
data.to_excel('pdf_table.xlsx', index=False, header=None)
2.3 合并PDF
该功能需要使用到os模块获取文件路径以及pypdf2模块中的PdfFileWriter进行生成一个新的PDF和PdfFileReader读取需要合并的PDF
pdf_paths = []
# 定义一个列表来存储所有的pdf路径
for file_name in os.listdir(dir_path):
# 使用os模块获得dir_path路径下的所有文件
if '.pdf' in file_name:
# 判断是否是pdf文件,如果是就加到列表中
pdf_paths.append(os.path.join(dir_path, file_name))
new_pdf = PdfFileWriter()
# 记录pdf的总页数
numPages = 0
for pdfpath in pdf_paths:
# PdfFileReader()参数为文件路径/文件对象
# 如果该步骤出问题 多半是PDF文件有问题
old_pdf = PdfFileReader(open(pdfpath, 'rb'))
# 获取当前pdf的页数
pdfPages = old_pdf.getNumPages()
numPages += pdfPages
for i in range(old_pdf.getNumPages()):
# 根据当前页数一次添加到合并的PDF对象中
new_pdf.addPage(old_pdf.getPage(i))
with open(os.path.join(new_pdf_path, new_name), 'wb') as p:
new_pdf.write(p)
2.4 Word转PDF
# 本方法是直接使用win32com调用word中的接口进行转换为PDF,相当于用代码点击word中的转换为PDF
# 此种方式只适合win电脑, mac没有提供该模块的安装
from win32com.client import gencache
from win32com.client import constants, gencache
def word_Pdf(word_Path, pdf_Path):
# 调用word程序 并返回一个可执行的对象
word = gencache.EnsureDispatch('Word.Application')
docx = word.Documents.Open(word_Path, ReadOnly=1)
docx.ExportAsFixedFormat(pdf_Path,constants.wdExportFormatPDF,
Item=constants.wdExportDocumentWithMarkup,CreateBookmarks=constants.wdExportCrea
teHeadingBookmarks)
word.Quit(constants.wdDoNotSaveChanges)
word_Pdf("C:/Users/Administrator/Desktop/happy.docx"
,"C:/Users/Administrator/Desktop/res.pdf")
2.5 PDF添加水印
from PyPDF2 import PdfFileReader, PdfFileWriter
'''
PDF加水印就是将水印PDF和要加水印的PDF进行合并
'''
# 页的合并
pdf = PdfFileReader('C:/Users/Administrator/Desktop/files/first.pdf')
water_pdf = PdfFileReader('C:/Users/Administrator/Desktop/files/water.pdf')
# 准备要合并的两个页面
water = water_pdf.getPage(0)
for i in range(pdf.getNumPages()):
writer = PdfFileWriter()
# 使用页对象中的方法mergePage进行合并
pdf.getPage(i).mergePage(water)
#添加到新的PDF中
writer.addPage(pdf.getPage(i))
# 此处参数必须写文件对象
writer.write(open('newres.pdf', 'wb'))