Python script for automating the extraction of document links from a comapany network and writing to an Excel file
14:11 01 Feb 2026

I am in the process of finalizing a database in XLSX format containing all the products of my company where I am an intern. In particular, I created a column where I insert one by one the links leading to the user sheets as well as the technical sheets of these products. These links will be read and processed by a local AI model that I call later in my Python pipeline in order to process the semantic similarity between each document.

There are 330 products and doing this manually is very time-consuming. So I tried to create a Python extraction script using ChatGPT and Gemini. I methodically described to them what I was doing manually, step by step, for a given product so that they could offer me an extraction script.

Here are the prompts given to GPT and Gemini:
"CONTEXTE:
Je veux insérer des liens présents sur le réseau de mon entreprise dans la feuille "master_file" de mon classeur "product_catalog_V8_links" ci-joint. Les liens menant aux fichiers PDF contenant des notices d'utilisation sont placés dans la colonne M ("instructions_for_use"), tandis que les liens menant aux dossiers de validation techniques sont palcés dans la colonne N ("Validation_file").. La logique que je suis manuellement est la suivante:

1. j'accède au réseau via le lien “\\dc2022adia\BioxAdia\R11_Marketing\0_Library”

2. je vais chercher dans la colonne A de la feuille “master_file” la référence produit recherchée; elle est en début de cellule entre crochets. (ex: “[ADI631-100] ADIAVET SALMONELLA ST/SE FAST TIME 100R” en colonne A86) (cf. screenshot “1” en PJ)

3. je tape le code produit dans la barre de recherche en haut à droite en réduisant le code produit à sa “base”, sans le conditionnement (ex:ADI631-100 devient ADI631)

4. j’obtiens ensuite :

a. soit une liste de fichiers et un dossier parent de ces fichiers nommé explicitement “ADI631”, b. soit uniquement des fichiers (cas minoritaire);

5. je clique sur le dossier (cas a.) s’il apparaît (cf. screenshot “2” en PJ). J’ouvre ensuite les dossiers enfants “Dossier de validation” et “Notices” dans un nouvel onglet avec un clic droit.

6. Dans “Notices”, je cherche un fichier répondant à 2 critères:

a. en anglais (obligatoire). Ces fichiers sont indiqués avec les critères suivants:

i. (EN) écrit vers la fin du nom du fichier, avant le numéro de version (exemple: ADC22LD-CSFV-NO-(EN)-V01.pdf )

ii. NE écrit en début de fichier (ex: NE014S-03.pdf) b. version la plus récente (si disponible). Le numéro de version est indiqué de la façon suivante: i. V0x en fin de fichier (ex: ADC72LD-EHDV-NO-(EN)-V02.pdf) ii. -xx.pdf en fin de fichier (ex:NE601-01.pdf)

iii. soit il n’y a aucune indication donc ce critère saute .

7. 2 cas de figure:

a. je ne trouve pas de fichier répondant aux critères 6. C’est le cas ici (cf. screenshot “3”) Je constate ici qu’il n’y a aucun fichier répondant au critère 6)a. Ma recherche s’arrête alors pour la notice de la cellule / produit A86 et je marque en rouge (couleur de remplissage) la cellule “instructions_for_use” relative à la cellule / produit A86 sur laquelle on travaille, soit la cellule M86 (cf. 7.a dans le screenhot “4”).

b. je trouve un fichier répondant aux critères 6. Imaginons ici que le fichier “ADI631-STSE-DV-(FR)-V01” me donne satisfaction. Alors je fais clic droit dessus >propriétés > sécurité. Je copie ensuite le “nom de l’objet” (cf. screenshot “4”) que j’insère en lien hypertexte dans la cellule “instructions_for_use” relative à la cellule / produit A86 sur laquelle on travaille, soit la cellule M86 (cf. 7.b dans le screenshot 4).

8. Je passe ensuite à l’onglet “Dossier de validation” de la cellule / produit A86 pour répéter exactement la même procédure que pour l’onglet “notices” dans le cas de figure où le dossier de la cellule / produit A86 contenait bien un dossier nommé ““Dossier de validation” . Il faut noter les différences suivantes:

a. On insérera les liens trouvés en colonne N de la feuille “master_file”, contrairement aux liens “instructions_for_use” qui étaient inscrits en colonne M.

b. écriture en anglais:

i. -en en fin de fichier (ex: R262-C-en.pdf )

ii. (EN) écrit vers la fin du nom du fichier, avant le numéro de version (exemple: ADI045-PTUB-DV-(EN)-V01.pdf) Dans le cas de figure où le dossier de la cellule / produit A86 ne contenait pas de dossier nommé ““Dossier de validation”, on passe à l’étape 9.

9. Une fois l’étape 8. achevé, je passe à la la cellule / produit A87, etc… jusqu’à la dernière cellule / produit de la colonne A (A338).

Si pour un produit donné, tu n'as obtenu que des fichiers à l’étape 4 (cas minoritaire), inscrit marque en violet (couleur de remplissage) la cellule “instructions_for_use” sur le modèle de l’étape 7.a, puis passe au produit suivant (cf étape 9).

OBJECTIF: Propose moi un script python pour automatiser entièrement cette procédure"

Here is what GPT Gave me:

import re
from pathlib import Path
from collections import defaultdict
from openpyxl import load_workbook
from openpyxl.styles import PatternFill

# =========================
# CONFIG
# =========================
LIBRARY_ROOT = Path(r"\\[NETWORK PATH]")
MASTER_PATH = Path("product_catalog_V8.xlsx")
MASTER_SHEET = "master_file"

COL_PRODUCT_CODE = "A"
COL_NOTICE = "M"
COL_VALIDATION = "N"

RED_FILL = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")

# =========================
# UTILS
# =========================
BASE_CODE_RE = re.compile(r"^([A-Z]+\d+)")
VERSION_RE = re.compile(r"V0*(\d+)", re.IGNORECASE)

def extract_base_code(code: str) -> str | None:
    if not code:
        return None
    m = BASE_CODE_RE.match(code.strip())
    return m.group(1) if m else None

def extract_version(path: Path) -> int | None:
    m = VERSION_RE.search(path.name)
    return int(m.group(1)) if m else None

# =========================
# INDEXATION
# =========================
def build_index(root: Path):
    index = defaultdict(lambda: {"notice": [], "validation": []})

    for pdf in root.rglob("*.pdf"):
        p = str(pdf)
        if "\\Notices\\" in p:
            kind = "notice"
        elif "\\Dossier de validation\\" in p:
            kind = "validation"
        else:
            continue

        if "EN" not in pdf.name.upper():
            continue

        base_match = BASE_CODE_RE.search(pdf.name)
        if not base_match:
            continue

        base_code = base_match.group(1)
        version = extract_version(pdf)

        if version is None:
            continue

        index[base_code][kind].append((version, pdf))

    return index

# =========================
# RESOLUTION
# =========================
def resolve_best(files):
    if not files:
        return None
    return max(files, key=lambda x: x[0])[1]

# =========================
# MAIN
# =========================
def main():
    print("Indexation réseau…")
    index = build_index(LIBRARY_ROOT)
    print(f"{len(index)} produits indexés")

    wb = load_workbook(MASTER_PATH)
    ws = wb[MASTER_SHEET]

    for row in range(2, ws.max_row + 1):
        code_cell = ws[f"{COL_PRODUCT_CODE}{row}"]
        notice_cell = ws[f"{COL_NOTICE}{row}"]
        validation_cell = ws[f"{COL_VALIDATION}{row}"]

        if notice_cell.value or validation_cell.value:
            continue

        base_code = extract_base_code(str(code_cell.value))
        if not base_code or base_code not in index:
            notice_cell.fill = RED_FILL
            validation_cell.fill = RED_FILL
            continue

        notice = resolve_best(index[base_code]["notice"])
        validation = resolve_best(index[base_code]["validation"])

        if not notice or not validation:
            notice_cell.fill = RED_FILL
            validation_cell.fill = RED_FILL
            continue

        notice_cell.value = notice.as_posix()
        validation_cell.value = validation.as_posix()

    wb.save(MASTER_PATH)
    print("Terminé.")

if __name__ == "__main__":
    main()

Output: The M and N columns of my excel file were filled with red cells only!

He re is what Gemini suggested me:

import pandas as pd
import os
import re

# --- CONFIGURATION ---
# Chemin vers votre fichier Excel
excel_path = 'product_catalog_V8_links.xlsx'

# Chemin racine du réseau (Note : utilisez r'' pour les chemins Windows)
root_folder_path = r'[NETWORK PATH]

# Noms des colonnes dans Excel
col_reference = 'Reference'
col_notice = 'instructions_for_use'
col_validation = 'Validation_file'

# --- FONCTION DE RECHERCHE ---
def find_files_for_ref(ref_id, root_path):
    """
    Cherche les chemins des fichiers Notice et Validation pour un ID donné.
    Retourne un dictionnaire avec les chemins trouvés.
    """
    paths = {'notice': None, 'validation': None}
    
    # Nettoyage de la référence : on garde ce qu'il y a entre crochets ou la ref brute
    # Ex: "[#416443]" -> "416443" ou "ADI015"
    clean_ref = str(ref_id).replace('[', '').replace(']', '').split(']')[0].strip()
    
    # Si la référence est vide ou trop courte, on ignore pour éviter les faux positifs
    if len(clean_ref) < 3:
        return paths

    # 1. On cherche d'abord le dossier du produit correspondant à la ref
    product_folder_path = None
    
    # os.walk permet de descendre dans toute l'arborescence (ORT, BVD, etc.)
    for root, dirs, files in os.walk(root_path):
        for dir_name in dirs:
            # Si le nom du dossier contient la référence (ex: "416443-ADIAVET ORT" contient "416443")
            if clean_ref.lower() in dir_name.lower():
                product_folder_path = os.path.join(root, dir_name)
                break
        if product_folder_path:
            break
    
    # Si aucun dossier produit n'est trouvé, on arrête là
    if not product_folder_path:
        return paths

    # 2. Une fois dans le bon dossier produit, on cherche les sous-dossiers spécifiques
    # On regarde dans le dossier produit et ses sous-dossiers immédiats
    for root, dirs, files in os.walk(product_folder_path):
        
        # Recherche NOTICE
        if "notice" in root.lower() or "notices" in root.lower():
            for file in files:
                if file.lower().endswith('.pdf'):
                    # On prend le chemin absolu
                    paths['notice'] = os.path.join(root, file)
                    break # On prend le premier PDF trouvé
        
        # Recherche VALIDATION
        if "validation" in root.lower() or "dossier de validation" in root.lower():
            for file in files:
                if file.lower().endswith('.pdf'):
                    paths['validation'] = os.path.join(root, file)
                    break

    return paths

# --- EXÉCUTION ---
print("Chargement du fichier Excel...")
# On charge l'onglet 'master_file' (ajustez le sheet_name si besoin)
df = pd.read_excel(excel_path, sheet_name='master_file')

print(f"Début du traitement de {len(df)} lignes. Cela peut prendre du temps sur un réseau...")

# On itère sur chaque ligne
for index, row in df.iterrows():
    ref = row[col_reference]
    
    # On ne traite que si la ref n'est pas vide
    if pd.notna(ref):
        print(f"Recherche pour : {ref} ...")
        found_paths = find_files_for_ref(ref, root_folder_path)
        
        # Si on trouve une Notice, on l'écrit
        if found_paths['notice']:
            df.at[index, col_notice] = found_paths['notice']
            print(f"   -> Notice trouvée : {os.path.basename(found_paths['notice'])}")
            
        # Si on trouve une Validation, on l'écrit
        if found_paths['validation']:
            df.at[index, col_validation] = found_paths['validation']
            print(f"   -> Validation trouvée : {os.path.basename(found_paths['validation'])}")

# --- SAUVEGARDE ---
output_file = 'product_catalog_V8_links_UPDATED.xlsx'
print(f"Sauvegarde dans {output_file}...")
df.to_excel(output_file, index=False, sheet_name='master_file')
print("Terminé !")

Output: Nothing changed to the input file!

Any idea please on how to to make any of theses two suggestions work?
Thank you in advance!

python excel pandas automation