【问题标题】:how to extract bold text from a pdf using R如何使用R从pdf中提取粗体文本
【发布时间】:2019-04-23 05:28:44
【问题描述】:

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

【问题讨论】:

  • “请不要建议可能使用特定的工具,因为我不愿意做必要的工作来建立一个可用的数据科学环境” 并不完全会引起人们来回答这个问题。成千上万的 R 人设法拥有一个工作的 R + rJava 环境。它有些头疼。最终它是值得的,因为您可以访问整个世界的优秀 Java 库。无论如何,不​​会为完整的答案而烦恼,但 github.com/hrbrmstr/pdfbox 可能会有所帮助(但 rJava 又“工作”了)。
  • 你能提供一个样本PDF吗?
  • 感谢您的直接建议@hrbrmstr。我明白你想说的话。为听起来懒惰而道歉。我将检查您的链接并尝试设置 rJava 环境。我想我也理解,在你的字里行间,如果我使用 R,rJava 对于数据科学环境是必不可少的。我正确吗?
  • @RalfStubner:我在这个link here 上使用pdf。虽然它有点大,但它的所有标题都是粗体,因此是使用粗体标题作为节标题的文本处理的理想情况。如果您可以提出任何简单的方法(不打算偷懒,但如果有更简单的方法,我会很乐意使用它)。
  • 所以你的实际目标不是“识别粗体文本”而是“识别章节标题”?

标签: r pdf


【解决方案1】:

2021 年 6 月 14 日更新答案

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

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

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

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

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

备注:

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

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

【讨论】:

    【解决方案2】:

    此答案基于从@hrbmstr 和@ralf 收到的答案。所以感谢他们。我使答案变得更简单(主要是排除了 HTML 转换和文件命名的特殊性)。它也是为 MAC OS 用户量身定做的(也许 LINUX 也是)——不确定 Windows 用户。

    我想你的机器上安装了pdftohtml。如果不使用brew install pdftohtml。如果您的 MAC 上没有 homebrew,请先安装它。提供了一个链接来帮助您for homebrew

    一旦您确定pdftohtml 已安装在 mac 上,请使用此 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)
    }
    

    【讨论】:

    • @hrbmstr:您能否检查一下我的回答,看看是否有意义?或者它是否需要一些改进而不增加复杂性,当然还要保持易于阅读。谢谢
    【解决方案3】:

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

    但是,首先,关于 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 人永远迷失了( 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)
    
    }
    

    【讨论】:

    • @hrbmstr :非常感谢。我已经开始了实施过程。 pdftohtml 命令似乎有问题。在自制 poppler(在 Mac OS Mujave 上成功通过)并创建所有临时目录,将 pdf 文件复制到那里等之后,pdftohtml 似乎返回状态 = 1。Warning message: In system2(command = "pdftohtml", args = args, stdout = TRUE) : running command ''pdftohtml' -i' had status 1;请注意,我没有向 pdftohtm 提供 extra_args。 args = "-i" 仅被使用。
    • 尝试在没有该功能的情况下运行它。在终端中,只尝试pdftohtml -i thenameofyourpdffile.pdf,然后在文件末尾有s 的文件上输入read_html
    • 我设法使用命令pdftohtml -i file.pdf 后跟bold_tags &lt;- html_nodes(doc, xpath=".//b"); bold_words &lt;- html_text(bold_tags) 将pdf 转换为html,但变量bold_words 是一个0 长度字符向量。
    • 您能否将您所做的以及之后的确切(包含库调用)R 代码添加到原始问题中?
    • Bingo 成功了!我在默认形成的三个文件中使用了不正确的 html 文件。另外,我建议我们为其他人简化答案。我制作了这个简单的函数,它可以在不添加太多检查的情况下提供输出。
    【解决方案4】:

    你不必使用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
    

    【讨论】:

    • 我认为 Ralf 您的答案使用了 java,因此我现在不尝试了。此外,其余部分似乎与@hrbmstr 相同。非常感谢。我现在将我的工作功能复制为一个新的答案,以便其他搜索此功能的人(包括我自己)不会因为选择不正确的 html 文件而陷入困境。
    猜你喜欢
    • 2018-09-07
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 2013-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多