Go 原生解析 pdf 库与 Apache Tika 的比较

1、概述

目前项目采用 go 语言进行编写,有对 pdf 进行抽取文字的需求,目前采用了 Apache 的 Tika 作为 pdf 的解析服务。出于多尝试的目的,考察一下 go 在解析 pdf 方面的能力。

2、调研的库

初始锁定的目标有:

https://github.com/rsc/pdf
https://github.com/yob/pdfreader
https://github.com/hhrutter/pdfcpu
https://github.com/ledongthuc/pdf
https://github.com/unidoc/unidoc
https://github.com/sajari/docconv
https://github.com/jung-kurt/gofpdf
https://github.com/signintech/gopdf
https://github.com/mikeshimura/goreport

在调研过程中,发现绝大多数的用 go 语言编写的与 pdf 相关库均是 pdf 的生成器,能够进行解析 pdf 的库如下:

https://github.com/rsc/pdf
https://github.com/sajari/docconv
https://github.com/unidoc/unidoc
https://github.com/ledongthuc/pdf
https://github.com/hhrutter/pdfcpu
https://github.com/yob/pdfreader

在进一步的分析过程中,ledongthuc/pdf 实际是 rsc/pdf 的在封装,pdfcpu 则采用的 cmd 命令行模式,pdfreader 采用的则是对 pdf 抽取成新文档的模式,参见:https://code.google.com/archive/p/pdfreader/downloads; sajari/docconv 则在其 issue 中申明目前真该尝试解析 pdf ,参见:https://github.com/sajari/docconv/issues/38
综合下来,最终需要对比的库为如下:

https://github.com/unidoc/unidoc
https://github.com/ledongthuc/pdf

3、对比

下面将从抽取的准确性、解析速度方面进行对比。
解析对比的 pdf 文件介入如下:


Go 原生解析 pdf 库与 Apache Tika 的比较_第1张图片
测试 pdf 1

Go 原生解析 pdf 库与 Apache Tika 的比较_第2张图片
测试 pdf 2

全英文字母解析测试

tika

采用 github.com/google/go-tika/tika 库进行,tika 的解析结果如下:

Examples

Multiple examples are provided in our example repository. Many features for

processing PDF files with documented examples on our website.

Contact us if you need any specific examples.

Vendoring

For reliability, we recommend using specific versions and the vendoring

capability of golang. Check out the Releases section to see the tagged releases.

Licensing Information

This library (UniDoc) has a dual license, a commercial one suitable for closed

source projects and an AGPL license that can be used in open source software.

Depending on your needs, you must choose one of them and follow its policies.

A detail of the policies and agreements for each license type are available in

the LICENSE.COMMERCIAL and LICENSE.AGPL files.

In brief, purchasing a license is mandatory as soon as you develop activities

distributing the UniDoc software inside your product or deploying it on a network

without disclosing the source code of your own applications under the AGPL

license. These activities include:

https://unidoc.io/examples
https://www.ctolib.com/unidoc-unidoc.html
https://www.ctolib.com/unidoc-unidoc.html
https://www.ctolib.com/unidoc/unidoc/blob/master/LICENSE.COMMERCIAL
https://www.ctolib.com/unidoc/unidoc/blob/master/LICENSE.AGPL


 offering services as an application service provider or over-network

application programming interface (API)

 creating/manipulating documents for users in a web/server/cloud

application

 shipping UniDoc with a closed source product

Please see pricing to purchase a commercial license or contact sales

at [email protected] for more info.

http://unidoc.io/pricing
mailto:[email protected]

ledongthuc/pdf

解析代码如下:

func extractText(path string) (string, error) {
    f, r, err := pdf.Open(path)
    // remember close filuilder bytee
    defer f.Close()
    if err != nil {
        return "", err
    }
    var buf bytes.Buffer
    b, err := r.GetPlainText()
    if err != nil {
        return "", err
    }
    buf.ReadFrom(b)
    return buf.String(), nil
}

上述是不含格式的纯抽取文本的代码,抽取结果如下:

ExamplesMultipleexamplesareprovidedinourexamplerepository.ManyfeaturesforprocessingPDFfileswithdocumentedexamplesonourwebsite.Contactusifyouneedanyspecificexamples.VendoringForreliability,werecommendusingspecificversionsandthevendoringcapabilityofgolang.CheckouttheReleasessectiontoseethetaggedreleases.LicensingInformationThislibrary(UniDoc)hasaduallicense,acommercialonesuitableforclosedsourceprojectsandanAGPLlicensethatcanbeusedinopensourcesoftware.Dependingonyourneeds,youmustchooseoneofthemandfollowitspolicies.AdetailofthepoliciesandagreementsforeachlicensetypeareavailableintheLICENSE.COMMERCIALandLICENSE.AGPLfiles.Inbrief,purchasingalicenseismandatoryassoonasyoudevelopactivitiesdistributingtheUniDocsoftwareinsideyourproductordeployingitonanetworkwithoutdisclosingthesourcecodeofyourownapplicationsundertheAGPLlicense.Theseactivitiesinclude:offeringservicesasanapplicationserviceproviderorover-networkapplicationprogramminginterface(API)creating/manipulatingdocumentsforusersinaweb/server/cloudapplicationshippingUniDocwithaclosedsourceproductPleaseseepricingtopurchaseacommerciallicenseorcontactsalesatsales@unidoc.ioformoreinfo.

如上可以看出,ledongthuc/pdf 在纯文本抽取的情况下,不包含空格和换行符,在抽取效果上不如 tika;
ledongthuc/pdf 还提供了一种包含 pdf 的格式信息,如字体、字号、文字位置等相关信息的抽取方法,
代码如下:

func extractText2(path string) (string, error) {
    f, err := pdf.Open(path)
    if err != nil {
        return "", err
    }

    pages := f.NumPage()
    var content string
    for i := 1; i <= pages; i++ {
        p := f.Page(i)
        if err != nil {
            return "", err
        }
        content += fmt.Sprintf("%+v", p.Content())
    }
    return content, nil
}

其解析结果如下:

{Text:[{Font:SymbolMT FontSize:9.950000000000001 X:108 Y:750.65 W:0 S:} {Font:SymbolMT FontSize:9.950000000000001 X:108 Y:750.65 W:0 S:x} {Font:FreeSans FontSize:12 X:126 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:126 Y:750.65 W:0 S:R} {Font:FreeSans FontSize:12 X:132.3594 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:132.3594 Y:750.65 W:0 S:I} {Font:FreeSans FontSize:12 X:135.7188 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:135.7188 Y:750.65 W:0 S:I} {Font:FreeSans FontSize:12 X:139.07819999999998 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:139.07819999999998 Y:750.65 W:0 S:H} {Font:FreeSans FontSize:12 X:145.43759999999997 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:145.43759999999997 Y:750.65 W:0 S:U} {Font:FreeSans FontSize:12 X:149.39699999999996 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:149.39699999999996 Y:750.65 W:0 S:L} {Font:FreeSans FontSize:12 X:152.15639999999996 Y:750.65 W:0 S:} {Font:FreeSans FontSize:12 X:152.15639999999996 Y:750.65 W:0 S:Q}...

p.Content 返回的数据结构如下:

type Content struct {
    Text []Text
    Rect []Rect
}

type Text struct {
    Font     string  // the font used
    FontSize float64 // the font size, in points (1/72 of an inch)
    X        float64 // the X coordinate, in points, increasing left to right
    Y        float64 // the Y coordinate, in points, increasing bottom to top
    W        float64 // the width of the text, in points
    S        string  // the actual UTF-8 text
}

Text 中的 S 才是对应的文本,不过其是按照字符进行拆解的,从抽取的结果上看,其抽取结果不准确,输出的头几个 Text 结构体中均未出现 Example 的完整表达。

unidoc

unidoc 的相关文档参见:https://unidoc.io/examples/text/extract-text-in-pdf
其实际抽取效果如下:

(
[
D
P
S
O
H
V0
X
O
W
L
S
O
H   H
[
D
......

从抽取结果上来看,完全不满足使用需求。

包含中文的问题抽取

tika

tika 的效果如下:

路由设置�-�beego:�简约�&�强⼤并存的�Go�应⽤框架

https://beego.me/docs/mvc/controller/router.md 1/10

到�GitHub�上改进本⻚⾯�(https://github.com/beego/beedoc/blob/master/zh-CN/mvc/controller/router.md)

⾃定义搜索

路由设置

什么是路由设置呢?前⾯介绍的�MVC�结构执⾏时,介绍过�beego�存在三种⽅式的路由:固定路由、正则路由、
⾃动路由,接下来详细的讲解如何使⽤这三种路由。

基础路由

从�beego�1.2�版本开始⽀持了基本的�RESTful�函数式路由,应⽤中的⼤多数路由都会定义在� routers/router.go
⽂件中。最简单的�beego�路由由�URI�和闭包函数组成。

基本�GET�路由

beego.Get("/",func(ctx�*context.Context){�

�����ctx.Output.Body([]byte(�hello�world�))�

})

基本�POST�路由

beego.Post(�/alice�,func(ctx�*context.Context){�

�����ctx.Output.Body([]byte(�bob�))�

})

注册⼀个可以响应任何�HTTP�的路由

beego.Any(�/foo�,func(ctx�*context.Context){�

�����ctx.Output.Body([]byte(�bar�))�

})

所有的⽀持的基础函数如下所⽰

https://github.com/beego/beedoc/blob/master/zh-CN/mvc/controller/router.md


2018/11/23 路由设置�-�beego:�简约�&�强⼤并存的�Go�应⽤框架

基本的中文字符已经抽取出来,部分特殊字符显示上存在问题

ledongthuc/pdf

由于上述全英文的对比过程中,包含字体、字号的全解析方法效果不好,故只比对纯文本的抽取:

¹¶CFFHPG    Êõ&(P¨¹ÌIUUQTFFHPNFEPDTNWDDPOUSPMMFSSPVUFSNEy(JU)VC¥÷      IUUQTJUIVCDPNCFFHPCFFEPDCMPCNBTUFS[I$/NWDDPOUSPMMFSSPVUFSNE
Ëö
GVODìDUY<ö½SPVUFSTSPVUFSHP"Ët¤o&CFFHP¹¶¶63*U$Ù2÷Ã7tý÷(&5¹¶CFFHP(FU  
        DPOUFYU$POUFYU
\DUY0VUQVU#PEZ  <>CZUF  
                        IFMMPXPSME


^
ý÷1045¹¶CFFHP1PTU   
                     BMJDF
GVOD    DUY
        DPOUFYU$POUFYU
\DUY0VUQVU#PEZ  <>CZUF  
                        CPC


^Çcë¸
o    )551&¹¶CFFHP"OZ    
                     GPP
GVOD    DUY
        DPOUFYU$POUFYU
\DUY0VUQVU#PEZ  <>CZUF  
                        CBS


^
CFFHP'JMUFS'VODPG   Êõ&(P¨¹ÌIUUQTFFHPNFEPDTNWDDPOUSPMMFSSPVUFSNECFFHP(FU    SPVUFS
CFFHP'JMUFS'VODFS
CFFHP'JMUFS'VOD
CFFHP'JMUFS'VODFS
CFFHP'JMUFS'VODFS
CFFHP'JMUFS'VODPVUFS
CFFHP'JMUFS'VODPVUFS
CFFHP'JMUFS'VOD
ºËö&IBOEMFS
u7yCFFHPËNFSWFS¨
½cëÓ:&u7QD/FX4FSWFS 
T3FHJTUFS$PEFD  KTPO/FX$PEFD    

从上述抽取结果来看,在包含中文的文本提取过程中,ledongthuc/pdf 基本不可用。

纯英文的抽取时间对比

经过上述比较之后,只考虑 ledongthuc/pdf 与 tika 在纯英文抽取方面的对比:

库/server 第一次抽取 第二次抽取 第三次抽取 第四次抽取 第五次抽取
tika 408ms 40ms 35ms 33ms 40ms
ledongthuc/pdf 4ms 2ms 2ms 4ms 2ms

从上述时间对比上看,tika 尽在初次解析的时候慢,但后来基本都稳定在 30-40 ms,然后 ledongthuc/pdf 基本都稳定在 2-4 ms, 两者之间有 10 倍的性能差距。

总结

从上述的解析效果来看,tika 具有巨大的优势,中英文皆可以,然而 go 语言的相关库却在解析效果上不尽人意,且不支持中文的解析。
但若对于纯英文的解析,ledongthuc/pdf 有着较大的性能优势,却是在忽略空格,换行符等的情况下。考虑到解析方法可以进一步优化的下,可以考虑修改对空格等的支持。
ledongthuc/pdf 在 github 上的 demo 解析效果非常好,预计有提升的空间。

你可能感兴趣的:(Go 原生解析 pdf 库与 Apache Tika 的比较)