Python+Streamlit在网页中提取PDF中文字、表格对象

673 阅读3分钟

大家好,今天给大家带来的是结合Streamlit,我们提取PDF文档中的一些内容的方法,如提取PDF的基本信息、文本信息、表格。

实现效果

PDF内容.png

实现代码

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[01:])
                        new_df.index.name = arr[00]
                        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[01:])
                        new_df.index.name = arr[00]
                        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[01:])
            new_df.index.name = arr[00]
            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\

个人二维码.jpg

公众号二维码.png