python reptiles Online Preview pdf document processing

 

introduction

 

 

In recent climb a website, then climbed details page, it found that the target content is in pdf Online Preview

For example, the following website:

 

https://camelot-py.readthedocs.io/en/master/_static/pdf/foo.pdf

 

 

 

 

 

According to my analysis found that such online preview pdf employs pdfjs loading preview, the method crawlers can not get the content directly within the pdf, right, you noticed I said [in] can not directly get direct word, does not directly get, how to do it? Pdf can only be downloaded to the local, and then use the tool to turn, I looked through a lot of relevant information discovery, there are still many tools:

  1. convert pdf to borrow third party websites turn out

  2. Python package to turn: as: pyPdf, pyPdf2, pyPdf4, pdfrw other tools

These tools pypi community in a search a lot:

 

 But how kind of effect do not know, only one by one to try, and I finally found behind a library, the library fits my needs --camelot

 

camelot data can be read pdf files, and automatically converted into pandas library (data analysis related) in the DataFrame type, and then can be converted to csv by DataFrame, json, html will do, my goal is to be converted to html format good, ado, began to engage in

 

Start parsing

  

1. Install camelot:

 

pip install camelot-py

pip install cv2 (because camelot need to use this library)

 

2. Download the pdf: pdf because online is actually a binary stream, so you have to follow to download pictures and video downloads, and then save to a local file, this step is not to say   

 

3. Resolution:

 

import camelot

file = 'temp.pdf'
table = camelot.read_pdf(file,flavor='stream') 
table[0].df.to_html('temp.html')

 

以上的temp.html就是我希望得到的数据了,然后根据我的分析发现,在read_pdf方法里一定带上参数  【flavor='stream'】,不然的话就报这个错:

 

RuntimeError: Please make sure that Ghostscript is installed

 

原因就是,read_pdf默认的flavor参数是lattice,这个模式的话需要安装ghostscript库,然后你需要去下载Python的ghostscript包和ghostscript驱动(跟使用selenium需要下载浏览器驱动一个原理),而默认我们的电脑肯定是没有安装这个驱动的,所以就会报上面那个错。我试着去装了这个驱动和这个包,去read_pdf时其实感觉没有本质区别,是一样的,所以带上参数flavor='stream'即可,当然如果你硬要用lattice模式的话,安装完ghostscript包和ghostscript驱动之后,记得在当前py文件用  【import ghostscript】导入下这个包,不然还是会报如上错误

 

继续走,发现能拿到我想要的数据了,非常nice,然后突然的,报了如下错误:

PyPDF2.utils.PdfReadError: EOF marker not found

 

 

 当时就是卧槽,这什么情况,我开始去研究EOF marker是什么意思,但是我直接打开这个pdf文件又是正常的

 

 

 

非常诡异,网上查阅了一堆,大概意思就是说,没有EOF结束符,这个东西在之前我做js开发的时候遇到过,js的语句体{},少了最后的【}】,

我又去了解了下EOF到底在二进制文件指的什么,然后看到老外的这个帖子:

 

 我用同样的方法查看数据的前五个字符和后五个字符:

 

 

 

 

 

 

 

 

好像有了眉目,我以文本的方式打开了我下载到本地的一个pdf,在%%EOF结尾之后还有很多的null

 

 

 

难道是NULL的问题?我手动删掉null之后,单独对这个修改过的pdf用pdf查看器打开,正常打开,没有问题,我接着用代码针对这个文件执行read_pdf,发现非常神奇的不会报错了,那还真是结尾的NULL元素了。

然后我在从网上读取到pdf之后的二进制部分用字符串的strip()方法,以为用strip可以去除那些null,结果发现还是如此

 

 

 

 -------------------------------------

那就只有先锁定  %%EOF 所在位置,然后切片操作了,部分代码如下,果然问题解决,但同时又报了一个新的错,这个就是个编码问题了,相信搞爬虫的朋友们对这个问题非常熟悉了

 

 

 

先暂时不管这个问题,我又改了下目标网站的指定页码

 

 

 

pdfminer.psparser.SyntaxError: Invalid dictionary construct: [/'Type', /'Font', /'Subtype', /'Type0', /'BaseFont', /b"b'", /"ABCDEE+\\xcb\\xce\\xcc\\xe5'", /'Encoding', /'Identity-H', /'DescendantFonts', <PDFObjRef:11>, /'ToUnicode', <PDFObjRef:19>]

 

 

发现问题越来越严重了,我鼓捣了一番之后,又查了一堆资料,将utf-8改成gb18030还是报错,我发现我小看这个问题了,接着查阅,然后发现github上camelot包的issues也有人提了这个报错,

https://github.com/atlanhq/camelot/issues/161

 

 

 

 

 

 

 

然后这里有个人说可以修复下pdf文件:

 

 

我查了下,需要安装一个软件mupdf,然后在终端用命令 修复  

mutool clean 旧的.pdf 新的.pdf

 

首先这并不是理想的解决方法,在python代码中,是可以调用终端命令,用os和sys模块就可以,但是万一因为终端出问题还不好找原因,所以我并没有去修复,之后我发现我这个决定是对的 

 

接着看,发现issue里很多人都在反馈这个问题,最后看到这个老哥说的

 

 

 

 

 

大概意思就是说pypdf2无法完美的处理中文文档的pdf,而camelot对pdf操作就基于pypdf2,卧槽,这个就难了。

 

 

然后我又查到这篇文章有说到这个问题:https://blog.csdn.net/kmesky/article/details/102695520

 

 

那只能硬改源码了,改就改吧,毕竟这也不是我第一次改源码了

 

注意:如果你不知道的情况下,千万不要改源码,这是一个大忌,除非你非常清楚你要做什么

 

修改源码:

1.format.py

C:\Program Files\Python37\Lib\site-packages\pandas\io\formats\format.py该文件的第846行

 

由这样:

 

 

 

改成这样:

 

  

 

2.generic.py

 

File "D:\projects\myproject\venv\lib\site-packages\PyPDF2\generic.py", 该文件的第484行

 

 

 

 

3.utils.py

Lib/site-packages/PyPDF2/utils.py 第238行

 

 

4.运行

 

再运行:之前那些错误已经没有了

 

但同时又有了一个新的错

 

其实这个超出索引范围的报错的根本是上面的警告:UserWarning:page-1 is image-based,camelot only works on text-based pages. [streams.py:443]

 

 

 

因为源数据pdf的内容是个图片,不再是文字,而camelot只能以文本形式提取数据,所以数据为空,所以 table[0]会报索引超出范围

 

针对图片的处理,我网上查阅了一些资料,觉得这篇文章写的不错,可以提取pdf中的图片

https://blog.csdn.net/qq_15969343/article/details/81673302 

 

但是,我的目标是希望拿到pdf中的内容,然后转成html格式,在之前,我已经由在线pdf->本地pdf->提取表格->表格转html,这是第一种。

如果要提取图片的话,那步骤就是第二种:在线pdf->本地pdf->提取图片->ocr提取表格->验证对错->表格转html,这样就会多些步骤,想想,我为了拿到一个网站的数据,每个网页就要做这些操作,而且还要判断是图片就用用第二种,是表格就用第一种,两个方法加起来的话,爬一个网站的数据要做的操作的就多了,虽然这些都属于IO操作型,但是到后期开启多线程,多进程时,与那些直接就能从源网页提取的相比就太耗时间了。

这样不是不行,是真的耗时间,所以我暂时放弃对图片的提取了,只提取table,先对pdf二进制数据判断是否是图片,是图片就跳过了

 

原理就是,根据上面那片博客里的:

 

 

 

 

 

打开二进制源码验证:

 

 

第一个,它确实是图片的:

 

 

 

 

 

 

第二个,它是表格:

 

 

 

 

 

 

不过经过我的验证,发现这个方法正确率不能百分之百,少部分的即使是表格还是有/Image和/XObject相关的字符串

 

 

那没办法了,有多少是多少吧

 

部分代码实现:

fujian_data = requests.get(fujian_url, headers=headers).content
fujian_index = fujian_data.index(b'%%EOF')
fujian_data = fujian_data[:fujian_index + len(b'%%EOF')]
checkXO = rb"/Type(?= */XObject)"
checkIM = rb"/Subtype(?= */Image)"
isXObject = re.search(checkXO, fujian_data)
isImage = re.search(checkIM, fujian_data)
if isXObject and isImage:
    # 是图片跳过
    pass
f = open('temp.pdf', 'wb')
f.write(fujian_data)
f.close()
tables = camelot.read_pdf('temp.pdf', flavor='stream')
if os.path.exists('temp.pdf'):
    os.remove('temp.pdf')  # 删除本地的pdf
tables[0].df.to_html('foo.html', header=False, index=False)

 

 

 

 

至此完毕,当然,你也可以用camelot 的to_csv 和 to_json方法转成你希望的,具体就自己研究了

 

以上就是Python处理在线pdf的所有内容

Guess you like

Origin www.cnblogs.com/Eeyhan/p/12111371.html