Mein altes Blog mit Archive.org und einer Menge C# auferstehen
So könnten Sie bemerkt haben, Hunderte von "neue" Blog-Beiträge Vor kurzem erschienen. Nun, sie sind überhaupt nicht neu - sie sind OLD. Wie 2004 alt. Ich habe endlich ein Werkzeug gebaut, um meine Inhalte aus dem digitalen Friedhof zu retten, der mein alter Blog auf meistlucid.co.uk war.
Warum Archive.org absolut brillant ist
Bevor ich in das technische Zeug eintauche, muss ich dem Internet-Archiv und ihre Wayback-Maschine. Diese Non-Profit-Organisation archiviert das Web seit 1996 ruhig und bewahrt Milliarden von Webseiten, die sonst für immer verloren gehen würden.
Denken Sie eine Sekunde darüber nach. Jeder Blogbeitrag, den Sie 2005 geschrieben haben, jede GeoCities-Seite, jedes MySpace-Profil - es gibt eine gute Chance, dass es immer noch über Archive.org zugänglich ist. Sie betreiben im Wesentlichen ein Museum des gesamten Internets, finanziert durch Spenden und Zuschüsse.
Als mein alter Hosting-Provider verschwand (zusammen mit meinen Backups, weil ich SMART war so), dachte ich, dass alle Inhalte für immer verschwunden waren. Es stellte sich heraus, dass die Wayback-Maschine meine Website seit Jahren fleißig snapshoting. Archive.org ganz buchstäblich gespeichert 6+ Jahre meiner Blogging-Geschichte.
Wenn du ihnen noch nie gespendet hast, dann überlege es dir, sie bewahren unsere kollektive digitale Geschichte.
Das Problem: Mein Blog war ein Fehler
Hier ist die Sache über den Betrieb eines Blogs von 2004 bis 2010 - Web-Technologien verändert A LOT während dieser Zeit. Und offenbar habe ich meine Blogging-Setup mindestens dreimal geändert:
- Frühe Tage (2004): Einige homebrew ASP.NET Sache mit Inhalt eingewickelt in
<div class="post">in einem<form>Element (weil damals alles eine Form war) - Mittlerer Zeitraum: Verschiedene Vorlagenstruktur, Termine an verschiedenen Orten
- Spätere Jahre: Eine weitere Umstrukturierung mit leicht unterschiedlichen Selektoren
Aus dem Speicher verwendet es etwas benutzerdefinierte Sache dann Untertext (Phil Haacks Blogging-Ding) gefolgt von Community Server (a) Telligent Sache ASP.NET verwendet, um Websites zu hosten, von ANOTHER ex ASP,NET PM Rob Howard) . ALL von denen hatte verschiedene Möglichkeiten der Aktualisierung und verschiedene Möglichkeiten der Anzeige von Inhalten.
Dies bedeutete, dass jedes Extraktionstool flexibel genug sein musste, um mehrere HTML-Strukturen zu handhaben. Ein "One size fits all"-Schablonen würde ihn nicht schneiden.
Geben Sie den ArchivOrgImporter ein
Ich habe gebaut ArchivOrgImporter um dieses sehr spezifische Problem zu lösen. Es ist eine .NET 9.0 Konsolen-Anwendung, die:
- Respektiert die Nutzungsgrenzen von Archive.org - Sie sind ein Non-Profit, der mit Spenden arbeitet, also wäre es eine schreckliche Sache, ihre Server zu hämmern.
- Downloads archivierte Seiten zwischen konfigurierbaren Daten
- Extrahiert Blog-Inhalte aus mehreren HTML-Strukturen
- Erzeugt sauberen Markdown in meinem aktuellen Blog-Format
- Benutzt Ollama, um nützliche Tags zu generieren - denn warum nicht etwas LLM-Magie darauf werfen?
Wie es funktioniert
Das Tool folgt einer Pipeline-Architektur mit drei Hauptphasen:
Archive.org CDX API → Download HTML → Convert to Markdown → Generate Tags → Output Files
Phase 1: Abfrage des Archivs
Das Tool verwendet Archive.org's CDX API um alle archivierten Snapshots meines Blogs zu finden. Diese API gibt eine Liste der erfassten URLs mit Zeitstempeln, MIME-Typen und HTTP-Statuscodes zurück.
// The CDX query builds a URL like this:
// https://web.archive.org/cdx/search/cdx?url=mostlylucid.co.uk/posts/&output=json&collapse=urlkey
Das collapse=urlkey Parameter ist clever - es gibt nur den neuesten Snapshot für jede eindeutige URL zurück, was die Anzahl der Duplikate, mit denen Sie umgehen müssen, drastisch reduziert.
Ich benutze auch Regex-Muster, um URLs zu filtern. Meine alten Beiträge folgten dem Muster /posts/[number].aspx, so:
{
"IncludePatterns": ["/posts/\\d+\\.aspx$"]
}
Auf diese Weise packe ich nur aktuelle Blog-Posts, nicht die Archiv-Seiten, Kategorie-Seiten oder RSS-Feeds.
Phase 2: Ein guter Bürger mit zinsbeschränkender Wirkung sein
Archive.org ist ein öffentlicher Dienst. Sie haben nicht das Infrastrukturbudget von Google oder Amazon. So ist der Downloader bewusst konservativ:
// Default: 5 seconds between requests, single-threaded downloads
"RateLimitMs": 5000,
"MaxConcurrentDownloads": 1
Ja, das bedeutet, Hunderte von Beiträgen zu downloaden dauert eine Weile. Aber es ist das Richtige zu tun. Das Tool behandelt auch 429 (Rate Limit) Antworten anmutig mit exponentiellen Backoff.
Es gibt auch einige cleanup benötigt für heruntergeladene Dateien. Archive.org fügt Toolbar-Skripte hinzu und schreibt URLs in der aufgenommenen HTML. Der Downloader strips all that out:
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;
}
Die Regex-Muster Griff:
<!-- BEGIN WAYBACK TOOLBAR INSERT -->...<!-- END WAYBACK TOOLBAR INSERT -->- die Symbolleiste HTML<script>Verweise auf Tagsplayback.archive.org- Kommentare zu Metadaten des Archivs
- URL-Präfixe wie
https://web.archive.org/web/20040527/die auf alle Links vorbereitet werden
Phase 3: The Content Extraction Nightmare
Hier werden die Dinge interessant. Erinnern Sie sich, dass ich meine Blog-Struktur dreimal geändert? Hier ist, wie ich es behandelt.
Die primäre Extraktion verwendet einen CSS-Selektor:
{
"ContentSelector": "div.post"
}
Aber hier ist ein Spaß quirk - in einigen Versionen meiner alten Website, die div.post war INSIDE ein <form> element. Der Code muss Inhalt VOR dem Entfernen unerwünschter Elemente extrahieren, ansonsten entfernen <form> Tags würden den eigentlichen Blog-Inhalt nuke:
// 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);
Das ExtractMainContent Methode verwendet eine hierarchische Suche, um den Inhalt zu finden:
// 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));
Das Tool hat auch Fallback-Selektoren, wenn das primäre fehlschlägt:
div.blogpost,div.singlepost,article#content,#main-content,#PostBody.post-content,.entry-content,.article-content- Schließlich, nur die
<body>wenn alles andere fehlschlägt
Dann gibt es eine lange Liste von Elementen, um die AFTER-Inhaltsextraktion zu entfernen:
{
"RemoveSelectors": [
"nav", "header", "footer", ".sidebar", ".advertisement",
".comments", ".social-share", ".related-posts", "script",
"style", "noscript", "iframe", "#commentform", ".postNav"
]
}
Phase 4: Markdown Generation
Sobald wir saubere HTML-Inhalte haben, UmkehrenMarkdown behandelt das schwere Heben der Umwandlung in GitHub-geschminkt Markdown.
Aber die Ausgabe brauchte etwas Nachbearbeitung. Altes HTML hatte oft eine seltsame Einrückung, die Markdown-Parser verwechseln würde (indizierte Zeilen werden zu Codeblöcken!).
// Removes leading whitespace from non-code lines
// Preserves code block formatting (respects ``` fences)
// Removes excessive blank lines (max 2 consecutive)
Die endgültige Ausgabe beinhaltet das Format meines Blogs:
# Article Title
Article content here...
Phase 5: LLM-Powered Tag Generation
Jetzt zum Spaß. Alte Blogposts hatten oft überhaupt keine Tags, oder Tags, die für meine aktuelle Sitestruktur keinen Sinn machten. So integrierte ich Ollama, um Inhalte zu analysieren und relevante Tags zu generieren.
Die Konfiguration ist einfach:
{
"Ollama": {
"BaseUrl": "http://localhost:11434",
"Model": "gemma3:4b",
"Temperature": 0.3,
"MaxTags": 5,
"Enabled": true
}
}
Ich verwende gemma3:4b da es schnell und klein genug ist, um lokal ohne viel Aufsehen zu laufen. Die niedrige Temperatur (0,3) hält die Ausgabe konstant – wir wollen keine kreativen Halluzinationen in unseren Tags. Beachten Sie; für meinen Blog funktionierte dies, da die Beiträge kurz waren, wenn Sie länger sicher sind, sich mit Chunking- und Zusammenfassungstechniken zu beschäftigen, die in das Kontextfenster Ihres Modells passen.
Umgang mit längeren Posts
Hier ist eine praktische Einschränkung: LLMs haben Kontextfenster, und das Pumpen eines ganzen Blog-Posts in sie für die Tag-Generierung ist verschwenderisch. Das Tool verkürzt Inhalt auf 3000 Zeichen:
var truncatedContent = content.Length > 3000
? content[..3000] + "..."
: content;
Für die Tag-Generierung enthalten die ersten 3000 Zeichen in der Regel genügend Kontext, um zu verstehen, worum es beim Post geht. Die Aufforderung weist das Modell an, sich auf tech-spezifische Kategorien zu konzentrieren:
Generate up to 5 tags, short (1-3 words), focusing on:
.NET, C#, ASP.NET, JavaScript, Docker, Database, API, Security, DevOps, Cloud
Die Response Parsing ist defensiv – wenn Ollama Müll oder mal rausgibt, bekommen wir einfach eine leere Tagliste, anstatt die gesamte Pipeline abzustürzen.
Datumsentnahme: Ein Multi-Strategy-Ansatz
Die Suche nach dem ursprünglichen Veröffentlichungsdatum ist überraschend schwierig. Mein alter Blog gespeichert Daten an verschiedenen Orten im Laufe der Jahre. Das Tool versucht mehrere Strategien:
- Konfigurierter Selektor (z.B.
.postfoot) - Meta-Tags:
article:published_time,DC.date.issued - HTML5 Veröffentlichungen der Europäischen Gemeinschaften
<time>Elemente - Gemeinsame Datumsklassen:
.date,.post-date,.entry-date - Regex-Muster auf rohem HTML (ISO-Format, slash-separiert, etc.)
- Rückfall: Das Archiv-Snapshot-Datum selbst
Ein besonders ärgerliches Format war meine alte Blog-Vorlage "gepostet am Donnerstag, 27. Mai 2004 11:21" String. Dafür gibt es spezifische Parsen.
Die Pipeline-Orchestrierung
Das coolste architektonische Bit ist, wie Download und Konvertierung parallel mit .NET-Kanälen laufen:
var channel = Channel.CreateBounded<string>(10);
// Producer: downloads and writes file paths to channel
// Consumer: reads file paths and converts to markdown
Dies bedeutet, dass die Konvertierung beginnt, sobald die erste Datei heruntergeladen wird, anstatt darauf zu warten, dass alle Downloads abgeschlossen werden. Die begrenzte Kapazität (10 Elemente) sorgt für Rückdruck - wenn die Konvertierung zurückfällt, verlangsamt sich das Herunterladen auf Übereinstimmung.
Einschränkungen und Gotchas
Seien wir ehrlich darüber, was dieses Tool nicht kann:
Es ist sehr spezifisch für MEIN Blog – Die Selektoren, Muster und Datum Extraktion sind alle abgestimmt auf meistlucid.co.uk. Sie müssten alles für eine andere Website anpassen.
Archive.org hat nicht alles – Einige Seiten wurden nicht archiviert oder mit gebrochenen CSS/Bildern archiviert.
Bilder sind Best-Effort - Das Tool versucht, Bilder von der Wayback Machine herunterzuladen, aber viele sind einfach für immer verschwunden.
Überall tote Verbindungen - Externe Links von 2004 weisen auf Websites hin, die nicht mehr existieren. Ich arbeite an einer separaten Lösung dafür (automatic Archive.org Link Substitution in einer Middleware - Blog-Post kommt bald!).
LLM-Tags sind unvollkommen – Die Ollama-generierten Tags sind in der Regel sinnvoll, verpassen aber gelegentlich die Marke. Manuelle Überprüfung wird empfohlen.
CDX-API-Trinkung – Für Websites mit Tausenden von Seiten, kann die CDX API abgeschnittene Ergebnisse zurückgeben. Der Code behandelt dies anmutig, aber Sie könnten einige Seiten vermissen.
Es läuft
Wenn Sie dies für Ihre eigene Website anpassen möchten (es wird eine Anpassung erforderlich sein), sind die Befehle:
# 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
Das Tool unterstützt anmutiges Herunterfahren (Strg+C) und kann von dort, wo es aufgehört hat, wieder aufgenommen werden, da Dateien lokal zwischengespeichert werden.
Die Ergebnisse
Nachdem ich dies auf meinem alten Blog ausgeführt hatte, erholte ich mich von Beiträgen, die zurück zu 1. Januar 2004 und vor! Lesen durch sie ist eine faszinierende Reise durch die Zeit. Web-Entwicklung Diskussionen von vor jQuery existierte. Beiträge über Technologien, die jetzt völlig veraltet sind. Und einige peinliche Meinungen hielt ich als jüngerer Entwickler.
Sie können alle importierten Beiträge mit dem Einfuhr Kategorie-Tag.
Was kommt als Nächstes?
Der importierte Inhalt hat eine Menge toter Links – das ist nur die Natur von 20-jährigen Web-Inhalten. eine Middleware-Lösung gebaut dass
- 404s für alte URLs erkennen
- Finden Sie automatisch den nächsten Archive.org Snapshot
- Redirect oder Anzeige der archivierten Version
Einpacken
Wenn Sie Inhalte von einem alten Blog verloren haben, gibt es eine anständige Chance Archive.org hat es. Und wenn Sie sich mit C# wohl fühlen, könnten die Techniken hier (CDX API Abfrage, HTML Content Extraktion, Markdown Generation, LLM Tagging) für Ihr eigenes Recovery-Projekt angepasst werden.
Der Code ist bei github.com/scottgal/mostlylucid.nugetpackages. Es ist keine polierte Bibliothek – es ist ein zweckgebundenes Werkzeug für meine spezifische Situation – aber es könnte Ihnen Ideen für Ihre eigenen Archivabenteuer geben.
Und ernsthaft, spenden Sie an Archive.org. Sie leisten wichtige Arbeit.