如何使用R从pdf提取粗体文本

时间:2018-11-20 17:42:00

标签: r pdf

我已经搜索了SO,最接近答案的是here。 但是我的要求是要获得一种更简单,更优雅的方法来从pdf文本的简单段落中提取粗体。 pdftools包仅提取纯文本组件。有谁知道是否有其他方法可以从pdf文本中检测粗体标记(或单词)。我使用R是为了限制R中的建议。

4 个答案:

答案 0 :(得分:2)

您不必使用tabularizer,但是我不知道不涉及Java的方法。我曾希望可以通过rtika软件包使用Apache Tika。但是,bold text is not rendered as such。但是,可以使用该凭单中显示的pdfbox

 java -jar <pdfbox-jar> ExtractText -html <pdf-file> <html-file>

该命令通常在shell中启动,但是您也可以在R中使用system(2)。然后在R中使用

html <- xml2::read_html(<html-file>)
bold <- xml2::xml_find_all(html, '//b')
head(xml2::xml_contents(bold))

处理HTML文件。 对于您的文档,此返回结果

{xml_nodeset (6)}
[1] Preamble\n
[2] WHEREAS it is expedient to define and amend certain parts of the law relating to contracts;\n
[3] History\n
[4] Ancient and Medieval Period\n
[5] The Introduction of English Law Into India\n
[6] Mofussal Courts\n

答案 1 :(得分:2)

除了拥有灵活的工具包外,数据科学还经常需要开箱即用的思想(至少在我的职业中如此)。

但是,首先,关于PDF文件。

我认为它们不是您想的那样。 “粗体”(或“斜体”等)不是“元数据”。您应该花一些时间来阅读PDF文件,因为它们是复杂的,令人讨厌的,邪恶的东西,您在处理数据时可能经常遇到这些东西。阅读此书https://stackoverflow.com/a/19777953/1457051,以了解查找粗体文本实际上需要做些什么(请遵循1.8.x Java pdfbox解决方案的链接)。

返回我们不定期安排的答案

虽然我是R的YUGEst拥护者之一,但不是需要做的所有事情,也不应该在R中完成。当然,我们将使用R最终实现 >获得您的粗体文字,但我们将使用帮助程序命令行实用程序来实现。

pdftools程序包基于poppler库。它带有源,因此“我只是R用户”,他们的系统上可能没有完整的poppler工具集。

Mac使用者可以使用Homebrew(一旦您完成Homebrew设置):

  • brew install poppler

Linux人员知道如何做事。 Windows用户永远迷路了(有 are poppler二进制文件供您使用,但您最好花费时间切换到实际的操作系统上。)

完成此操作后,您可以使用以下内容实现您的目标。

首先,我们将创建一个具有许多安全保险杠的辅助功能:

#' Uses the command-line pdftohtml function from the poppler library
#' to convert a PDF to HTML and then read it in with xml2::read_html()
#'
#' @md
#' @param path the path to the file [path.expand()] will be run on this value
#' @param extra_args extra command-line arguments to be passed to `pdftohtml`.
#'        They should be supplied as you would supply arguments to the `args`
#'        parameter of [system2()].
read_pdf_as_html <- function(path, extra_args=character()) {

  # make sure poppler/pdftohtml is installed
  pdftohtml <- Sys.which("pdftohtml")
  if (pdftohtml == "") {
    stop("The pdftohtml command-line utility must be installed.", call.=FALSE)
  }

  # make sure the file exists
  path <- path.expand(path)
  stopifnot(file.exists(path))

  # pdf's should really have a PDF extension
  stopifnot(tolower(tools::file_ext(path)) == "pdf")

  # get by with a little help from our friends
  suppressPackageStartupMessages({
    library(xml2, warn.conflicts = FALSE, quietly = TRUE)
    library(rvest, warn.conflicts = FALSE, quietly = TRUE)
  })

  # we're going to do the conversion in a temp directory space
  td <- tempfile(fileext = "_dir")
  dir.create(td)
  on.exit(unlink(td, recursive=TRUE), add=TRUE)

  # save our current working directory
  curwd <- getwd()
  on.exit(setwd(curwd), add=TRUE)

  # move to the temp space
  setwd(td)
  file.copy(path, td)

  # collect the extra arguments
  c(
    "-i" # ignore images
  ) -> args

  args <- c(args, extra_args, basename(path), "r-doc") # saves it to r-doc-html.html

  # this could take seconds so inform users what's going on
  message("Converting ", basename(path), "...")

  # we'll let stderr display so you can debug errors
  system2(
    command = pdftohtml,
    args = args,
    stdout = TRUE
  ) -> res

  res <- gsub("^Page-", "", res[length(res)])
  message("Converted ", res, " pages")

  # this will need to be changed if poppler ever does anything different
  xml2::read_html("r-docs.html")

}

现在,我们将使用它:

doc <- read_pdf_as_html("~/Data/Mulla__Indian_Contract_Act2018-11-12_01-00.PDF")

bold_tags <- html_nodes(doc, xpath=".//b")

bold_words <- html_text(bold_tags)

head(bold_words, 20)
##  [1] "Preamble"                                                                                   
##  [2] "WHEREAS it is expedient to define and amend certain parts of the law relating to contracts;"
##  [3] "History"                                                                                    
##  [4] "Ancient and Medieval Period"                                                                
##  [5] "The Introduction of English Law Into India"                                                 
##  [6] "Mofussal Courts"                                                                            
##  [7] "Legislation"                                                                                
##  [8] "The Indian Contract Act 1872"                                                               
##  [9] "The Making of the Act"                                                                      
## [10] "Law of Contract Until 1950"                                                                 
## [11] "The Law of Contract after 1950"                                                             
## [12] "Amendments to This Act"                                                                     
## [13] "Other Laws Affecting Contracts and Enforcement"                                             
## [14] "Recommendations of the Indian Law Commission"                                               
## [15] "Section 1."                                                                                 
## [16] "Short title"                                                                                
## [17] "Extent, Commencement."                                                                      
## [18] "Enactments Repealed."                                                                       
## [19] "Applicability of the Act"                                                                   
## [20] "Scheme of the Act"

length(bold_words)
## [1] 1939

完全不需要Java,并且您的粗体字

如果您确实想像Ralf所说的走pdfbox-app路线,则可以使用此包装器使其更易于使用:

read_pdf_as_html_with_pdfbox <- function(path) {

  java <- Sys.which("java")
  if (java == "") {
    stop("Java binary is not on the system PATH.", call.=FALSE)
  }

  # get by with a little help from our friends
  suppressPackageStartupMessages({
    library(httr, warn.conflicts = FALSE, quietly = TRUE)
    library(xml2, warn.conflicts = FALSE, quietly = TRUE)
    library(rvest, warn.conflicts = FALSE, quietly = TRUE)
  })

  path <- path.expand(path)
  stopifnot(file.exists(path))

  # pdf's should really have a PDF extension
  stopifnot(tolower(tools::file_ext(path)) == "pdf")

  # download the pdfbox "app" if not installed
  if (!dir.exists("~/.pdfboxjars")) {
    message("~/.pdfboxjars not found. Creating it and downloading pdfbox-app jar...")
    dir.create("~/.pdfboxjars")
    httr::GET(
      url = "http://central.maven.org/maven2/org/apache/pdfbox/pdfbox-app/2.0.12/pdfbox-app-2.0.12.jar",
      httr::write_disk(file.path("~/.pdfboxjars", "pdfbox-app-2.0.12.jar")),
      httr::progress()
    ) -> res
    httr::stop_for_status(res)
  }

  # we're going to do the conversion in a temp directory space
  tf <- tempfile(fileext = ".html")
  on.exit(unlink(tf), add=TRUE)

  c(
    "-jar",
    path.expand(file.path("~/.pdfboxjars", "pdfbox-app-2.0.12.jar")),
    "ExtractText",
    "-html",
    path,
    tf
  ) -> args

  # this could take seconds so inform users what's going on
  message("Converting ", basename(path), "...")

  system2(
    command = java,
    args = args
  ) -> res

  xml2::read_html(tf)

}

答案 2 :(得分:0)

此答案基于从@hrbmstr和@ralf收到的答案。因此,感谢他们。我已经简化了答案(主要是去除了HTML转换和文件命名的特殊性)。它也是为MAC OS用户(也许还有LINUX)量身定制的-不确定Windows家伙。

我想您已经在计算机上安装了pdftohtml。如果不使用brew install pdftohtml。如果您的MAC上没有自制软件,请先安装。提供了一个链接来帮助您for homebrew

确定要在Mac上安装pdftohtml后,请使用此R函数从任何pdf文档中提取粗体。

library(magrittr)
library(rvest)
library(stringr)

# pass a pdf file in current directory to this function
extr_bold <- function(file) {
  basefile <- str_remove(file,"\\.pdf|\\.PDF")
  htmlfile <- paste0(basefile,"s",".html")
  if(!exists(htmlfile) ) 
    system2("pdftohtml",args = c("-i",file),stdout=NULL)
  nodevar <- read_html(htmlfile)
  x <- html_nodes(nodevar,xpath = ".//b")
  html_text(x)
}

答案 3 :(得分:0)

2021 年 6 月 14 日更新的答案

我认为这个问题需要一个更新的答案。

好消息:R 包 pdftools 在其最近的更新中包含了从 pdf 中提取 font-data 的选项。函数 pdf_data 现在具有 documentation 中描述的附加参数 font_info

<块引用>

font_info 如果为 TRUE,则为每个框提取字体数据。小心,这 需要最新版本的 poppler,否则会出错。

使用 pdftools::pdf_datafont_info=TRUE 的简单实现表明:

pdftools::pdf_data(pdf = "https://arxiv.org/pdf/2012.10582.pdf", font_info = TRUE)

enter image description here

备注:

  1. 某些粗体字体由中等字体 (Medi) 的后缀表示,例如 KBJWLM+NimbusRomNo9L-Medi
  2. 斜体由后缀表示,如 ReguItal 代表“常规斜体”。例如,ZBSJXS+NimbusRomNo9L-ReguItal
  3. 常规字体显然由纯 Regu 后缀表示,如 VDTZKA+NimbusRomNo9L-Regu

警告:此答案从未针对具有重叠/重叠文本的 pdf 图像(扫描)进行过测试。