[R語言應用] 利用R語言處理PDF檔案

0

Last Updated on 2023-10-05

Home » R語言教學 » R語言應用 » [R語言應用] 利用R語言處理PDF檔案

你有沒有時常遇到PDF的檔案,需要把裡面的文字複製出來再貼上到其他地方,但又覺得麻煩?這篇文章告訴你解決方法


平常工作上會用到很多PDF格式的檔案,常常會用到當中的文字,或是需要裁切與合併PDF檔案,又或者要把PDF轉成txt、jpeg,但是用起來很麻煩嗎?

我整理了在R語言當中,如何利用套件處理PDF檔案的方法,讓你不用再打開線上的PDF處理工具啦,而且確保資料都留在自己的電腦裡。

R語言中PDF相關套件介紹

在R語言中,如果想要處理PDF(Portable Document
Format)格式的檔案,依照使用者需求,最常用的有3個套件。

  • pdftools: 最實用,可以讀取PDF文件,提取裡面的文字、轉換成圖像、裁切與合併等。
  • tabulizer: 在看政府公告或研究報告的時候常常會需要提取表格,但直接複製貼上格式會跑掉,這個套件就是專門拿來提取表格用的。
  • tesseract: 如果檔案根本就是圖檔怎麼辦?有很多偽PDF其實是圖像,這時候需要光學字元識別(Optical Character Recognition, OCR),把圖像轉文字,這個套件就可以派上用場。

怎麼利用R語言套件處理PDF

底下會分別介紹上面提到的3個在R語言中處理PDF(Portable Document Format)檔案的套件。

pdftools:提取文字

這個套件由 R Open Science(簡稱為rOpenSci,是一個提倡開放科學研究的專案)開發,如前所述,它能夠幫上我們日常生活的工作,不過,其實套件中有好幾個函數都是來自於另一個套件 qpdf 當中,所以它算是站在前人的肩膀上!

底下介紹幾個常用函數:
pdf_length(): 計算 PDF 檔案的頁數,平常其實打開檔案就看得到頁數,但如果處理大量檔案的時候就能派上用場。
pdf_info(): 取得 PDF 的 metadata,不確定要翻成元資料還是後設資料,總之就是這份資料的相關資訊。
pdf_length(): 計算 PDF 檔案的頁數。
pdf_convert(): 把 PDF 檔案轉換成圖像,選項有 png, jpeg, jpg, tiff, pnm 等,如果想輸出成 txt 不能用這個函數喔。
pdf_text(): 把 PDF 檔案中的文字提取出來,但成功的前提是,本來就可以用滑鼠選取文字!如果本來就是圖檔,那什麼都抓不到。
pdf_subset(): 裁切 PDF ,要提供它開始跟結束的頁數。
pdf_combine(): 拼接 PDF ,要提供它想要合併的 PDF 檔案名稱。

底下我們就來看要怎麼活用 pdftools 套件吧,我使用台北市媒體服務代理商協會的2021年媒體白皮書作為案例,你可以到這裡下載。

library(pdftools)

### 長度
pdf_length("2021年媒體白皮書.pdf")
#> [1] 81

### metadata
pdf_info("2021年媒體白皮書.pdf")
#> $version
#> [1] "1.7"
#> 
#> $pages
#> [1] 81
#> 
#> $encrypted
#> [1] FALSE
#> 
#> $linearized
#> [1] FALSE
#> 
#> $keys
#> $keys$Title
#> [1] "貳、領隊實務(一)"
#> 
#> $keys$Author
#> [1] "user"
#> 
#> $keys$Creator
#> [1] "適用於 Microsoft 365 的 Microsoft® Word"
#> 
#> $keys$Producer
#> [1] "適用於 Microsoft 365 的 Microsoft® Word"
#> 
#> 
#> $created
#> [1] "2021-08-25 16:22:29 CST"
#> 
#> $modified
#> [1] "2021-08-25 16:22:29 CST"
#> 
#> $metadata
#> [1] "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?><x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"3.1-701\">\n<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n<rdf:Description rdf:about=\"\"  xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n<pdf:Producer>é\u0081©ç\u0094¨æ\u0096¼ Microsoft 365 ç\u009a\u0084 Microsoft® Word</pdf:Producer></rdf:Description>\n<rdf:Description rdf:about=\"\"  xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n<dc:title><rdf:Alt><rdf:li xml:lang=\"x-default\">è²³ã\u0080\u0081é \u0098é\u009a\u008a實å\u008b\u0099ï¼\u0088ä¸\u0080ï¼\u0089</rdf:li></rdf:Alt></dc:title><dc:creator><rdf:Seq><rdf:li>user</rdf:li></rdf:Seq></dc:creator></rdf:Description>\n<rdf:Description rdf:about=\"\"  xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n<xmp:CreatorTool>é\u0081©ç\u0094¨æ\u0096¼ Microsoft 365 ç\u009a\u0084 Microsoft® Word</xmp:CreatorTool><xmp:CreateDate>2021-08-25T16:22:29+08:00</xmp:CreateDate><xmp:ModifyDate>2021-08-25T16:22:29+08:00</xmp:ModifyDate></rdf:Description>\n<rdf:Description rdf:about=\"\"  xmlns:xmpMM=\"http://ns.adobe.com/xap/1.0/mm/\">\n<xmpMM:DocumentID>uuid:E812F000-7269-4A87-ABCF-462F611E126B</xmpMM:DocumentID><xmpMM:InstanceID>uuid:E812F000-7269-4A87-ABCF-462F611E126B</xmpMM:InstanceID></rdf:Description>\n \n</rdf:RDF></x:xmpmeta><?xpacket end=\"w\"?>"
#> 
#> $locked
#> [1] FALSE
#> 
#> $attachments
#> [1] FALSE
#> 
#> $layout
#> [1] "no_layout"

### 轉圖檔
pdf_convert("2021年媒體白皮書_前10頁.pdf", page = 10, format = "png")
#> Converting page 10 to 2021年媒體白皮書_前10頁_10.png... done!
#> [1] "2021年媒體白皮書_前10頁_10.png"

### 裁切
pdf_subset(input = "2021年媒體白皮書.pdf", pages = c(1:10), output = "2021年媒體白皮書_前10頁.pdf")
#> [1] "/Users/dennistseng/Documents/GitHub/r-lover/2023-05-15-tutorial-material-r-import-pdf/2021年媒體白皮書_前10頁.pdf"
pdf_subset(input = "2021年媒體白皮書.pdf", pages = c(11), output = "2021年媒體白皮書_11頁.pdf")
#> [1] "/Users/dennistseng/Documents/GitHub/r-lover/2023-05-15-tutorial-material-r-import-pdf/2021年媒體白皮書_11頁.pdf"

### 拼接
pdf_combine(input = c("2021年媒體白皮書_前10頁.pdf","2021年媒體白皮書_11頁.pdf"), output = "2021年媒體白皮書_前11頁.pdf")
#> [1] "/Users/dennistseng/Documents/GitHub/r-lover/2023-05-15-tutorial-material-r-import-pdf/2021年媒體白皮書_前11頁.pdf"

### 檢查長度
pdf_length("2021年媒體白皮書_前11頁.pdf")
#> [1] 11

### 提取文字
text <- pdf_text("2021年媒體白皮書_前10頁.pdf")
text[5]
#> [1] "MAA 簡介                         © 2021 年 MAA 台灣媒體白皮書\n               MAA 簡介\n台北市媒體服務代理商協會(Media Agency Association,簡稱MAA),乃集\n合以”媒體整合企劃與購買”為核心服務之代理商,共同推動一流的媒體專業\n服務於廣告主、廣告服務與媒體產業。\n本會之任務如下:\n一、 提供一個便利會員及媒體服務代理同業互相交換意見及溝通之場所。\n二、 與會員有關之立法通知,並就會員之共同利益協助提供立法建議。\n三、 代表會員之意見,並協助會員進行與媒體、政府之洽商。\n四、 定期舉辦各類座談會、演講等活動。\n五、 促使本會會員在與客戶交易或與同業往來時,皆遵守最高之商業道德\n     標準。\n六、 制定會員共同遵守之執業標準。\n七、 向社會大眾及政府機關推廣廣告之重要性及促進大眾對媒體、廣告行\n     銷之暸解。\n八、 對影響本會會員權益之事,代表會員與有關單位洽詢並提供改革計劃。\n九、 保障本會會員及廣告公司之共同利益。\n十、 促進會員製作廣告之合法性、誠實性、淨化和真實,並保障一般大眾\n     之利益。\n                    1\n"
text[10]
#> [1] "台灣地區整體概況                                    © 2021 年 MAA 台灣媒體白皮書\n2018年:無線:0.056, 有線:0.053, 報紙:0.324, 雜誌:0.349, 廣播:0.226, 戶外:0.85\n2019年:無線:0.053, 有線:0.052, 報紙:0.320, 雜誌:0.335, 廣播:0.223, 戶外:0.81\n2020年:無線:0.050, 有線:0.046, 報紙:0.230, 雜誌:0.280, 廣播:0.190, 戶外:0.70\n                  年度前十大品類與廣告主\n廣告量前十大品類 (不含網路廣告量)\n    排名                   品類                         金額(千元)\n      1               醫藥美容類                          5,975,986\n      2             電腦網路資訊類                          2,275,789\n      3                 其他類                          1,805,137\n      4                 建築類                          1,485,144\n      5                交通工具                          1,369,904\n      6               影劇媒體類                          1,243,139\n      7                 食品類                          1,129,679\n      8                 服務類                          1,111,289\n      9                家用品類                          1,005,668\n     10                 文康類                           998,639\n附註: 報紙專業綜合廣告不計入排名之列\n廣告量前十大廣告主 (不含網路廣告量)\n    排名                 廣告主                          金額(千元)\n      1              民視電視(股)                          639,932\n      2           荷商葛蘭素史克藥廠                           587,597\n      3             東森得意購(股)                          555,687\n      4              寶僑家品公司                           540,010\n      5             三得利健益(股)                          539,592\n      6              佳格食品(股)                          499,067\n      7              統一企業(股)                          348,595\n      8              輝瑞生技(股)                          302,267\n      9              白蘭氏三得利                           295,610\n     10            台灣松下電器(股)                          272,598\n資料來源:Nielsen 廣告監播服務,2020年\n廣告量依MAA調整率調整:無線0.050,有線0.046,報紙0.230,雜誌0.280,廣播0.190,\n戶外0.700\n                                 6\n"

上面最後提取文字部分,我抓了第五頁跟第十頁,我們來看一下原本長什麼樣子。

要轉換的圖像

要轉換的圖像 – 第5頁,是一段文字

要轉換的圖像

要轉換的圖像 – 第10頁,是一張表格

這樣看起來轉換的結果還不錯吧!不過,對照第5頁還好,對照第10頁就會發現,表格怎麼變成不是表格了?

雖然 pdftools 很好,但它並非萬能,上面的表格就是它的限制之一。在官方介紹中,就提到 pdftools 無法處理設有密碼的 PDF,也沒辦法處理非文字的 PDF,而且處理表格的時候問題更大。幸好,R語言當中還有其他套件可以應對。

tabulizer:提取表格

在這邊要先說聲抱歉,我寫部落格文章的電腦有缺某個 JAVA 元件,但使用
tabulizer 套件必須要該元件才能執行,所以底下只能用說明而無法實作,我放上之前用其他電腦的程式碼供參考。

這個套件又是 R Open Science 開發,它裡面有好幾個函數,但最常用也最關鍵的函數就是 extract_tables(),因為 pdftools 就能夠處理其他任務了。使用這個函數後,它會提取 PDF 檔案中的所有表格,因此得到的 table 會是一個大型的 list,每個 list 當中的元素都會是一張表格,我習慣把它變成 tibble,再後續處理。

不過,它提取出的表格也會有點髒,這邊附上因為上述問題無法實際執行的程式碼供參考,你可以看到我要另外花心思整理表格,若只有1個表格,可以考慮用 word 打開,然後把表格複製貼上到 excel 裡面,量夠少的話也可以直接手寫轉錄出來,我的這個 case 是因為有大量格式一致的檔案,所以還能替自己省下時間。

library(tabulizer)
table_all <- extract_tables("報紙發行量稽核報告.pdf")
table_all[[1]] %>% as_tibble()

### 讀進來之後一些處理步驟,展示一段清理過程
table_channel_13 <- 
  table_channel_compact[vector_channel_13] %>% map(~filter(., str_detect(V1, "年"))) %>% 
  map(~select(., -V9)) %>% bind_rows() %>% `colnames<-`(
    c("月份","訂戶_報社訂戶","訂戶_經銷商訂戶","訂戶_小計","分銷承攬_派報","分銷承攬_批報","分銷承攬_小計",
      "零售通路","大宗銷售","其他","月發行量","日發行量")
  )

tesseract:光學字元辨識

這篇文章的架構是按照處理 PDF 任務的難度由簡至難,從PDF提取文字最簡單,從PDF提取抓表格次之,從從PDF將圖像轉成文字是最難的了。

這個套件同樣由人很好的 R Open Science 開發,如前面的介紹所說,tesseract做的就是 OCR 的工,雖然它號稱可以識別包含中、英文、法、德等多種語言,而且函數也很單一,就是直接用 ocr(),但成功率常常要看你的檔案狀況。

趕快補充一下,使用前要先下載你想辨識語言的引擎,例如繁體中文就是
“chi_tra”。你可以到這個頁面確認你想下載的語言名稱。

我們先來看一下要轉換的圖像長什麼樣子。雖然它是 PNG 格式,但其實原本是 PDF 喔,我就是用上面 pdftools 的裁切跟轉換,先取第一頁,接著轉成 PNG,得到這張圖像的。

要轉換的圖像

要轉換的圖像 – 封面圖案

library(tesseract)

### 檢查你安裝了哪些語言引擎
tesseract_info()
#> $datapath
#> [1] "/Users/dennistseng/Library/Application Support/tesseract4/tessdata/"
#> 
#> $available
#> [1] "chi_tra" "eng"     "osd"    
#> 
#> $version
#> [1] "4.1.0"
#> 
#> $configs
#>  [1] "alto"             "ambigs.train"     "api_config"       "bigram"          
#>  [5] "box.train"        "box.train.stderr" "digits"           "get.images"      
#>  [9] "hocr"             "inter"            "kannada"          "linebox"         
#> [13] "logfile"          "lstm.train"       "lstmbox"          "lstmdebug"       
#> [17] "makebox"          "pdf"              "quiet"            "rebox"           
#> [21] "strokewidth"      "tsv"              "txt"              "unlv"            
#> [25] "wordstrbox"
### 使用前要先下載,因為我下載過了所以註解掉
# tesseract_download("chi_tra")
# tesseract_download("eng")

### 載入引擎
eng <- tesseract("eng")
chi <- tesseract("chi_tra")

### OCR
text <- tesseract::ocr("/Users/dennistseng/Documents/GitHub/r-lover/2021年媒體白皮書_第1頁_1.png", engine = eng)
text2 <- tesseract::ocr("/Users/dennistseng/Documents/GitHub/r-lover/2021年媒體白皮書_第1頁_1.png", engine = chi)

### 來看結果
text
#> [1] "QQ» 2021 sseKBALe\n~» Media Book\nMedia Agency Association\nMé@ithsaaBte eae\nAA ©2021% MAAS RRR OR\n—_—__\n"
text2
#> [1] "全說、2021 年台灣媒體白皮書\n“有 Media Book\nMedia Agency Association\nPI台北市媒體服務代理商和佃會\n為吃 @2021年 MAA台灣疑馨白皮書\n還\n"

從這個結果我們看出什麼重點?英文引擎無法辨識中文,中文引擎則可以,我自己很滿意辨識成果,因為原始圖像頗為模糊,至少中文辨識結果正確率大概還有 8 成左右,只是這個結果能「用」嗎?假設要辨識整整一百頁的內容,但需要大修特修,可能就要尋求其他解法,除了請工讀生以外,也可以去研究其他更強大的辨識引擎。

當然,你可以提高原本圖檔的清晰程度與檔案大小,這些是語言印情以外,會影響識別結果的關鍵喔!

小結

這篇文章中,介紹了三個在R語言當中處理PDF檔案的套件,分別是
pdftools, tabulizer, tesseract,它們各擅勝場,分別可以負責提取文字裁切合併的一般用途、提取表格、光學字元辨識三種不同任務。希望像這篇一樣的R語言應用對你有幫助也希望你喜歡這篇文章。

關於用R語言處理PDF檔案的常見問題

想要從PDF檔案中提取內容,可以使用R語言嗎?

可以的,R語言中有不同套件可以從PDF檔案中提取內容。

要怎麼利用R語言提取PDF檔案的文字?

你可以利用 pdftools 套件裡面的 pdf_text() 提取文字。

要怎麼利用R語言提取PDF檔案的表格?

你可以利用 tabulizer 套件裡面的 extract_tables() 提取表格。

要怎麼利用R語言把PDF檔案的圖像轉成文字?

你可以利用 tesseract 套件裡面的 ocr() 提取表格。

No Comments

Leave a Reply