本文介绍了一个私有化pdf文档问答系统的实现方案,重点解决了中文pdf解析和加密文件处理问题。系统采用pdfplumber替代pypdf2进行文本提取,优化了对中文pdf的支持,并新增了加密pdf的密码输入和解密功能。系统提供用户友好的web界面,包含文档上传、问题输入、回答生成和内容预览等功能,支持文本型pdf和加密pdf(需输入密码)的处理。
完整代码
# encoding: utf-8
# 版权所有 2024 ©涂聚文有限公司
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:
# author : geovindu,geovin du 涂聚文.
# ide : pycharm 2023.1 python 3.11
# os : windows 10
# database : mysql 9.0 sql server 2019, poostgresql 17.0 oracle 11g
# datetime : 2026/02/05 22:16
# user : geovindu
# product : pycharm
# project : pyoracledemo
# file : main.py
# explain : 学习
'''
https://github.com/gradio-app/gradio
https://modelscope.cn/models/zhipuai/glm-ocr
https://www.gradio.app/custom-components/gallery
pip install gradio -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install streamlit -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pypdf2 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pycryptodome -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install pdfplumber -i https://pypi.tuna.tsinghua.edu.cn/simple
'''
import gradio as gr
import os
import tempfile
import streamlit as st
#from pypdf2 import pdfreader, errors as pypdf2errors
from typing import optional
from typing import list
import pdfplumber # 替换pypdf2
import traceback
class documentqasystem:
"""私有化pdf文档问答系统(修复is_encrypted属性错误)"""
def __init__(self):
st.set_page_config(
page_title="私有化文档问答系统",
layout="wide",
initial_sidebar_state="collapsed"
)
if "doc_text" not in st.session_state:
st.session_state.doc_text = ""
self._set_custom_style()
def _set_custom_style(self):
st.markdown("""
<style>
.stbutton>button {width: 100%; margin-top: 10px;}
.sttextinput>div>div>input {padding: 8px;}
.doc-preview {max-height: 400px; overflow-y: auto; border: 1px solid #eee; padding: 10px; border-radius: 5px;}
</style>
""", unsafe_allow_html=true)
def _extract_pdf_text(self, pdf_path: str) -> str:
"""
修复:移除错误的is_encrypted判断,适配pdfplumber加密pdf处理逻辑
核心:pdfplumber打开加密pdf会抛异常,捕获后引导输入密码
"""
full_text = ""
try:
# 第一步:尝试直接打开pdf(非加密pdf直接处理)
try:
with pdfplumber.open(pdf_path) as pdf:
# 逐页提取文本(中文友好)
for page_num, page in enumerate(pdf.pages, 1):
text = page.extract_text()
if text:
full_text += f"\n=== 第 {page_num} 页 ===\n{text}\n"
# 第二步:捕获加密pdf异常,引导输入密码
except pdfplumber.utils.pdfencryptionerror:
st.warning("检测到该pdf文件已加密,请输入解密密码")
pdf_password = st.text_input(
"pdf解密密码",
type="password",
key="pdf_pwd",
help="输入密码后会自动重新解析"
)
# 有密码时尝试解密打开
if pdf_password:
try:
with pdfplumber.open(pdf_path, password=pdf_password) as pdf:
for page_num, page in enumerate(pdf.pages, 1):
text = page.extract_text()
if text:
full_text += f"\n=== 第 {page_num} 页 ===\n{text}\n"
except exception:
st.error("密码错误!请输入正确的pdf解密密码")
return ""
else:
st.info("请输入密码后重试解析")
return ""
return full_text.strip()
except importerror:
st.error("依赖缺失:请执行 `pip install pdfplumber` 安装解析库")
return ""
except exception as e:
# 打印详细错误(仅调试用,可注释)
st.error(f"pdf解析失败:{str(e)}")
st.debug(f"详细错误信息:{traceback.format_exc()}")
return ""
def _mock_llm_answer(self, question: str, context: str) -> str:
return f"【回答】:针对问题“{question}”,上下文中检索到相关内容:{context[:100]}..."
def render_ui(self):
st.title("📄 文档问答工具(中文pdf优化+加密修复版)")
st.divider()
col1, col2 = st.columns([1, 2])
with col1:
st.subheader("📤 文档上传与提问")
uploaded_file = st.file_uploader("选择pdf文档", type=["pdf"])
if uploaded_file is not none:
with st.spinner("正在解析pdf(中文优化版)..."):
# 临时文件处理
with tempfile.namedtemporaryfile(delete=false, suffix=".pdf") as tmp_file:
tmp_file.write(uploaded_file.read())
tmp_path = tmp_file.name
# 提取文本(核心修复后的方法)
st.session_state.doc_text = self._extract_pdf_text(tmp_path)
# 清理临时文件(增加异常捕获)
try:
os.unlink(tmp_path)
except exception as e:
st.warning(f"临时文件清理失败:{str(e)}(不影响功能)")
if st.session_state.doc_text:
st.success(f"✅ pdf解析成功!提取字符总数:{len(st.session_state.doc_text)}")
# 清空文档按钮
if st.button("🗑️ 清空已上传文档", type="secondary"):
st.session_state.doc_text = ""
st.rerun()
st.divider()
# 用户提问
question = st.text_input(
"💡 请输入你要查询的问题",
placeholder="例如:文档中提到的核心结论是什么?",
disabled=not st.session_state.doc_text
)
# 提交按钮(仅当有文档和问题时可用)
submit_btn = st.button(
"🚀 提交问题生成回答",
type="primary",
disabled=not (question and st.session_state.doc_text)
)
with col2:
st.subheader("📋 结果展示")
# 显示回答
if submit_btn:
with st.spinner("正在基于文档内容生成回答..."):
answer = self._mock_llm_answer(question, st.session_state.doc_text)
st.markdown("### 🎯 回答结果")
st.write(answer)
st.divider()
# 文档内容预览
if st.session_state.doc_text:
st.markdown("### 📄 文档内容预览(前1500字符)")
st.markdown(
f'<div class="doc-preview">{st.session_state.doc_text[:1500]}...</div>',
unsafe_allow_html=true
)
else:
st.info("📌 请先上传pdf文档,支持中文文本型pdf、加密pdf(需输入密码)")
# 底部提示
st.divider()
st.caption("💡 注意:扫描件pdf(纯图片)无法提取文本,需先进行ocr识别;文本型pdf均可正常解析")
def main():
"""程序主入口函数"""
# 实例化问答系统并渲染界面
qa_system = documentqasystem()
qa_system.render_ui()
# press the green button in the gutter to run the script.
if __name__ == '__main__':
print('hello world')
main()
在终端运行:
# 2. 用streamlit专用命令启动(关键!) streamlit run main.py
方法补充
下面是小编整理的python提取pdf内容的其他方法,希望对大家有所帮助
1. python提取指定页文字
import pdfplumber
path = './练习文件/文字.pdf'
# with语句:打开文件不用手动关闭,但要注意缩进
with pdfplumber.open(path) as pdf:
# # 获取首页
first_page = pdf.pages[0]
# 将指定页提取文字
text = first_page.extract_text()
textw = open('./结果文件/1.txt', mode='a', encoding='utf-8')
textw.write(text)2. python提取所有页面文字
import pdfplumber
path = './练习文件/文字.pdf'
with pdfplumber.open(path) as pdf:
for page in pdf.pages:
# 将每页提取文字
text = page.extract_text()
textw = open('./结果文件/2.txt', mode='a', encoding='utf-8')
textw.write(text)3.python从特定pdf页面提取文本
从指定的pdf页面提取文本,大致步骤如下:
- 加载pdf文档。
- 通过页面索引获取指定页面。
- 提取页面的文本内容。
- 将提取的文本保存到文本文件中。
代码:
from spire.pdf.common import *
from spire.pdf import *
# 定义一个从pdf文档指定页面提取文本的函数,参数分别为输入pdf文档的路径,需要提取文本的页面索引(从0开始),存放提取文本的文本文档的路径
def extract_text_from_page(file_path, page_num, output_file):
# 创建pdfdocument类的实例
doc = pdfdocument()
# 加载pdf文档
doc.loadfromfile(file_path)
# 根据页面索引获取特定页面
page = doc.pages[page_num]
# 从该页面提取文本
text = page.extracttext(true)
# 将提取的文本存储到文本文件
with open(output_file, "w", encoding="utf-8") as text_file:
text_file.write(text)
doc.close()
# 调用函数实现从pdf指定页面提取文本
file_path = "测试.pdf"
page_num = 0 # 指定从中提取文本的页码(页码索引从0开始)
output_file = "提取页面文本.txt"
extract_text_from_page(file_path, page_num, output_file)
4.python从特定pdf页面区域提取文本
从指定pdf页面区域提取文本,大致步骤如下:
- 加载pdf文档。
- 通过页面索引获取指定页面。
- 指定需要提取文本内容的矩形区域的坐标、宽度和高度。
- 从页面的指定矩形区域中提取文本。
- 将提取的文本保存到文本文件中。
代码:
from spire.pdf.common import *
from spire.pdf import *
# 定义一个从pdf文档指定页面的指定区域提取文本的函数,参数分别为输入pdf文档的路径,需要提取文本的页面索引(从0开始),区域的x坐标,区域的y坐标,区域的宽度,区域的高度,存放提取文本的文本文件路径
def extract_text_from_page_area(file_path, page_num, x, y, width, height, output_file):
# 创建pdfdocument实例
doc = pdfdocument()
# 加载pdf文档
doc.loadfromfile(file_path)
# 根据页面索引获取特定页面
page = doc.pages[page_num]
# 定义一个矩形来指定文本提取的区域
rectangle = rectanglef(x, y, width, height)
# 从页面的指定矩形区域中提取文本
text = page.extracttext(rectangle)
# 将提取的文本存储到文本文件
with open(output_file, "w", encoding="utf-8") as text_file:
text_file.write(text)
doc.close()
# 调用函数实现从pdf指定页面的指定区域提取文本
file_path = "测试.pdf"
page_num = 0 # 指定从中提取文本的页码(页码索引从0开始)
x = 0.0
y = 180.0
width = 500.0
height = 200.0
output_file = "提取页面区域文本.txt"
extract_text_from_page_area(file_path, page_num, x, y, width, height, output_file)
到此这篇关于python实现简单提取pdf文档内文字的文章就介绍到这了,更多相关python提取pdf文字内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论