← Back to list

Python PDF Document Separator

The one script that shall yet again preclude the need for Adobe Acrobat Pro for splitting a large document into smaller ones.

Joydeep Chatterjee in GoPenAI · 2025-01-20 21:38 · 0 claps · 7.7 min read
#pdf #pypdf2 #python #data-processing #data-engineering
Open on Medium ↗
Wiki topics: 🔧 · Data Engineering

Python PDF Document Separator

The one script that shall yet again preclude the need for Adobe Acrobat Pro for splitting a large document into smaller ones.

Created with DALL-E

Created with DALL-E

Have you ever received a large PDF document that you wanted to split into smaller ones and annotate by date and name? You can write a simple script in Python to automate that process for multiple files in a folder.

Here is an example of a full dump of employee timesheets that need to be separated by name and annotated by date to create an archive of timesheets over a period of time for long-term storage:

A page from a typical employee timesheet document with employee name and timesheet period end date.

A page from a typical employee timesheet document with employee name and timesheet period end date.

The components of the time sheet that matter are:

  1. Employee First Name
  2. Employee Last Name
  3. Timesheet Period End Date

The goal is to break up this 154-page document in this example into individual timesheets for each distinct employee that are typically 1 page but may extend to multiple depending on the complexity of their work week.

The file names of these individual timesheets shall be as follows:

{YYYY}-{MM}-{DD}_{LAST NAME}_{FIRST NAME}

Structure this process to have the Python program process files within a folder consisting of input files into another folder consisting of output files. Therefore, use the os and glob packages for both navigating between directories and grouping multiple files together:

# Packages for working with file structures
import os
import glob

Fortunately, since the PDF file is searchable and has a consistent structure, there is no need for any RegEx coding to find distinct patterns for search for as reference points. Rather, the PyPDF2 package and its classes PdfReader and PdfWriter can be deployed:

# Packages for processing PDF files
from PyPDF2 import PdfReader, PdfWriter 

First, write a function for extracting the distinct first and last names from the top of the page, using the comma as a separator:

def extract_name(name_section):
    """
    Extracts the last name and first name from a name section.
    Handles cases with suffixes, multiple commas, or compound names.
    """
    parts = name_section.split(",")
    if len(parts) >= 2:
        last_name = parts[0].strip()
        first_name = "_".join([name.strip() for name in parts[1:]]).strip()  # Join remaining parts for first name
    else:
        last_name, first_name = "UNKNOWN", "UNKNOWN"
    return last_name, first_name

This code is designed to handle edge cases in which first names can be two words (e.g. “Lee Ann”, “James David”, etc.) or last names can be hyphenated (e.g. maiden name — marriage name) or suffixed (e.g. “Jr.”, “III”, etc.) by extracting the text before the first comma as the last name while extracting all other text separated by commas (“,”) as the first name and joined by underscores (“_”), to later by joined by them to the last name.

Next, write a function that shall call the function for extracting the names to process the page(s) containing the distinct employee timesheet.

Start by creating a directory to ensure that a folder to output the file exists, otherwise defaulting to using it:

# Ensure output directory exists
    os.makedirs(output_directory, exist_ok=True)

Load the the PDF file that the timesheet is in by using the PdfReader class of PyPDF2:

# Load the PDF
    reader = PdfReader(input_pdf_path)

This shall create an object containing the distinct pages of the file.

Initialize a dictionary for the distinct timesheets to be stored as individual pages in the values while the names extracted by the extract_name() function that you have just written shall form the keys:

# Initialize variables to group pages by individuals
    grouped_timesheets = {}
    current_name = None

Now cycle through each of the pages parsed by the PdfReader class of PyPDF2 extracting the names and elements of the date (year, month, and date) separated by slashes while reordering them in the the “YYYY-MM-DD” format required by the aforementioned filename convention:

# Parse each page in the PDF
    for page in reader.pages:
        text = page.extract_text()
        if "Timesheet ending:" in text:
            # Identify a new individual if a new heading is found
            try:
                extract_dates = text.split("Timesheet ending:")[1].split("|")[0].strip().split("/")
                ending_date = "-".join([extract_dates[2], extract_dates[0], extract_dates[1]])
                name_section = text.split("<TEXT BEFORE NAME>")[1].split("Timesheet ending:")[0].strip()
                last_name, first_name = extract_name(name_section)
                current_name = f"{ending_date}_{last_name}_{first_name}"
                if current_name not in grouped_timesheets:
                    grouped_timesheets[current_name] = []
            except Exception as e:
                print(f"Error identifying individual on page: {e}")
                current_name = None

        # Add the page to the current individual's timesheet group
        if current_name:
            grouped_timesheets[current_name].append(page)

The text between the phrase “Timesheet ending:” and the first pipe (“|”) symbol shall be extracted using consecutive split() methods, stripped of additional white spaces using the strip() method, and then stored as a list of its respective elements applying the split() method to the forward slash (“/”) elements separating them. That list shall then be rearranged through the join() method using hyphens (“-”) in the desired format.

The text after a common identifier before the employee name (redacted) an before the phrase “Timesheet ending” shall be extracted and then supplied as input for the extract_name() function to obtain the distinct last_name and first_name variables.

Finally, the ending_date, last_name, and first_name shall be concatenated into the string variable currentname with **underscores (“”) to form a new key the grouped_timesheets dictionary with its respective page object from the reader.pages list of objects within the PdfReader()** class that the pages of the PDF document were loaded into.

The second half of this function shall be using the PdfWriter class of PyPDF2 to cycle through the distinct keys and values (the PDF pages themselves) in the grouped_timesheets dictionary to generate files named after their keys and containing their respective pages for each distinct employee:

# Write each individual's grouped timesheet to a single PDF
    output_files = []
    for name, pages in grouped_timesheets.items():
        output_path = os.path.join(output_directory, f"{name}.pdf")
        writer = PdfWriter()
        for page in pages:
            writer.add_page(page)
        with open(output_path, "wb") as output_file:
            writer.write(output_file)
        output_files.append(output_path)

An optional step is to output which filenames were generated:

print(f"Generated files: {output_files}")

Therefore, the final function should look like this:

def process_timesheet_pdf(input_pdf_path, output_directory):
    """
    Processes a timesheet PDF to split it into individual files.

    Args:
        input_pdf_path (str): Path to the input PDF file.
        output_directory (str): Directory to save the individual timesheet files.
    """
    # Ensure output directory exists
    os.makedirs(output_directory, exist_ok=True)

    # Load the PDF
    reader = PdfReader(input_pdf_path)

    # Initialize variables to group pages by individuals
    grouped_timesheets = {}
    current_name = None

    # Parse each page in the PDF
    for page in reader.pages:
        text = page.extract_text()
        if "Timesheet ending:" in text:
            # Identify a new individual if a new heading is found
            try:
                extract_dates = text.split("Timesheet ending:")[1].split("|")[0].strip().split("/")
                ending_date = "-".join([extract_dates[2], extract_dates[0], extract_dates[1]])
                name_section = text.split("<TEXT BEFORE NAME>")[1].split("Timesheet ending:")[0].strip()
                last_name, first_name = extract_name(name_section)
                current_name = f"{ending_date}_{last_name}_{first_name}"
                if current_name not in grouped_timesheets:
                    grouped_timesheets[current_name] = []
            except Exception as e:
                print(f"Error identifying individual on page: {e}")
                current_name = None

        # Add the page to the current individual's timesheet group
        if current_name:
            grouped_timesheets[current_name].append(page)

    # Write each individual's grouped timesheet to a single PDF
    output_files = []
    for name, pages in grouped_timesheets.items():
        output_path = os.path.join(output_directory, f"{name}.pdf")
        writer = PdfWriter()
        for page in pages:
            writer.add_page(page)
        with open(output_path, "wb") as output_file:
            writer.write(output_file)
        output_files.append(output_path)

    # Optional
    print(f"Generated files: {output_files}")

The third function that needs to be written shall now call this process_timesheet_pdf() function to iterate through multiple files placed in a directory of raw PDF files to be processed and deposited into the output directory:

# Data processing function to be performed on files within target folder
def process_file():

    # Navigate to input file directory
    path = os.getcwd()
    source = '/'
    input_folder = 'timesheets_input'
    os.chdir(path + source + input_folder)

    # Navigate to output file directory
    output_folder = 'timesheets_output'
    output_dir = path + source + output_folder

    # Define the path to the directory containing the PDF files
    all_files = glob.glob('*.pdf')

    # Iterate through every file in the glob and check encoding
    for file in all_files:

        # Process PDF file
        process_timesheet_pdf(file, output_dir)

The user can define the folder containing the files to be processed in the in the input_folder variable as well as the folder of processed files in the output_folder variable, with the path = os.getcwd() definition to ensure that the program is being directed to the directory where the program is stored. Ideally, keep the program in its own directory while separating the distinct folders for the input and output in their own subdirectories.

The glob method of the glob package is used to aggregate all the raw PDF files into one list called all_files that the process_timesheet_pdf() function shall cycle through.

Optionally, keep a counter within the process_file() function for keeping track of the progress, since this operation can be time-consuming:

...

# Define the path to the directory containing the PDF files
    all_files = glob.glob('*.pdf')
    file_count = len(all_files)

    # Iterate through every file in the glob and check encoding
    for index, file in enumerate(all_files):

        # Process PDF file
        print(f'Processing file {index} of {file_count}')
        process_timesheet_pdf(file, output_dir)
        print(f'Processed file {index} of {file_count}')

Finally, to make this an executable file (i.e. being able to be deployed in the terminal through python <filename>, be sure to specify this last function to be run by default at the very end:

if __name__ == "__main__":
    process_file()

That is all to batch process a large volume of aggregated PDF files, such as timesheets, into individually named ones without having to purchase a copy of Adobe Acrobat Pro. Here is the final completed code:

# Packages for working with file structures
import os
import glob

# Packages for processing PDF files
from PyPDF2 import PdfReader, PdfWriter

def extract_name(name_section):
    """
    Extracts the last name and first name from a name section.
    Handles cases with suffixes, multiple commas, or compound names.
    """
    parts = name_section.split(",")
    if len(parts) >= 2:
        last_name = parts[0].strip()
        first_name = "_".join([name.strip() for name in parts[1:]]).strip()  # Join remaining parts for first name
    else:
        last_name, first_name = "UNKNOWN", "UNKNOWN"
    return last_name, first_name

def process_timesheet_pdf(input_pdf_path, output_directory):
    """
    Processes a timesheet PDF to split it into individual files.

    Args:
        input_pdf_path (str): Path to the input PDF file.
        output_directory (str): Directory to save the individual timesheet files.
    """
    # Ensure output directory exists
    os.makedirs(output_directory, exist_ok=True)

    # Load the PDF
    reader = PdfReader(input_pdf_path)

    # Initialize variables to group pages by individuals
    grouped_timesheets = {}
    current_name = None

    # Parse each page in the PDF
    for page in reader.pages:
        text = page.extract_text()
        if "Timesheet ending:" in text:
            # Identify a new individual if a new heading is found
            try:
                extract_dates = text.split("Timesheet ending:")[1].split("|")[0].strip().split("/")
                ending_date = "-".join([extract_dates[2], extract_dates[0], extract_dates[1]])
                name_section = text.split("<TEXT BEFORE NAME>")[1].split("Timesheet ending:")[0].strip()
                last_name, first_name = extract_name(name_section)
                current_name = f"{ending_date}_{last_name}_{first_name}"
                if current_name not in grouped_timesheets:
                    grouped_timesheets[current_name] = []
            except Exception as e:
                print(f"Error identifying individual on page: {e}")
                current_name = None

        # Add the page to the current individual's timesheet group
        if current_name:
            grouped_timesheets[current_name].append(page)

    # Write each individual's grouped timesheet to a single PDF
    output_files = []
    for name, pages in grouped_timesheets.items():
        output_path = os.path.join(output_directory, f"{name}.pdf")
        writer = PdfWriter()
        for page in pages:
            writer.add_page(page)
        with open(output_path, "wb") as output_file:
            writer.write(output_file)
        output_files.append(output_path)

    # Optional
    print(f"Generated files: {output_files}")

# Data processing function to be performed on files within target folder
def process_file():

    # Navigate to input file directory
    path = os.getcwd()
    source = '/'
    input_folder = 'timesheets_input'
    os.chdir(path + source + input_folder)

    # Navigate to output file directory
    output_folder = 'timesheets_output'
    output_dir = path + source + output_folder

    # Define the path to the directory containing the PDF files
    all_files = glob.glob('*.pdf')
    file_count = len(all_files)

    # Iterate through every file in the glob and check encoding
    for index, file in enumerate(all_files):

        # Process PDF file
        print(f'Processing file {index} of {file_count}')
        process_timesheet_pdf(file, output_dir)
        print(f'Processed file {index} of {file_count}')

if __name__ == "__main__":
    process_file()

The PyPDF2 package of Python is extremely powerful for bulk document processing and should be an essential tool for all business intelligence developers and information technology specialists to boost the productivity of their respective enterprise organizations.


메타데이터
post_id
9a6e1c482b60
slug
pdf-document-separator-9a6e1c482b60
url
https://blog.gopenai.com/pdf-document-separator-9a6e1c482b60
canonical_url
https://blog.gopenai.com/pdf-document-separator-9a6e1c482b60
author_url
https://medium.com/@chatterjee.prime
status
ok
fetched_at
2026-07-21 06:32:45