178 lines
7.0 KiB
Python
178 lines
7.0 KiB
Python
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:
|
||
# 策略1:2页
|
||
strategies.append((0, 2))
|
||
else:
|
||
# 策略1:1页
|
||
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("普通型") |