Skip to content

Export SharePoint navigation structure to CSV — both Quick Launch

and top nav bars, as a flat inventory.

Walks every node (recursively via get_all_nodes) with a progress bar and projects the loaded collections to records via to_records() — the pipeline's synchronous projection, which is reliable on walked collections (deferred exporters like to_csv fire before the recursive walk completes).

Reference

View source

import argparse
import csv

from office365.runtime.operations import Progress
from office365.sharepoint.client_context import ClientContext
from office365.sharepoint.navigation.nodes.collection import NavigationNodeCollection
from office365.sharepoint.navigation.nodes.node import NavigationNode
from tests.settings import cert_path, cert_thumbprint, client_id, site_url, tenant


def progress_bar(description: str):
    """tqdm-backed hook — the library only needs a ``Callable[[Progress], None]``."""
    from tqdm import tqdm

    bar = tqdm(desc=description)

    def hook(p: Progress[NavigationNode]) -> None:
        bar.update(p.done - bar.n)

    return hook


def export_bar(collection: NavigationNodeCollection, writer, no_progress: bool) -> None:
    """Walk a navigation bar and write its nodes as flat rows."""
    hook = None if no_progress else progress_bar("Scanning navigation")
    nodes = collection.get_all_nodes(recursive=True, progress=hook).execute_query()
    for record in nodes.select(["Id", "Title", "Url"]).to_records():
        writer.writerow([record["Title"], record["Url"]])


def main():
    parser = argparse.ArgumentParser(description="Export SharePoint navigation to CSV")
    parser.add_argument("--site-url", default=site_url, help="site URL")
    parser.add_argument("--output", default="nav_export.csv", help="output CSV file")
    parser.add_argument("--no-progress", action="store_true", help="do not show tqdm progress bars")
    args = parser.parse_args()

    ctx = ClientContext(args.site_url).with_client_certificate(
        tenant, client_id=client_id, thumbprint=cert_thumbprint, cert_path=cert_path
    )

    with open(args.output, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["Title", "URL"])
        print("Quick Launch:")
        export_bar(ctx.web.navigation.quick_launch, writer, args.no_progress)
        print("Top nav bar:")
        export_bar(ctx.web.navigation.top_navigation_bar, writer, args.no_progress)

    print(f"Exported to {args.output}")


if __name__ == "__main__":
    main()

← Back to Navigation