zbparse/flask_app/general/clean_pdf.py

178 lines
7.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
import fitz
from PyPDF2 import PdfReader
def extract_common_header(pdf_path):
"""
提取 PDF 文件的公共页眉。
参数:
- pdf_path: PDF 文件路径。
返回:
- common_header: 公共页眉内容,字符串。如果未找到,则返回空字符串。
"""
def get_headers(pdf_document, start_page, pages_to_read, is_pypdf2):
headers = []
for i in range(start_page, min(start_page + pages_to_read,
len(pdf_document.pages) if is_pypdf2 else pdf_document.page_count)):
try:
if is_pypdf2:
page = pdf_document.pages[i]
text = page.extract_text() or ""
else:
page = pdf_document.load_page(i)
text = page.get_text() or ""
if text:
# 只取每页的前三行,去除前后的空白字符
first_lines = [line.strip() for line in text.strip().split('\n')[:3]]
headers.append(first_lines)
except Exception as e:
print(f"提取第 {i} 页文本时出错: {e}")
continue
return headers
def find_common_headers(headers):
if not headers:
return []
# 转置,使得每一行对应所有页的同一行
transposed_headers = list(zip(*headers))
common_headers = []
for lines in transposed_headers:
# 将每行按空格分割成部分
split_lines = [line.split() for line in lines]
# 找出所有行中最短的部分数
min_parts = min(len(parts) for parts in split_lines)
if min_parts == 0:
continue
common_parts = []
for part_idx in range(min_parts):
# 获取当前部分在所有行中的值
current_parts = [parts[part_idx] for parts in split_lines]
# 检查所有部分是否相同
if all(part == current_parts[0] for part in current_parts[1:]):
common_parts.append(current_parts[0])
else:
break # 如果某部分不相同,停止进一步比较
if common_parts:
# 将共同的部分重新组合成字符串
common_header_line = ' '.join(common_parts)
if len(common_header_line) >= 5: # 可以根据实际情况调整最小长度
common_headers.append(common_header_line)
return common_headers
try:
# 尝试使用 PyPDF2 读取 PDF
try:
pdf_document = PdfReader(pdf_path)
total_pages = len(pdf_document.pages)
is_pypdf2 = True
# print("使用 PyPDF2 成功读取 PDF 文件。")
except Exception as e_pypdf2:
print(f"extract_common_header:使用 PyPDF2 读取 PDF 失败: {e_pypdf2}")
try:
# 如果 PyPDF2 失败,尝试使用 PyMuPDF 读取 PDF
pdf_document = fitz.open(pdf_path)
total_pages = pdf_document.page_count
is_pypdf2 = False
# print("使用 PyMuPDF 成功读取 PDF 文件。")
except Exception as e_fitz:
print(f"extract_common_header:使用 PyMuPDF 读取 PDF 也失败: {e_fitz}")
return "" # 或者根据需求抛出异常
# 定义两个提取策略
strategies = []
if total_pages >= 3:
# 策略1中间的3页
middle_page = total_pages // 2
start_page = max(0, middle_page - 1)
strategies.append((start_page, 3))
elif total_pages == 2:
# 策略12页
strategies.append((0, 2))
else:
# 策略11页
strategies.append((0, 1))
# 策略2前三页
if total_pages >= 3:
strategies.append((0, 3))
elif total_pages == 2:
strategies.append((0, 2))
elif total_pages == 1:
strategies.append((0, 1))
common_headers = []
for idx, (start, count) in enumerate(strategies):
headers = get_headers(pdf_document, start, count, is_pypdf2)
if len(headers) < 2:
continue # 需要至少2页来比较
current_common = find_common_headers(headers)
if current_common:
common_headers = current_common
# print(f"使用策略{idx + 1}找到共同的页眉: {common_headers}")
break # 找到共同部分后退出
# 如果没有找到,继续下一个策略
return '\n'.join(common_headers)
except Exception as e:
print(f"Error in extract_common_header: {e}")
return "" # 根据需求调整返回值
def clean_page_content(text, common_header):
# 首先删除抬头公共部分
if common_header: # 确保有公共抬头才进行替换
for header_line in common_header.split('\n'):
if header_line.strip(): # 只处理非空行
# 替换首次出现的完整行
text = re.sub(r'^' + re.escape(header_line.strip()) + r'\n?', '', text, count=1)
# 预处理:删除文本开头的所有空白字符(包括空格、制表符等)
text = text.lstrip()
# 删除文本开头的“第 x 页”格式的页码
text = re.sub(r'^第\s*\d+\s*页\s*', '', text)
# 删除页码 eg:89/129 这个代码分三步走可以把89/129完全删除
text = re.sub(r'^\s*\d+\s*(?=\D)', '', text) # 删除开头的页码,仅当紧跟非数字字符时 投标人须知这块, 页码和顶部序号混在一起的时候也会把序号给去除了。'2018.' 20为页码 18.为序号
text = re.sub(r'^\s*\/?\s*(共\s*)?\d+\s*(页)?\s*', '', text) #删除/123 /共123 /共123页 /123页
text = re.sub(r'^\s*[—-]\s*(第\s*)?\d{1,3}\s*(页)?\s*[—-]\s*', '', text) # 删除形如 '—2—', '-2-', 或 '-第2页-' 的页码
return text
def is_scanned_pdf(file_path, max_pages=15):
"""
检查 PDF 是否为扫描件(即前 15 页无文本)。
参数:
- file_path: PDF 文件路径。
- max_pages: 最大检查页数,默认为 15 页。
返回:
- True: 如果前 15 页都没有文本,认为是扫描件。
- False: 如果有任何页有文本,认为不是扫描件。
"""
with open(file_path, 'rb') as file:
reader = PdfReader(file)
for i, page in enumerate(reader.pages):
if i >= max_pages: # 超过最大检查页数,停止检查
break
if page.extract_text().strip(): # 如果有文本
return False # 不是扫描型
return True # 前 max_pages 页都没有文本
if __name__ == '__main__':
file_path = r"C:\Users\Administrator\Documents\WeChat Files\wxid_d11awe5rp1y722\FileStorage\File\2024-12\2020-安徽-安徽省生态环境厅电梯采购.pdf"
res=is_scanned_pdf(file_path)
if res:
print("扫描型")
else:
print("普通型")