Python实现PDF转TXT | Word count: 1.9k | Reading time: 7min | Post View:
Python 实现 PDF 转 TXT
用手机或者 Kindle 看 PDF 文档字实太是太小了,总觉得 PDF 转 TXT
是个刚需,却一直没找到 PDF 转 TXT 的简单方法,最近有空,不妨自己用
Python 写一个。
将 PDF 格式转换成纯文本的
TXT,虽然会损失掉一些排版和图片,却可以给文件瘦身,也可将其中的文字用于更多场合。
PDF
里一般都包含文字和图片,有些文字以图片形式存储,比如大多数以扫描方式制作的
PDF 图书都使用这种方式,以此方式存储的 PDF
文件占空间也比较大,一般都有几十兆。另一种,以文本方式存储字符,同时存储字符的大小和位置,在显示对应的页面时绘制图片和文字,以此方式存储的
PDF 文件占空间较小,一般只有几兆大小。
分辨文字的存储方式很简单,只需要用任意支持 PDF
格式的软件打开文件,放大几倍,如果文字依然清晰,则是以字符方式存储的,如果字的边缘变得模糊,则一般是以图片方式存储文字。
以字符方式存储 PDF 的文本比较容易获取,使用 Linux 下的 pdftotxt
命令即可过滤出其中的文字。以图片方式存储的相对比较复杂,需要识别图片中的文字,会用到
OCR 技术,OCR 是光学字符识别的简称,目前的 OCR
一般利用深度学习技术实现,同样也是训练模型比较困难,但单纯地使用模型则非常简单。
本文使用现成的 Python 三方库,实现对 PDF
中文本和图片两种文字的识别,程序运行环境仍然是
Linux(主要因为笔者不怎么用 Windows),Python 版本为 3.6(与 Python 2.7
的三方库略有差异)。
安装软件
程序主要包括解析 PDF 格式和 OCR 识别两部分,首先安装三方库:
1 2 3 4 5 $ sudo pip install pdfminer3k # PDF格式解析 $ sudo apt-get install tesseract-ocr # 离线OCR工具tesseract $ sudo apt-get install tesseract-ocr-chi-sim # OCR简体中文支持 $ sudo pip install pytesseract # OCR工具的Python支持包 $ sudo pip install baidu-aip # 在线OCR:百度提供的字符识别工具。
本例中使用了在线和离线两种
OCR,离线版本识别率稍差,在线版本是百度提供的字符识别服务,对中文识别效果更好,它提供一定的免费额度,但是使用量大时需要付费。
离线 OCR
使用 OCR 的目的是识别图片中的文字,Tesseract 是一款由 HP
实验室开发,由 Google 维护的开源
OCR,支持多种文字,下面看看它的用法。
1 2 3 4 5 6 7 from PIL import Image import pytesseract def img_to_str_tesseract(image_path, lang='eng'): return pytesseract.image_to_string(Image.open(image_path), lang) print(img_to_str_tesseract('image/test1.png', lang='chi_sim'))
在线 OCR
百度、搜狗、有道等智能云平台都提供在线 OCR
服务,使用方法也大同小异,下面介绍百度 OCR 的使用方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from aip import AipOcr config = { 'appId': '', 'apiKey': '', 'secretKey': '' } client = AipOcr(**config) def img_to_str_baidu(image_path): with open(image_path, 'rb') as fp: image = fp.read() result = client.basicGeneral(image) if 'words_result' in result: return '\n'.join([w['words'] for w in result['words_result']]) return "" print(img_to_str_baidu('image/test1.png'))
其中的 appId, apiKey, secretKey
需要在百度智能云中创建自己的“文字识别”项目后获取,请访问:https://console.bce.baidu.com/ 。
识别效果如下图所示,其中左侧是被识别的原始图像,右侧上方是
tesseract 识别的效果图,右侧下方是百度 OCR 识别的效果图。百度 OCR 比
tesseract 识别效果稍好,尤其是对中英文混排、标点符号和数字效果更好,不过
tesseract 也基本可用。
文档 :文字识别
OCR
PDF 格式解析
本例中使用 pdfminer 库解析 PDF 文档,完整代码请从 github 下载:
https://github.com/xieyan0811/pdfconv.git
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 from pdfminer.pdftypes import LITERALS_DCT_DECODE, LITERALS_FLATE_DECODEfrom pdfminer.pdfcolor import LITERAL_DEVICE_GRAY, LITERAL_DEVICE_RGBfrom pdfminer.pdfparser import PDFParser,PDFDocumentfrom pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreterfrom pdfminer.converter import PDFPageAggregatorfrom pdfminer.layout import LTTextBoxHorizontal, LAParams, LTFigure, LTImage, LTChar, LTTextLinefrom pdfminer.pdfinterp import PDFTextExtractionNotAllowedimport osimport sysimport numpy as npimport importlibimportlib.reload(sys) TMPDIR = 'tmp/' PARSEIMG = True OCR_ONLINE = False def write_image (image, outdir ): stream = image.stream filters = stream.get_filters() if len (filters) == 1 and filters[0 ] in LITERALS_DCT_DECODE: ext = '.jpg' data = stream.get_rawdata() elif image.colorspace is LITERAL_DEVICE_RGB: ext = '.bmp' data = create_bmp(stream.get_data(), stream.bits*3 , image.width, image.height) elif image.colorspace is LITERAL_DEVICE_GRAY: ext = '.bmp' data = create_bmp(stream.get_data(), stream.bits, image.width, image.height) else : ext = '.img' data = stream.get_data() name = image.name+ext path = os.path.join(outdir, name) fp = open (path, 'wb' ) fp.write(data) fp.close() return path, len (data) def write_file (path, text, ftype, debug=False ): with open (path, ftype) as f: if debug: print ("write" , len (text)) f.write(text) def adjust (inpath, outpath ): f = open (inpath) lines = f.readlines() arr = [len (line) for line in lines] length = np.median(arr) string = "" for line in lines: if len (line) >= length and line[-1 ]=='\n' : string += line[:-1 ] elif line == '-----------\n' : pass else : string += line write_file(outpath, string, 'w' ) return def parse_section (layout, outpath, debug = False ): for x in layout: if (isinstance (x, LTTextBoxHorizontal)): write_file(outpath, x.get_text(), 'a' ) elif (isinstance (x, LTFigure)): parse_section(x, outpath) elif (isinstance (x, LTImage)) and PARSEIMG: path,length = write_image(x, TMPDIR) if length > 0 : if OCR_ONLINE: write_file(outpath, img_to_str_baidu(path), 'a' ) else : write_file(outpath, img_to_str_tesseract(path), 'a' ) write_file(outpath, '\n' + '-----------' + '\n' , 'a' ) def remove (path ): if not os.path.exists(path): return if os.path.isfile(path): os.remove(path) return dirs = os.listdir(path) for f in dirs: file_name = os.path.join(path, f) if os.path.isfile(file_name): os.remove(file_name) else : remove(file_name) os.rmdir(path) def parse (inpath, outpath ): remove(TMPDIR) os.mkdir(TMPDIR) remove(outpath) fp = open (inpath, 'rb' ) praser = PDFParser(fp) doc = PDFDocument() praser.set_document(doc) doc.set_parser(praser) doc.initialize() if not doc.is_extractable: raise PDFTextExtractionNotAllowed else : rsrcmgr = PDFResourceManager() laparams = LAParams() device = PDFPageAggregator(rsrcmgr, laparams=laparams) interpreter = PDFPageInterpreter(rsrcmgr, device) for idx,page in enumerate (doc.get_pages()): interpreter.process_page(page) layout = device.get_result() print ("parse" , idx) parse_section(layout, outpath) if __name__ == '__main__' : pdffile = "xxxx.pdf" tmpfile = pdffile.replace('pdf' ,'tmp' ) txtfile = pdffile.replace('pdf' ,'txt' ) parse(pdffile, tmpfile) adjust(tmpfile, txtfile)
其中 parse_section 用于解析数据块,PDF 的数据块有
LTTextBox,LTFigure,LTImage,LTRect,LTCurve 和 LTLine
等子对象。LTTextBox 表示一组文本块可能包含在一个矩形区域;LTTextLine
表示单个文本行 LTChar 对象的列表;LTImage 表示一个图像对象;LTLine
表示一条直线;LTRect: 表示矩形;LTCurve
表示曲线。有些对象之间包括嵌套关系。
一些问题
程序通过百余行代码实现转换功能,解析普通的 PDF
文件问题不大,但仍存在一些问题:
本文中使用的 pdfminer 库中对 pdf
文件中数据块的解析不够完美,只支持主流的 jpg、bmp 格式文件,有一些 pdf
中的图片无法被识别。
竖版文字也被识别成横版。
解析字符型文本时,比较简单粗暴,对于特殊的版式不一定按照从上到下,从左到右的顺序解析,有待更进。
程序目前以支持中文 PDF
文件为主,支持其它语言需要在代码中稍做调整。
参考
百度接口用法
https://cloud.baidu.com/doc/OCR/OCR-Python-SDK.html#.E9.80.9A.E7.94.A8.E6.96.87.E5.AD.97.E8.AF.86.E5.88.AB