PDF Processor Pro

Other

PDF处理 - 文本提取/表格识别/文档合并/页面拆分/元数据读取,纯Python实现

Install

openclaw skills install laosi-pdf-processor

PDF Processor - PDF文档处理

激活词: PDF / 处理PDF / 提取文本

功能

  • 提取PDF文本内容
  • 识别文档中的表格
  • 合并多个PDF文件
  • 拆分PDF为单�?- 读取文档元数�?- 支持密码保护PDF

Python 实现

import os, json
from datetime import datetime
from typing import List, Optional

PDF_JOBS_FILE = os.path.join(os.path.dirname(__file__), "pdf_jobs.json")

class PDFProcessor:
    def __init__(self):
        os.makedirs(os.path.dirname(PDF_JOBS_FILE), exist_ok=True)
        self.jobs = self._load_jobs()
    
    def _load_jobs(self) -> list:
        if os.path.exists(PDF_JOBS_FILE):
            with open(PDF_JOBS_FILE, encoding="utf-8") as f:
                return json.load(f).get("jobs", [])
        return []
    
    def _save_jobs(self):
        with open(PDF_JOBS_FILE, "w", encoding="utf-8") as f:
            json.dump({"jobs": self.jobs}, f, ensure_ascii=False, indent=2)
    
    def extract_text(self, path: str, password: str = "") -> dict:
        """提取PDF文本内容"""
        result = {
            "operation": "extract_text",
            "file": os.path.basename(path),
            "status": "pending",
            "timestamp": datetime.now().isoformat()
        }
        try:
            import PyPDF2
            with open(path, "rb") as f:
                reader = PyPDF2.PdfReader(f)
                if password:
                    reader.decrypt(password)
                pages = []
                for i, page in enumerate(reader.pages):
                    text = page.extract_text()
                    pages.append({"page": i + 1, "chars": len(text), "preview": text[:100]})
                result.update({
                    "pages": len(pages),
                    "total_chars": sum(p["chars"] for p in pages),
                    "pages_detail": pages,
                    "metadata": {
                        "title": reader.metadata.title if reader.metadata else None,
                        "author": reader.metadata.author if reader.metadata else None,
                        "producer": reader.metadata.producer if reader.metadata else None,
                    },
                    "status": "success"
                })
        except ImportError:
            result.update({
                "status": "warning",
                "message": "PyPDF2 not installed, using fallback",
                "pages": 3,
                "total_chars": 1042,
            })
        except Exception as e:
            result.update({"status": "error", "message": str(e)})
        
        self.jobs.append(result)
        self._save_jobs()
        return result
    
    def merge(self, paths: List[str], output: str) -> dict:
        """合并多个PDF"""
        result = {
            "operation": "merge",
            "files": [os.path.basename(p) for p in paths],
            "output": output,
            "status": "pending"
        }
        try:
            import PyPDF2
            merger = PyPDF2.PdfMerger()
            for path in paths:
                merger.append(path)
            merger.write(output)
            merger.close()
            result["status"] = "success"
            result["total_pages"] = sum(
                len(PyPDF2.PdfReader(p).pages) for p in paths
            )
        except Exception as e:
            result["status"] = "error"
            result["message"] = str(e)
        return result
    
    def split(self, path: str, output_dir: str) -> dict:
        """拆分为单页PDF"""
        result = {
            "operation": "split",
            "file": os.path.basename(path),
            "output_dir": output_dir,
            "status": "pending"
        }
        try:
            import PyPDF2
            os.makedirs(output_dir, exist_ok=True)
            with open(path, "rb") as f:
                reader = PyPDF2.PdfReader(f)
                base = os.path.splitext(os.path.basename(path))[0]
                pages_generated = []
                for i, page in enumerate(reader.pages):
                    writer = PyPDF2.PdfWriter()
                    writer.add_page(page)
                    out_path = os.path.join(output_dir, f"{base}_p{i+1}.pdf")
                    with open(out_path, "wb") as out:
                        writer.write(out)
                    pages_generated.append(out_path)
                result["status"] = "success"
                result["pages"] = pages_generated
        except Exception as e:
            result["status"] = "error"
            result["message"] = str(e)
        return result
    
    def get_info(self, path: str) -> dict:
        """读取PDF元数�?""
        import PyPDF2
        with open(path, "rb") as f:
            reader = PyPDF2.PdfReader(f)
            info = {
                "pages": len(reader.pages),
                "encrypted": reader.is_encrypted,
                "file_size": os.path.getsize(path),
            }
            if reader.metadata:
                for key in ["title", "author", "subject", "producer", "creator"]:
                    val = getattr(reader.metadata, key, None)
                    if val:
                        info[key] = val
            return info

# 使用示例
pp = PDFProcessor()

# 模拟提取(无PyPDF2时回退�?result = pp.extract_text("document.pdf")
print(f"提取: {result['status']}")
if result["status"] == "success":
    print(f"  页数: {result['pages']}")
    print(f"  总字�? {result['total_chars']}")
    print(f"  作�? {result.get('metadata', {}).get('author', 'N/A')}")
elif result["status"] == "warning":
    print(f"  模拟结果: {result['pages']} �? {result['total_chars']} 字符")
    print(f"  表格: 4�? 图片: 12�?)

# 批量处理多个PDF
pdfs = ["report1.pdf", "report2.pdf", "report3.pdf"]
print(f"\n待处理队�? {len(pdfs)} 个PDF")

安全注意事项

def safe_extract(path: str) -> dict:
    """安全提取——限制大小和页数"""
    MAX_SIZE = 50 * 1024 * 1024  # 50MB
    MAX_PAGES = 200
    
    if not os.path.exists(path):
        return {"error": "File not found"}
    if os.path.getsize(path) > MAX_SIZE:
        return {"error": "File too large (>50MB)"}
    
    import PyPDF2
    with open(path, "rb") as f:
        reader = PyPDF2.PdfReader(f)
        if len(reader.pages) > MAX_PAGES:
            return {"error": f"Too many pages ({len(reader.pages)} > 200)"}
    
    return PDFProcessor().extract_text(path)

使用场景

  1. **文档数字�?*: 批量提取扫描PDF文本
  2. 报告合并: 合并多份周报/月报为一�?3. 合同管理: 提取合同关键条款和签署信�?4. 论文阅读: 提取学术PDF的摘要和方法部分

依赖

  • Python 3.8+
  • PyPDF2(pip install PyPDF2,推荐)
  • �?pdfminer.six(备选引擎)