大家好,今天给大家带来的是结合Streamlit,我们提取PDF文档中的一些内容的方法,如提取PDF的基本信息、文本信息、表格。
实现效果
实现代码
import streamlit as st
import pdfplumber
import io
from pandas import DataFrame
import pandas as pd
import fitz
import streamlit.components.v1 as components
st.set_page_config(page_title="操作PDF", layout="wide")
css = """<style>
#MainMenu {visibility:hidden;}
footer {visibility:hidden;}
.stDownloadButton>button {
background-color: #0099ff;
color:#ffffff;
}
.stDownloadButton>button:hover {
background-color: #00ff00;
color:#ff0000;
}
</style>
"""
st.markdown(css, unsafe_allow_html=True)
def convert_df(df):
st.download_button(
label="点我下载表格",
data=df.to_csv().encode('gbk'),
file_name='table.csv',
mime='text/csv',
)
def draw_table(df, theme, table_height):
columns = df.columns
thead1="""<thead><th scope="col"></th>"""
thead_temp = []
for k in range(len(list(columns))):
thead_temp.append("""<th scope="col" class="text-white">"""+str(list(columns)[k])+"""</th>""")
header = thead1+"".join(thead_temp)+"""</tr></thead>"""
rows = []
rows_temp = []
for i in range(df.shape[0]):
rows.append("""<th scope="row">"""+str(i+1)+"""</th>""")
rows_temp.append(df.iloc[i].values.tolist())
td_temp = []
for j in range(len(rows_temp)):
for m in range(len(rows_temp[j])):
td_temp.append("""<td class="text-white">"""+str(rows_temp[j][m])+"""</td>""")
td_temp2 = []
for n in range(len(td_temp)):
td_temp2.append(td_temp[n:n+df.shape[1]])
td_temp3 = []
for x in range(len(td_temp2)):
if int(x % (df.shape[1])) == 0:
td_temp3.append(td_temp2[x])
td_temp4 = []
for y in range(len(td_temp3)):
td_temp4.append("".join(td_temp3[y]))
td_temp5 = []
for v in range(len(td_temp4)):
td_temp5.append("""<tr><th scope="row" class="text-white">"""+str(v+1)+"""</th>"""+str(td_temp4[v])+"""</tr>""")
table_html = """<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">"""+\
"""<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>"""+\
"""<table class="table text-center table-bordered """+str(theme)+'"'+">""" + \
header+"""<tbody>"""+"".join(td_temp5)+"""</tbody></table>"""
return components.html(table_html,height=table_height, scrolling=True)
file = st.file_uploader("请上传PDF")
if file is not None:
doc = fitz.open(stream=file.read(), filetype="pdf")
st.success("PDF基本信息:")
st.write(pd.DataFrame(data=doc.metadata, index=["value"]))
text = ""
for page in doc:
text += page.getText()
st.success("PDF文字内容:")
st.text_area("", text, height=300)
st.success("PDF表格内容:")
with pdfplumber.open(io.BufferedReader(file)) as p:
for i in range(int(doc.page_count)):
try:
page = p.pages[i]
table = page.extract_table()
if table is not None:
temp = {}
for j in range(len(table)):
temp[str(j)] = table[j]
df = DataFrame(temp)
df2 = pd.DataFrame(df.values.T, index=df.columns, columns=df.index)
def change_df(df):
arr = df.values
new_df = pd.DataFrame(arr[1:, 1:], index=arr[1:, 0], columns=arr[0, 1:])
new_df.index.name = arr[0, 0]
return draw_table(new_df.reset_index(), "bg-info", 200)
change_df(df2)
convert_df(df2)
except ValueError:
pass
doc.close()
实现原理
1、我们使用了PyMuPDF模块提取PDF的基本信息和文本内容
以下是PyMuPDF模块的安装方法:
pip install PyMuPDF
以下是PyMuPDF模块的导入方法:
import fitz
2、提取PDF文档基本信息的方法如下:
doc = fitz.open(stream=file.read(), filetype="pdf")
st.success("PDF基本信息:")
st.write(pd.DataFrame(data=doc.metadata, index=["value"]))
3、提取PDF文档中的文本信息方法如下:
doc = fitz.open(stream=file.read(), filetype="pdf")
text = ""
for page in doc:
text += page.getText()
st.success("PDF文字内容:")
st.text_area("", text, height=300)
4、提取PDF文档中表格的方法如下:
with pdfplumber.open(io.BufferedReader(file)) as p:
for i in range(int(doc.page_count)):
try:
page = p.pages[i]
table = page.extract_table()
if table is not None:
temp = {}
for j in range(len(table)):
temp[str(j)] = table[j]
df = DataFrame(temp)
df2 = pd.DataFrame(df.values.T, index=df.columns, columns=df.index)
def change_df(df):
arr = df.values
new_df = pd.DataFrame(arr[1:, 1:], index=arr[1:, 0], columns=arr[0, 1:])
new_df.index.name = arr[0, 0]
return draw_table(new_df.reset_index(), "bg-info", 200)
change_df(df2)
convert_df(df2)
except ValueError:
pass
其中doc.page_count代表PDF文档的总页数,page.extract_table()代表从该页中解析出对应表格的动作。
从调试过程看,这个过程释放的是一些列表,经过如下一些动作的处理:
(1)列表转字典(2)字典转Dataframe(3)Dataframe的转置(4)Dataframe的表头及索引index处理
然后我们就得到了每页中的目标Dataframe对象df2,最后使用draw_table进行绘制表格,并通过convert_df函数生成我们对应的表格下载链接。
这部分的处理代码如下:
try:
page = p.pages[i]
table = page.extract_table()
if table is not None:
temp = {}
for j in range(len(table)):
temp[str(j)] = table[j]
df = DataFrame(temp)
df2 = pd.DataFrame(df.values.T, index=df.columns, columns=df.index)
def change_df(df):
arr = df.values
new_df = pd.DataFrame(arr[1:, 1:], index=arr[1:, 0], columns=arr[0, 1:])
new_df.index.name = arr[0, 0]
return draw_table(new_df.reset_index(), "bg-info", 200)
change_df(df2)
convert_df(df2)
except ValueError:
pass
在测试中发现,部分页的表格提取存在None的结果,这时候会报ValueError的错误,为了不影响我们对其他页面的表格提取,作者在这里使用了跳过(pass)的方法。
5、如果你想实现对上传的PDF文档内容的预览功能,可以使用如下代码:
import base64
import streamlit as st
file = st.file_uploader("请上传PDF")
if file is not None:
base64_pdf = base64.b64encode(file.read()).decode('utf-8')
pdf_display = f'<embed src="data:application/pdf;base64,{base64_pdf}" width="100%" height="1000" type="application/pdf">'
st.markdown(pdf_display, unsafe_allow_html=True)
Streamlit交流群
如果你也想用Python+Streamlit做一些有用的应用,帮助提高办公效率,可以加入我们的Streamlit讨论群,一起学习,共同成长!
扫码添加作者微信,作者看到后会拉你入群,验证信息:我来自公众号Streamlit\