PdfToExcel / code4.py
aakanksha77's picture
Upload 5 files
0002c06 verified
Raw
History Blame Contribute Delete
8.86 kB
import pdfplumber
import pandas as pd
import re
from typing import List, Dict, Tuple, Any
class PDFSectionExtractor:
def __init__(self, pdf_path: str):
"""Initialize with path to PDF file."""
self.pdf_path = pdf_path
self.tables = []
self.table_names = {} # Store table names and their content
def is_level_three_section(self, section_number: str) -> bool:
"""Check if the section number is a level three section (e.g., 2.1.1)."""
return len(section_number.split('.')) == 3
def find_table_names(self, text: str) -> List[Dict[str, str]]:
"""Extract table names from text."""
table_pattern = r'\*\*Table\s+(\d+):\s+([^*]+)\*\*'
return [(match.group(1), match.group(2).strip())
for match in re.finditer(table_pattern, text)]
def clean_content(self, content: str) -> str:
"""Remove table references and names from content."""
# Remove table references
content = re.sub(r'\*\*Table\s+\d+:\s+[^*]+\*\*', '', content)
# Remove any empty lines created
content = '\n'.join(line for line in content.split('\n') if line.strip())
return content
def merge_split_tables(self, tables: List[List]) -> List[List]:
"""Merge tables that are split across pages."""
merged_tables = []
current_table = None
for table in tables:
if not table:
continue
if current_table is None:
current_table = table
else:
# Check if this table is a continuation
# Compare the number of columns
if len(table[0]) == len(current_table[0]):
current_table.extend(table)
else:
merged_tables.append(current_table)
current_table = table
if current_table:
merged_tables.append(current_table)
return merged_tables
def extract_tables(self) -> List[Dict[str, Any]]:
"""Extract tables with their names and merge split tables."""
tables_data = []
current_section = None
current_table_data = None
current_table_name = None
with pdfplumber.open(self.pdf_path) as pdf:
for page in pdf.pages:
text = page.extract_text(x_tolerance=1,y_tolerance=0) or ''
# Find table names in the text
table_names = self.find_table_names(text)
tables = page.extract_tables()
# Process each table found
if tables:
tables = self.merge_split_tables(tables)
for i, table in enumerate(tables):
table_name = None
if i < len(table_names):
table_num, name = table_names[i]
table_name = f"Table {table_num}: {name}"
if table: # Check if table has content
df = pd.DataFrame(table)
# Clean the DataFrame
df = df.dropna(how='all').dropna(axis=1, how='all')
# Replace None with empty string
df = df.fillna('')
tables_data.append({
'name': table_name,
'data': df
})
return tables_data
def extract_sections(self) -> List[Dict[str, str]]:
"""Extract sections from PDF with content, excluding tables."""
sections = []
current_section = None
current_content = []
section_pattern = r'^(\d+\.(?:\d+)?(?:\.\d+)?)\s+(.+)$'
with pdfplumber.open(self.pdf_path) as pdf:
for page in pdf.pages:
text = page.extract_text(x_tolerance=1)
if not text:
continue
lines = text.split('\n')
for line in lines:
match = re.match(section_pattern, line.strip())
if match:
if current_section:
content_text = '\n'.join(current_content)
content_text = self.clean_content(content_text)
if self.is_level_three_section(current_section[0]):
full_content = current_section[1] + '\n' + content_text
sections.append({
'section_number': current_section[0],
'section_name': '',
'content': full_content
})
else:
sections.append({
'section_number': current_section[0],
'section_name': current_section[1],
'content': content_text
})
current_section = (match.group(1), match.group(2))
current_content = []
elif current_section:
current_content.append(line.strip())
# Handle the last section
if current_section:
content_text = '\n'.join(current_content)
content_text = self.clean_content(content_text)
if self.is_level_three_section(current_section[0]):
full_content = current_section[1] + '\n' + content_text
sections.append({
'section_number': current_section[0],
'section_name': '',
'content': full_content
})
else:
sections.append({
'section_number': current_section[0],
'section_name': current_section[1],
'content': content_text
})
return sections
def convert_pdf_to_excel(pdf_path: str, excel_path: str):
"""Convert PDF with sections and tables to Excel file."""
try:
extractor = PDFSectionExtractor(pdf_path)
sections = extractor.extract_sections()
tables = extractor.extract_tables()
with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
# Write sections to main sheet
df_sections = pd.DataFrame(sections)
df_sections.to_excel(writer, index=False, sheet_name='Sections')
# Auto-adjust sections sheet
worksheet = writer.sheets['Sections']
for idx, col in enumerate(['A', 'B', 'C']):
worksheet.column_dimensions[col].width = 15 if idx < 2 else 50
# Write tables to separate sheets
for i, table_info in enumerate(tables, 1):
if table_info['name']:
sheet_name = table_info['name'][:31] # Excel sheet name length limit
else:
sheet_name = f'Table_{i}'
# Write table data
table_info['data'].to_excel(writer, sheet_name=sheet_name, index=False)
# Auto-adjust table sheet
worksheet = writer.sheets[sheet_name]
for column in worksheet.columns:
max_length = 0
column = [cell for cell in column]
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(cell.value)
except:
pass
adjusted_width = (max_length + 2)
worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
return True
except Exception as e:
print(f"Error converting PDF to Excel: {str(e)}")
return False
if __name__ == "__main__":
pdf_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/[00 12 10] 26251-100-3DR-S04-00001_002.docx.pdf"
excel_path = "/Users/aakanksha.n/Desktop/pdf_to_excel/format4.xlsx"
success = convert_pdf_to_excel(pdf_path, excel_path)
if success:
print("Successfully converted PDF to Excel!")
else:
print("Failed to convert PDF to Excel.")