写了个脚本将pdf转markdown

看到有人需要将扫描pdf文档转markdown,想起之前写的一个小工具。
这个脚本是为了将pdf转成markdown,只需要申请一个智谱的api key,并填到config里,使用的模型是4v flash,免费的,所以可以放心使用。
效果如下图:
在这里插入图片描述

脚本里的提示词可以根据个人需要进行修改。以下是原始代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-"""
PDF转Markdown自动化系统
功能:监控input/目录下的PDF文件,转换为Markdown格式
作者:您的专属程序员
日期:2025-04-03
版本:2.0.0
"""import base64
import logging
import time
import json
import os
import fitz  # PyMuPDF
from pathlib import Path
from typing import Optional, Dict, Any, List, Generator
from zhipuai import ZhipuAI
from zhipuai.core._errors import ZhipuAIError# 配置日志系统
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('pdf2md.log'),logging.StreamHandler()]
)
logger = logging.getLogger(__name__)class GLM4VTester:"""GLM-4V 模型测试工具类"""def __init__(self, api_key: str, model_name: str = "glm-4v-flash"):self.client = ZhipuAI(api_key=api_key)self.model_name = model_nameself.total_tokens = 0self.total_requests = 0self.total_time = 0.0def analyze_image(self, image_path: str, prompt: str = "你是一个OCR助手,请把图中内容按原有格式输出出来,如果有公式则输出为LaTeX") -> Dict[str, Any]:"""分析图片内容:param image_path: 图片路径:param prompt: 提示词:return: API响应结果"""start_time = time.time()# 读取图片并转为base64with open(image_path, "rb") as image_file:base64_image = base64.b64encode(image_file.read()).decode('utf-8')# 调用APIresponse = self.client.chat.completions.create(model=self.model_name,messages=[{"role": "user", "content": [{"type": "text", "text": prompt},{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}]}])# 更新统计信息elapsed_time = time.time() - start_timeself.total_requests += 1self.total_time += elapsed_timeif hasattr(response, 'usage') and response.usage:self.total_tokens += response.usage.total_tokenslogger.info(f"API请求完成,耗时: {elapsed_time:.2f}秒")return {"response": response, "time": elapsed_time}def generate_markdown_report(self, image_path: str, result: Dict[str, Any], output_path: str) -> str:"""生成Markdown格式的分析报告:param image_path: 原始图片路径:param result: API响应结果:param output_path: 输出文件路径:return: 生成的Markdown内容"""response = result["response"]elapsed_time = result["time"]# 提取文本内容content = response.choices[0].message.content# 生成Markdownmarkdown = f"""# 图像分析报告## 原始图像
![原始图像]({os.path.abspath(image_path)})## 分析结果
{content}## 统计信息
- 处理时间: {elapsed_time:.2f}秒
- 总请求数: {self.total_requests}
- 总Token数: {self.total_tokens}
- 平均响应时间: {self.total_time/self.total_requests:.2f}秒
"""# 保存到文件with open(output_path, 'w', encoding='utf-8') as f:f.write(markdown)return markdownclass ProcessingConfig:"""PDF处理配置类"""def __init__(self, config_dict: Dict[str, Any]):self.api_key = config_dict.get("api_key", "")self.input_dir = config_dict.get("input_dir", "input")self.output_dir = config_dict.get("output_dir", "output")self.model = config_dict.get("model", "glm-4v-flash")self.dpi = config_dict.get("dpi", 600)self.api_interval = config_dict.get("api_interval", 3.0)self.max_retries = config_dict.get("max_retries", 3)self.retry_backoff = config_dict.get("retry_backoff", 0.5)self.prompt = config_dict.get("prompt", "你是一个OCR助手,请把图中内容按原有格式输出出来,不要翻译,如果有公式则输出为LaTeX,图片忽略不管")class PDFProcessor:"""PDF处理核心类"""def __init__(self, config: ProcessingConfig, ocr_engine: GLM4VTester):"""初始化PDF处理器:param config: 处理配置:param ocr_engine: OCR引擎实例"""self.config = configself.ocr_engine = ocr_engineself.temp_dir = "temp_images"os.makedirs(self.temp_dir, exist_ok=True)def _convert_page_to_image(self, page, page_num: int) -> str:"""将PDF页面转换为图片:param page: PyMuPDF页面对象:param page_num: 页码:return: 图片文件路径"""pix = page.get_pixmap(dpi=self.config.dpi)img_path = os.path.join(self.temp_dir, f"page_{page_num}.png")pix.save(img_path)return img_pathdef _safe_api_call(self, image_path: str) -> str:"""安全的API调用方法,包含重试机制:param image_path: 图片路径:return: OCR结果文本"""retries = 0while retries <= self.config.max_retries:try:time.sleep(self.config.api_interval + (retries * self.config.retry_backoff))result = self.ocr_engine.analyze_image(image_path, self.config.prompt)return result["response"].choices[0].message.contentexcept ZhipuAIError as e:logger.warning(f"API调用失败(重试 {retries}/{self.config.max_retries}): {e}")retries += 1raise Exception(f"API调用失败,超过最大重试次数 {self.config.max_retries}")def _format_page(self, content: str, page_num: int) -> str:"""格式化单页内容为Markdown:param content: OCR原始内容:param page_num: 页码:return: 格式化后的Markdown"""return f"## 第 {page_num} 页\n\n{content}\n\n---\n"def process_pdf(self, pdf_path: str) -> Generator[str, None, None]:"""处理单个PDF文件:param pdf_path: PDF文件路径:return: 生成Markdown内容"""logger.info(f"开始处理PDF文件: {pdf_path}")with fitz.open(pdf_path) as doc:for page_num, page in enumerate(doc, start=1):try:# 转换为图片img_path = self._convert_page_to_image(page, page_num)# OCR识别content = self._safe_api_call(img_path)# 格式化输出yield self._format_page(content, page_num)# 清理临时图片os.remove(img_path)except Exception as e:logger.error(f"处理第{page_num}页时出错: {e}")yield f"## 第 {page_num} 页\n\n[处理错误: {str(e)}]\n\n"logger.info(f"完成PDF处理: {pdf_path}")def process_single_image(config: ProcessingConfig, image_path: str, output_path: str):"""处理单张图片模式"""try:tester = GLM4VTester(api_key=config.api_key, model_name=config.model)logger.info(f"开始分析文件: {image_path}")result = tester.analyze_image(image_path, config.prompt)markdown = tester.generate_markdown_report(image_path, result, output_path)print(f"\n分析完成! 结果已保存到: {output_path}\n")return Trueexcept Exception as e:logger.error(f"文件处理失败: {e}")return Falsedef process_pdf_file(config: ProcessingConfig, pdf_path: str, output_path: str):"""处理PDF文件模式"""try:tester = GLM4VTester(api_key=config.api_key, model_name=config.model)processor = PDFProcessor(config, tester)with open(output_path, 'w', encoding='utf-8') as f:for page_content in processor.process_pdf(pdf_path):f.write(page_content)logger.info(f"PDF转换完成! 结果已保存到: {output_path}")return Trueexcept Exception as e:logger.error(f"PDF处理失败: {e}")return Falsedef batch_process_pdfs(config: ProcessingConfig):"""批量处理input/目录下的PDF文件"""tester = GLM4VTester(api_key=config.api_key, model_name=config.model)processor = PDFProcessor(config, tester)input_dir = config.input_diroutput_dir = config.output_diros.makedirs(input_dir, exist_ok=True)os.makedirs(output_dir, exist_ok=True)processed_files = set()if os.path.exists("processed.log"):with open("processed.log", "r") as f:processed_files = set(f.read().splitlines())while True:try:for filename in os.listdir(input_dir):if filename.lower().endswith('.pdf') and filename not in processed_files:pdf_path = os.path.join(input_dir, filename)output_path = os.path.join(output_dir, f"{os.path.splitext(filename)[0]}.md")logger.info(f"开始处理: {filename}")with open(output_path, 'w', encoding='utf-8') as f:for page_content in processor.process_pdf(pdf_path):f.write(page_content)# 记录已处理文件with open("processed.log", "a") as f:f.write(f"{filename}\n")processed_files.add(filename)logger.info(f"处理完成: {filename} -> {output_path}")time.sleep(10)  # 每10秒检查一次新文件except KeyboardInterrupt:logger.info("收到中断信号,停止处理")breakexcept Exception as e:logger.error(f"批量处理出错: {e}")time.sleep(30)  # 出错后等待30秒再重试def load_config():"""加载配置文件"""config_path = "config.json"default_config = {"api_key": "","input_dir": "input","output_dir": "output","model": "glm-4v-flash","dpi": 600,"api_interval": 3.0,"max_retries": 3,"retry_backoff": 0.5,"prompt": "你是一个OCR助手,请把图中内容按原有格式输出出来,如果有公式则输出为LaTeX,图片请用《》描述"}try:with open(config_path, 'r') as f:config = json.load(f)# 合并配置,优先使用配置文件中的值return {**default_config, **config}except FileNotFoundError:logger.warning(f"配置文件 {config_path} 未找到,使用默认配置")# 创建默认配置文件with open(config_path, 'w') as f:json.dump(default_config, f, indent=2)return default_configexcept json.JSONDecodeError as e:logger.error(f"配置文件格式错误: {e}")return default_configdef main():"""主函数"""config_dict = load_config()config = ProcessingConfig(config_dict)# 检查API密钥是否设置if not config.api_key:logger.error("API密钥未设置,请在config.json中设置api_key")exit(1)# 确保目录存在os.makedirs(config.input_dir, exist_ok=True)os.makedirs(config.output_dir, exist_ok=True)# 直接启动批处理模式logger.info(f"启动批处理模式,监控目录: {config.input_dir}")batch_process_pdfs(config)if __name__ == '__main__':main()

自己修改一下config里面的智谱api key:

{"api_key": "智谱的api_key","input_dir": "input","output_dir": "output", "model": "glm-4v-flash","dpi": 600,"api_interval": 3.0,"max_retries": 3,"retry_backoff": 0.5
}

缺点是由于是ocr,所以无法提取图片,有需要图片的用minerU或者marker,我试了marker,效果还可以的。

🔥运维干货分享

  • 软考高级系统架构设计师备考学习资料
  • 软考中级数据库系统工程师学习资料
  • 软考高级网络规划设计师备考学习资料
  • Kubernetes CKA认证学习资料分享
  • AI大模型学习资料合集
  • 免费文档翻译工具(支持word、pdf、ppt、excel)
  • PuTTY中文版安装包
  • MobaXterm中文版安装包
  • pinginfoview网络诊断工具中文版
  • Xshell、Xsftp、Xmanager中文版安装包
  • Typora简单易用的Markdown编辑器
  • Window进程监控工具,能自动重启进程和卡死检测
  • Spring 源码学习资料分享
  • 毕业设计高质量毕业答辩 PPT 模板分享
  • IT行业工程师面试简历模板分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS--图片链接水平居中展示的方法

原文网址&#xff1a;CSS--图片链接居中展示的方法-CSDN博客 简介 本文介绍CSS图片链接水平居中展示的方法。 图片链接 问题复现 源码 <html xml:lang"cn" lang"cn"><head><meta http-equiv"Content-Type" content"te…

工具分享:通过滑块拉取CAN报文信号数值自动发送报文

0. 概述 CAN报文发送工具使用wxpython进行开发,配套Excel模板可以通过修改Excel自定义界面展示的信号名称和信号的属性;同时,工具支持导入现场采集的报文数据自动按照配套Excel模板定义的报文发送周期进行模拟发送。 由于是我好几年前开发的作品,一些开发细节也记得不是很…

【Python】os模块

os 模块是 Python 标准库中用于与操作系统交互的核心模块&#xff0c;提供了许多操作文件和目 录的函数。 1. 基本介绍 os 模块提供了以下主要功能&#xff1a; 文件和目录操作路径操作进程管理环境变量访问 import os2. 常用功能分类 2.1 文件和目录操作 函数/方法描述o…

ai agent(智能体)开发 python3基础11: java 调用python waitfor卡死,导致深入理解操作系统进程模型和IPC机制

java 调用python waitfor 卡死 导致浏览器无法自动关闭&#xff0c;java &#xff0c;python双发无限等待 根源在于还是没有理解 进程之间标准输入输出到底是什么含义 系统进程与跨语言调用的核心机制 在跨语言调用&#xff08;如Java调用Python&#xff09;时&#xff0c;理…

Kubernetes(k8s)学习笔记(九)--搭建多租户系统

K8s 多租户管理 多租户是指在同一集群中隔离多个用户或团队&#xff0c;以避免他们之间的资源冲突和误操作。在K8s中&#xff0c;多租户管理的核心目标是在保证安全性的同时&#xff0c;提高资源利用率和运营效率。 在K8s中&#xff0c;该操作可以通过命名空间&#xff08;Nam…

同质化的旅游内核

湘西凤凰古城、北京非常有文艺氛围的方家胡同都在被改造翻新为现代的其他城市范式式的样式。 什么意思呢&#xff1f;很多古城的老房子&#xff0c;从外面看&#xff0c;很古老、很漂亮&#xff0c;但是进去以后&#xff0c;完全不是那么回事&#xff0c;整座房子已经被完全掏…

鸿蒙开发——3.ArkTS声明式开发:构建第一个ArkTS应用

鸿蒙开发——3.ArkTS声明式开发:构建第一个ArkTS应用 一、创建ArkTS工程二、ArkTS工程目录结构&#xff08;Stage模型&#xff09;三、构建第一个页面四、构建第二个页面五、实现页面之间的跳转六、模拟器运行 一、创建ArkTS工程 1、若首次打开DevEco Studio&#xff0c;请点击…

C语言初阶:数组

目录 0.数组要讲的知识点 1.一维数组的创建和初始化 1.1 数组的创建&#xff1a; 1.2数组实例&#xff1a; 1.3 数组的初识化&#xff1a; 例子&#xff1a; 2.一维数组的使用 例子&#xff1a; 总结&#xff1a; 3.一维数组在内存中的存储 4.二维数组的创建和初始化 4.…

UE5 Daz头发转Blender曲线再导出ABC成为Groom

先安装Daz to Blender Import插件 【神器】 --DAZ一键导入blender插件的详细安装和使用&#xff0c;自带骨骼绑定和控制器&#xff0c;多姿势动画&#xff0c;Importer桥接插件_哔哩哔哩_bilibili 然后安装DAZHairConverter插件 一分钟将DAZ头发转化成Blender粒子毛发_哔哩哔…

浅聊find_package命令的搜索模式(Search Modes)

背景 find_package应该算是我们使用最多的cmake命令了。但是它是如何找到上游库的.cmake文件的&#xff1f; 根据官方文档&#xff0c;整理下find_package涉及到的搜索模式。 搜索模式 find_package涉及到的搜索模式有两种&#xff1a;模块模式(Module mode)和配置模式(Conf…

什么是先验?(CVPR25)Detail-Preserving Latent Diffusion for Stable Shadow Removal论文阅读

文章目录 先验&#xff08;Prior&#xff09;是什么&#xff1f;1. 先验的数学定义2. 先验在深度生成模型中的角色3. 为什么需要先验&#xff1f;4. 先验的常见类型5. 如何选择或构造先验&#xff1f;6. 小结 先验&#xff08;Prior&#xff09;是什么&#xff1f; 在概率统计…

【视觉基础模型-SAM系列-2】SAM2: Segment Anything in Images and Videos

论文链接&#xff1a;SAM 2: Segment Anything in Images and Videos 代码链接&#xff1a;https://github.com/facebookresearch/sam2?tabreadme-ov-file 作者&#xff1a;Nikhila Ravi, Valentin Gabeur, Yuan-Ting Hu, Ronghang Hu, Chaitanya Ryali, Tengyu Ma, Haitham…

OpenShift AI - 模型注册管理

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.18 OpenShift AI 2.19 的环境中验证 文章目录 启用模型注册管理功能安装管理数据库启用模型注册功能 注册模型部署模型归档模型归档模型和模型版本恢复归档模型 模型注册表访问权限管理参考…

【背包dp----01背包】例题三------(标准的01背包+变种01背包1【恰好装满背包体积 产生的 最大价值】)

【模板】01背包 题目链接 题目描述 : 输入描述: 输出描述: 示例1 输入 3 5 2 10 4 5 1 4输出 14 9说明 装第一个和第三个物品时总价值最大&#xff0c;但是装第二个和第三个物品可以使得背包恰好装满且总价值最大。 示例2 输入 3 8 12 6 11 8 6 8输出 8 0说明 装第三个物…

Node.js 的 child_process 模块详解

Node.js 的 child_process 模块提供了创建子进程的能力,使 Node.js 应用能够执行系统命令、运行其他程序或脚本。这个模块非常强大,可以帮助我们实现很多复杂的功能。 1. exec - 执行 shell 命令 exec 方法用于执行 shell 命令,并缓冲任何产生的输出。 特点 创建 shell 来…

进程与线程详细介绍

目录 一 进程概念 二 进程的组成 2.1 PCB 2.2 数据段 2.3 程序段 三 进程的五大特点 四 进程的创建与销毁 五 线程概念 六 线程特征 七 进程与线程的区别与联系 区别 联系 一 进程概念 进程是程序的一次执行过程&#xff0c;是操作系统进行资源分配和调度的基本单位…

如何在服务器后台运行Python脚本,并配置虚拟环境与GPU支持

使用Conda虚拟环境在服务器后台运行Python脚本&#xff0c;并检查GPU分配 在服务器开发环境中&#xff0c;我们需要确保Python脚本运行在指定的Conda虚拟环境中&#xff0c;并且确认是否正确分配了GPU资源。本文将通过一个完整的start.sh脚本&#xff0c;完成以下功能&#xff…

前端取经路——工程化渡劫:八戒的构建之道

大家好,我是老十三,一名前端开发工程师。前端工程化就像八戒的钉耙,看似简单却能降妖除魔。在本文中,我将带你探索前端工程化的九大难题,从模块化组织到CI/CD流程,从代码规范到自动化测试,揭示这些工具背后的核心原理。无论你是初学者还是资深工程师,这些构建之道都能帮…

Ubuntu 安装 Keepalived

Keepalived 是什么 Keepalived 是一个用于实现高可用性&#xff08;High Availability, HA&#xff09;的服务&#xff0c;是一款基于 VRRP 协议的高可用软件&#xff0c;常用于主备切换和虚拟IP漂移&#xff0c;在服务故障时自动实现故障转移。 Keepalived 的核心功能 功能说…

DHCP理解

文章目录 DHCP理解DHCP的核心作用DHCP默认端口DHCP的工作原理&#xff08;4个步骤&#xff09;图示说明&#xff08;含中继代理&#xff09;DHCP Discover&#xff08;客户端发现阶段&#xff09;DHCP Offer&#xff08;服务器提供阶段&#xff09;DHCP Request&#xff08;客户…