批量提取PDF文本时,如何将表格置于对应位置?
批量提取PDF文本并精准放置表格的优化方案
以下是几种比文本匹配替换更高效的解决方案,无需手动匹配文本片段:
1. 基于tabulizer的坐标定位插入法
先通过可视化工具锁定表格的页面坐标,再按坐标对应位置将表格插入到整页文本中:
- 用
tabulizer::locate_areas(pdf_path)打开PDF可视化界面,手动框选表格区域,记录下每个表格的坐标(格式:c(左上角x, 左上角y, 右下角x, 右下角y)) - 批量处理时,先提取整页文本,再根据坐标对应的页面位置,找到文本中的插入点(可通过表格前后的标志性文本或坐标估算位置),将提取的表格转为格式化文本后插入
示例代码:
library(tabulizer) library(stringr) # 遍历文件夹内所有PDF pdf_files <- list.files(pattern = "\\.pdf$") for (pdf in pdf_files) { # 预定义各页表格坐标(用locate_areas获取) table_areas <- list( 1 = c(120, 60, 480, 560), 3 = c(100, 80, 500, 590) ) # 提取整页文本并按页拆分 full_text <- extract_text(pdf) page_texts <- str_split(full_text, "\f")[[1]] # \f为PDF换页符 # 逐页处理表格插入 for (page_num in seq_along(page_texts)) { if (as.character(page_num) %in% names(table_areas)) { # 提取该页表格并转为制表符分隔的文本 table_data <- extract_tables(pdf, pages = page_num, area = table_areas[[as.character(page_num)]])[[1]] table_text <- paste(apply(table_data, 1, paste, collapse = "\t"), collapse = "\n") # 示例:通过表格前的标志性文本定位插入点 marker <- "以下为统计表格:" insert_pos <- str_locate(page_texts[page_num], marker)[2] if (!is.na(insert_pos)) { page_texts[page_num] <- str_replace( page_texts[page_num], paste0("(", marker, ")"), paste0("\\1\n\n", table_text, "\n\n") ) } } } # 合并处理后的文本并保存 final_text <- paste(page_texts, collapse = "\n\n") writeLines(final_text, gsub("\\.pdf$", "_processed.txt", pdf)) }
2. 基于pdftools的位置元数据拼接法
利用pdftools提取PDF中所有元素的位置信息,将文本块和表格按页面坐标排序后重新拼接,自动保持原页面布局:
- 提取文本块的坐标和内容,同时提取表格的边界框和内容
- 按页码、y轴坐标(PDF原点在左下角,需倒序排序)将文本块和表格重新排列,自动实现表格在原位置的插入
示例代码:
library(pdftools) library(tabulizer) library(dplyr) process_single_pdf <- function(pdf_path) { # 提取每页的文本块及位置信息 text_blocks <- pdf_data(pdf_path) # 提取表格数据及位置边界框 tables <- extract_tables(pdf_path, output = "data.frame") table_locations <- extract_tables(pdf_path, output = "locations")[[1]] # 整理文本块为统一数据格式 text_df <- bind_rows(lapply(text_blocks, function(page) { page %>% mutate(type = "text", page = unique(page$page)) %>% select(page, x, y, text, type) })) # 整理表格为统一数据格式,包含格式化后的表格文本 table_df <- bind_rows(lapply(seq_along(tables), function(i) { bbox <- table_locations[i,] table_text <- paste(capture.output(print(tables[[i]])), collapse = "\n") tibble( page = as.integer(bbox["page"]), x = as.numeric(bbox["x1"]), y = as.numeric(bbox["top"]), text = table_text, type = "table" ) })) # 合并文本与表格,按页码、y坐标倒序排序(还原页面从上到下的顺序) combined_df <- bind_rows(text_df, table_df) %>% arrange(page, desc(y)) # 拼接内容,同一行的文本合并,表格单独换行分隔 final_content <- "" current_page <- 1 prev_y <- NULL for (row in split(combined_df, 1:nrow(combined_df))) { if (row$page != current_page) { final_content <- paste0(final_content, "\n\n--- 第", row$page, "页 ---\n\n") current_page <- row$page prev_y <- NULL } if (row$type == "table") { final_content <- paste0(final_content, "\n\n", row$text, "\n\n") } else { # 坐标相近的文本视为同一行,合并内容 if (is.null(prev_y) || abs(row$y - prev_y) > 5) { final_content <- paste0(final_content, "\n", row$text) prev_y <- row$y } else { final_content <- paste0(final_content, " ", row$text) } } } return(final_content) } # 批量处理PDF pdf_files <- list.files(pattern = "\\.pdf$") for (pdf in pdf_files) { processed_content <- process_single_pdf(pdf) writeLines(processed_content, gsub("\\.pdf$", "_processed.txt", pdf)) }
3. 基于Camelot的流式提取法(Python)
Camelot的stream模式可保留表格的页面布局信息,结合PyPDF2提取的文本,直接将表格插入到对应位置:
- 用Camelot提取表格并保留边界框,同时提取整页文本
- 根据表格的页面位置,将表格的markdown格式文本插入到对应页面的文本中
示例代码:
import camelot import PyPDF2 import os def process_pdf(pdf_path): # 提取所有页面的表格(stream模式保留位置) tables = camelot.read_pdf(pdf_path, flavor='stream', pages='all') # 提取整页文本 reader = PyPDF2.PdfReader(pdf_path) page_texts = [page.extract_text() for page in reader.pages] final_content = [] for page_num, text in enumerate(page_texts, 1): # 筛选当前页的表格 page_tables = [tbl for tbl in tables if tbl.page == page_num] for tbl in page_tables: # 将表格转为markdown格式 table_markdown = tbl.df.to_markdown() # 示例:根据表格的y坐标估算插入位置,这里简化为在文本末尾添加 # 实际可通过文本行的位置匹配更精准的插入点 text += f"\n\n{table_markdown}\n\n" final_content.append(f"--- 第{page_num}页 ---\n\n{text}") return "\n\n".join(final_content) # 批量处理文件夹内的PDF pdf_files = [f for f in os.listdir('.') if f.endswith('.pdf')] for pdf in pdf_files: content = process_pdf(pdf) with open(pdf.replace('.pdf', '_processed.txt'), 'w', encoding='utf-8') as f: f.write(content)
内容的提问来源于stack exchange,提问作者user760900
相关产品推荐
相关产品推荐

