如何使用Golang像Camelot Python一样从PDF中提取表格

如何使用Golang像Camelot Python一样从PDF中提取表格 如何像 Python 的 Camelot 库一样从 PDF 中提取表格。

2 回复

嗨,@packs,经过一番搜索,我未能找到一个Go社区普遍喜欢的Go PDF库;我建议你通过调用Python子进程、嵌入libpython或其他一些机制,来配合Camelot获取你所需的功能。

也许这里的其他人会有更好的建议 ¯\_(ツ)_/¯

更多关于如何使用Golang像Camelot Python一样从PDF中提取表格的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中从PDF提取表格可以使用以下两种主要方法:

1. 使用unipdf库(推荐)

package main

import (
    "fmt"
    "log"
    "github.com/unidoc/unipdf/v3/extractor"
    "github.com/unidoc/unipdf/v3/model"
)

func extractTablesFromPDF(pdfPath string) {
    // 打开PDF文件
    f, err := os.Open(pdfPath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    // 创建PDF阅读器
    pdfReader, err := model.NewPdfReader(f)
    if err != nil {
        log.Fatal(err)
    }

    // 获取页数
    numPages, err := pdfReader.GetNumPages()
    if err != nil {
        log.Fatal(err)
    }

    // 遍历每一页
    for pageNum := 1; pageNum <= numPages; pageNum++ {
        page, err := pdfReader.GetPage(pageNum)
        if err != nil {
            log.Fatal(err)
        }

        // 创建提取器
        ex, err := extractor.New(page)
        if err != nil {
            log.Fatal(err)
        }

        // 提取文本
        text, err := ex.ExtractText()
        if err != nil {
            log.Fatal(err)
        }

        // 这里需要实现表格检测逻辑
        // unipdf不直接提供表格提取,需要基于文本位置分析
        fmt.Printf("Page %d:\n%s\n", pageNum, text)
    }
}

2. 使用gofpdf和自定义表格检测

package main

import (
    "fmt"
    "strings"
    "github.com/jung-kurt/gofpdf"
)

type TableCell struct {
    Text     string
    X, Y     float64
    Width    float64
    Height   float64
}

func detectTablesFromText(text string, positions []TableCell) [][]string {
    // 简单的表格检测算法示例
    var tables [][]string
    
    // 按行分割
    lines := strings.Split(text, "\n")
    
    // 检测表格特征(如对齐的列、分隔线等)
    for i, line := range lines {
        // 检查是否为表格行(包含多个制表符或连续空格)
        if strings.Count(line, "  ") > 2 || strings.Contains(line, "\t") {
            // 分割单元格
            cells := strings.Fields(line)
            tables = append(tables, cells)
            fmt.Printf("检测到表格行 %d: %v\n", i+1, cells)
        }
    }
    
    return tables
}

// 使用正则表达式提取表格数据
func extractWithRegex(text string) {
    // 匹配表格模式
    re := regexp.MustCompile(`(\S+\s+){2,}\S+`)
    matches := re.FindAllString(text, -1)
    
    for _, match := range matches {
        fmt.Printf("疑似表格行: %s\n", match)
    }
}

3. 结合外部工具(Tabula)

package main

import (
    "os/exec"
    "encoding/csv"
    "io"
    "fmt"
)

func extractWithTabula(pdfPath, outputPath string) error {
    // 调用Tabula命令行工具
    cmd := exec.Command("java", "-jar", "tabula.jar", 
        pdfPath, 
        "--pages", "all",
        "--format", "CSV",
        "--output", outputPath)
    
    output, err := cmd.CombinedOutput()
    if err != nil {
        return fmt.Errorf("tabula执行失败: %v\n输出: %s", err, output)
    }
    
    // 读取CSV结果
    file, err := os.Open(outputPath)
    if err != nil {
        return err
    }
    defer file.Close()
    
    reader := csv.NewReader(file)
    for {
        record, err := reader.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }
        fmt.Printf("表格行: %v\n", record)
    }
    
    return nil
}

4. 完整的表格提取示例

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

type PDFTableExtractor struct {
    MinColSpacing float64
    MinRowSpacing float64
}

func (e *PDFTableExtractor) Extract(pdfPath string) ([][][]string, error) {
    // 读取PDF文本内容
    text, err := e.extractTextFromPDF(pdfPath)
    if err != nil {
        return nil, err
    }
    
    // 检测表格结构
    tables := e.detectTables(text)
    
    return tables, nil
}

func (e *PDFTableExtractor) detectTables(text string) [][][]string {
    var tables [][][]string
    var currentTable [][]string
    
    scanner := bufio.NewScanner(strings.NewReader(text))
    inTable := false
    
    for scanner.Scan() {
        line := scanner.Text()
        
        // 简单的表格检测逻辑
        if e.isTableRow(line) {
            if !inTable {
                inTable = true
                currentTable = [][]string{}
            }
            cells := e.parseTableRow(line)
            currentTable = append(currentTable, cells)
        } else {
            if inTable && len(currentTable) > 0 {
                tables = append(tables, currentTable)
                inTable = false
            }
        }
    }
    
    return tables
}

func (e *PDFTableExtractor) isTableRow(line string) bool {
    // 检测是否为表格行的启发式规则
    trimmed := strings.TrimSpace(line)
    if len(trimmed) == 0 {
        return false
    }
    
    // 检查是否有多个连续空格或制表符
    spaceCount := strings.Count(line, "  ")
    tabCount := strings.Count(line, "\t")
    
    return spaceCount >= 2 || tabCount >= 1
}

func (e *PDFTableExtractor) parseTableRow(line string) []string {
    // 使用正则表达式或自定义逻辑分割单元格
    // 这里使用简单的空格分割
    return strings.Fields(line)
}

Golang目前没有直接等同于Camelot的库,上述方法提供了不同的实现途径。对于复杂的PDF表格,建议结合多种方法或考虑使用外部工具。

回到顶部