Resurrecting My Old Blog with Archive.org and a Lot of C# (Français (French))

Resurrecting My Old Blog with Archive.org and a Lot of C#

Monday, 24 November 2025

//

11 minute read

Vous avez peut-être remarqué des centaines Postes sur le blog "new" apparaissant récemment. bien , ils' ne sont pas neufs du tout M SK3 ils+'ils sont âgés de plus de 5 ans Comme=, 2004 anciensMSC8 J’ai enfin construit un outil pour sauver mon contenu du cimetière numérique qui était mon vieux blog à mostlylucidMNK9coMRK10ukMBK11

Pourquoi Archive.org est absolument brillante

Avant de me plonger dans les choses techniques,, je dois faire un grand cri au Archives Internet Cette organisation sans but lucratif, -, archive silencieusement le Web depuis qu’elle préserve des milliards de pages qui auraient été perdues pour toujours.

Pensez-en un second.

Lorsque mon vieux fournisseur d'hébergement a disparu (along avec mes sauvegardes parce que j'étais SMART comme ça ), J'avais pensé que tout ce contenu était perdu pour toujours. Il se dégage que la Machine de retour avait été diligentement prise en photo mon site pendant des annéesM SK3 ArchiveMSC4org quite literally savé 6+ ans de ma histoire de blogoingMNK6

Si vous n’avez jamais fait de don à eux, envisagez de le faire. Ils préserveront notre histoire numérique collective

Le problème: Mon blog était un message

Voici ce qu’il y a à faire pour gérer un blog de 2004 à 2010 - les technologies du web ont changé DE LA LOT au cours de cette périodeM SK4 Et apparemment, J’ai modifié ma configuration de blogging au moins trois fois

  1. Jours précoces (2004): Certaines applications ASP de langue maternelle .NET dont le contenu est emballé <div class="post"> à l'intérieur d'un <form> élément ( car tout était une forme à l'époque
  2. Période moyenne: Structure de modèle différente, dates à différents endroits
  3. Les années suivantes: Une nouvelle restructuration avec des sélecteurs légèrement différents

À partir de la mémoire, il a utilisé quelque chose personnalisé alors Sous-texte (Phil Haack'le blog de ). Suivi par le serveur communautaire (a Telligent thing ASP.NET utilisé pour héberger des sites, par un autre ancien ASP ,NET PM Rob HowardM SK3 MSC4 Tous dont les moyens de mise à jour et d'affichage du contenu diffèrent

Cela signifiait que tout outil d'extraction devait être suffisamment souple pour manipuler plusieurs structures HTML.

Inscrire l'ArchiveOrgImporter

J'ai construit ArchiveOrgImporter pour résoudre ce problème très spécifique.

  1. Respecte les limites d'utilisation des archives.org' - Ils sont un organisme non lucratif fondé sur des dons ,, c’est pourquoi il serait terrible de battre leurs serveurs
  2. Télécharge des pages archivées entre dates configurables
  3. Extrait le contenu du blog à partir de plusieurs structures HTML
  4. génère un marquage-démarrage propre dans le format de mon blog actuel
  5. Utilise Ollama pour générer des balises utiles - parce que pourquoi ne pas y jeter un peu de magie LLM?

Comment cela fonctionne

L’outil suit une architecture de pipeline comportant trois phases principales:

Archive.org CDX API → Download HTML → Convert to Markdown → Generate Tags → Output Files

Phase 1: Enquête sur l'archive

L'outil utilise Archive.org's L'API de CDX pour trouver tous les snapshots archivés de mon blog. Cette API retourne une liste des URL capturées avec des timestamps, Types MIMEMSC2 et codes d'état HTTPM SK3

// The CDX query builds a URL like this:
// https://web.archive.org/cdx/search/cdx?url=mostlylucid.co.uk/posts/&output=json&collapse=urlkey

L'article 5 de la Loi sur l'immigration et le statut des réfugiés collapse=urlkey Paramètre clever - il ne retourne que le dernier coup d’œil pour chaque URL unique , ce qui réduit considérablement le nombre de duplicats dont vous avez besoin pour traiter avec

J’utilise également des modèles regex pour filtrer les URLs. mes anciens messages ont suivi le modèle /posts/[number].aspx, ainsiM SK1

{
  "IncludePatterns": ["/posts/\\d+\\.aspx$"]
}

De cette façon, je n'obtiens que des articles de blog réels, pas les pages d'archives , les pages de catégorieM SK2 ou les flux RSS.

Phase 2: Être un bon citoyen à taux limité

Archive.org est un service public. Il n’y a pas de budget d’infrastructures de Google ou Amazon . Le téléchargeur est donc délibérément conservateur

// Default: 5 seconds between requests, single-threaded downloads
"RateLimitMs": 5000,
"MaxConcurrentDownloads": 1

Oui, cela signifie que le téléchargement de centaines d’articles prend du temps . Mais c’est la bonne chose à faire. L’outil gère également M SK4 ( limite tarifaireMSC6 les réponses gracieusement avec un ralentissement exponentiel~.

Il y a aussi une certaine nettoyage nécessaire pour les fichiers téléchargés.

private static string CleanWaybackArtifacts(string html)
{
    // Remove the interactive Wayback toolbar
    html = WaybackToolbarRegex().Replace(html, string.Empty);
    // Remove playback.archive.org script references
    html = WaybackScriptRegex().Replace(html, string.Empty);
    // Strip archival metadata comments
    html = WaybackCommentRegex().Replace(html, string.Empty);
    // Rewrite archived URLs back to original paths
    html = WaybackUrlRewriteRegex().Replace(html, "$1$2");
    return html;
}

Les patrons regex manipulent:

  • <!-- BEGIN WAYBACK TOOLBAR INSERT -->...<!-- END WAYBACK TOOLBAR INSERT --> - la barre d'outils HTML
  • <script> Référence aux tags playback.archive.org
  • Commentaires sur les métadonnées archivées
  • Préfixes URL comme https://web.archive.org/web/20040527/ qui sont reliés à tous les liens

Phase 3: The Content Extraction Nightmare

C'est là où les choses se font intéressantes. Je me souviens que j'ai mentionné mon blog a changé de structure trois fois

L'extraction primaire utilise un sélecteur CSS:

{
  "ContentSelector": "div.post"
}

Mais ici ' est un truc amusant - dans certaines versions de mon site antique div.post a été à l'intérieur d'un <form> élément. Le code doit extraire le contenu AVANT d'enlever les éléments non désirés, autrement enlever <form> Les tags nuisent au contenu réel du blog:

// In ConvertFileAsync - the order here is critical
var contentNode = ExtractMainContent(doc);
if (contentNode == null)
{
    _logger.LogWarning("Could not find main content in {File}", htmlFilePath);
    return articles;
}

// NOW we can safely remove unwanted elements from within the extracted content
RemoveUnwantedElementsFromNode(contentNode);

L'article 5 de la Loi sur l'immigration et le statut des réfugiés ExtractMainContent la méthode utilise une recherche hiérarchique pour trouver le contenu:

// For selectors like "div.post", split and search by element + class
node = doc.DocumentNode.Descendants()
    .FirstOrDefault(n =>
        n.Name.Equals(elementName, StringComparison.OrdinalIgnoreCase) &&
        HasExactClass(n, className));

L'outil comporte également des sélecteurs de rappel si le premier faillit:

  1. div.blogpost, div.singlepost, article
  2. #content, #main-content, #PostBody
  3. .post-content, .entry-content, .article-content
  4. Enfin, juste le <body> si tout le reste échoue

Il y a ensuite' une longue liste d’éléments à retirer après l’extraction du contenu:

{
  "RemoveSelectors": [
    "nav", "header", "footer", ".sidebar", ".advertisement",
    ".comments", ".social-share", ".related-posts", "script",
    "style", "noscript", "iframe", "#commentform", ".postNav"
  ]
}

Phase 4: Production de marquage

Une fois que nous avons un contenu HTML propre, ReverseMarkdown gère l’élévation lourde de la conversion à GitHub-flavored Markdown.

Mais la sortie a besoin d’une certaine post-processing. Le HTML antérieur avait souvent une indentation étrange qui confondait les parseurs de Markdown (les lignes indentées deviendaient des blocs de codeMSC3 Donc làM SK4le nettoyageMNK5

// Removes leading whitespace from non-code lines
// Preserves code block formatting (respects ``` fences)
// Removes excessive blank lines (max 2 consecutive)

La sortie finale inclut mon blog'format de la matière première:

# Article Title




Article content here...

Phase 5: LLM-Generation d'étiquettes électriques

Maintenant, pour la partie amusante. Les anciens articles de blog n’avaient souvent pas d’étiquettes à l’égard desquelles je travaillait, ou des étiquettes qui ne pouvaient pas fonctionnerMSC2pour ma structure actuelle du siteM SK3 J’ai donc intégré Ollama pour analyser le contenu et générer les tags pertinents .

La configuration est simple:

{
  "Ollama": {
    "BaseUrl": "http://localhost:11434",
    "Model": "gemma3:4b",
    "Temperature": 0.3,
    "MaxTags": 5,
    "Enabled": true
  }
}

J'utilise gemma3:4b comme il' est assez rapide et petit pour fonctionner localement sans beaucoup de fuss . La basse température (0.3) maintient la cohérence de l'extrait – nous ne voulons pasM SK4 ne veux pas des hallucinations créatives dans nos balisesMSC5 RemarqueMNK6 pour mon blog cela a fonctionné car les articles étaient courtsMMK7 si votre est plus long, assurez-vous d'examiner les techniques de couplage et de récapitulation qui conviennent à votre modèle MNK8s fenêtre contextuelleMRK9

Traitement des postes plus longs

Il s’agit là d’une limitation pratique. Les LLM ont des fenêtres contextuelles et il est inutile de pomper dans elles un tout post sur le blog pour produire les balises.

var truncatedContent = content.Length > 3000
    ? content[..3000] + "..."
    : content;

Pour la génération de balises, les premiers caractères , contiennent habituellement suffisamment de contexte pour comprendre le sujet du post.

Generate up to 5 tags, short (1-3 words), focusing on:
.NET, C#, ASP.NET, JavaScript, Docker, Database, API, Security, DevOps, Cloud

L’analyse de la réponse est défensive – si Ollama retourne du gaspillage ou l’évite , nous obtenons simplement une liste d’étiquettes vides plutôt que de crasher l’ensemble du pipeline

Extraction de la date: Approche stratégique multilatérale

Trouver la date de publication originale est surprenantement compliqué. Mon vieux blog a stocké les dates dans différents endroits au fil des ansM SK1 L’outil essaie plusieurs stratégies:

  1. Définition du choix (eM SK1g., .postfoot)
  2. Méta-étiquettes: article:published_time, DC.date.issued
  3. HTML5 <time> Éléments
  4. Classes de date communes: .date, .post-date, .entry-date
  5. Patterns de Regex sur HTML brut (Format ISO,slashM SK2separated , etcMSC4
  6. Fallback: La date de prise d'image archivée elle-même

Un format particulièrement inquiétant a été mon vieux modèle de blog's "posted on ThursdayM SK2 May 27,

L'orchestre du pipeline

Le bit architectural le plus cool est la façon dont le téléchargement et la conversion se déroulent parallèlement en utilisant .NET Channels:

var channel = Channel.CreateBounded<string>(10);

// Producer: downloads and writes file paths to channel
// Consumer: reads file paths and converts to markdown

Cela signifie que la conversion commence dès le premier téléchargement de fichiers, plutôt qu’à attendre que tous les téléchargements soient terminés . La capacité limitée M SK2 des éléments) fournit une pression d’arrière - si la conversion tombe derrièreMSC5 le téléchargeur ralentit pour correspondre à ce qui est prévuMNK6

Limitations et gains

Nous devons être honnêtes quant à ce que cet outil peut faire.

  1. C' est très spécifique à mon blog – Les sélecteursM SK1 les patrons , et l’extraction des dates sont tous ajustés pour la plupartlucid.coMNK4ukMMK5 VousMSC6d avez besoin de customiser tout pour un site différentMRK7

  2. Archive.org ne possède pas – Certaines pages n’ont pas été archivées't archivés , ou ont été archivés avec un CSS défectueuxMSC3imagesM SK4

  3. Images sont les meilleures-effort - L'outil essaie de télécharger des images de la machine de retour, mais beaucoup sont simplement perdus pour toujours.

  4. Liens morts partout - Liens externes de 2004 pointent vers des sites qui ne sont plus existants. IM SK3 je travaille à une solution distincte pour ce archive automatiséeMSC5 substitution du lienorg dans un logiciel de middleware

  5. Les balises LLM sont incomplètes – Les balises générées par OllamaM SK1 sont habituellement sensibles , mais occasionnellement manquent la marque. Il est recommandé d’examiner manuellement les balises.

  6. CDX API truncation – Pour les sites comportant des milliers de pages, l’API CDX peut retourner des résultats tronqués . Le code le gère gracieusement, mais vous pourriez manquer quelques pages

Faire fonctionner

Si vous voulez adapter ce pour votre propre site (it'll'aura besoin d'une customizationM SK2 les commandes sont

# Full pipeline (download + convert)
dotnet run -- full

# Just download HTML from Archive.org
dotnet run -- download

# Just convert existing HTML files to Markdown
dotnet run -- convert

L'outil appuie la fermeture gracieuse (Ctrl+CM SK2 et peut reprendre à partir de là où il a été reporté puisque les fichiers sont cachés localement .

Les résultats

Après avoir mené ce sur mon vieux blog, j’ai récupéré des articles remontant à Janvier 1stM SK1 2004 et avant! La lecture de ces articles est un voyage fascinant dans le temps . Discussions sur le développement du Web d’avant que jQuery n’existe. Posts sur les technologies qui sont maintenant complètement désuètesM SK3 Et quelques opinions embarrassantes que j’ai eues en tant que jeune développeur

Vous pouvez trouver tous les postes importés en utilisant le Importation tag de catégorie.

Quoi' Suivant

Le contenu importé a un LOT de liens morts. a construit une solution de middleware que:

  1. Détecter 404s pour les anciens URL
  2. Trouver automatiquement l'archive la plus proche.org snapshot
  3. Rediriger ou afficher la version archivée

Établissement

Construire cet outil a été un projet de fin de semaine qui s'est transformé en quelque chose vraiment utile. Si vous ' avez perdu du contenu d'un vieux blog, il y a làM SK3 une chance décente ArchiveMSC4org l'aMSSK5 Et si vous

Le code est à github.com /scottgal/mostlylucidM SK3nugetpackages. Il n’est pas une bibliothèque polissée, mais un outil construit pour ma situation particulière.

Et sérieusement, je vais faire un don à l’Archive.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.