Quindi potresti aver notato centinaia di "nuovi" post sul blog Beh, non sono affatto nuovi - sono vecchi. Come, 2004 vecchio. Ho finalmente costruito uno strumento per salvare i miei contenuti dal cimitero digitale che era il mio vecchio blog a per lo piùlucid.co.uk.
Prima di immergermi nella roba tecnica, devo dare un enorme grido al Archivio Internet Questa organizzazione no-profit archivia tranquillamente il web dal 1996, conservando miliardi di pagine web che altrimenti andrebbero perse per sempre.
Pensaci per un secondo. Ogni post del blog che hai scritto nel 2005, ogni pagina di GeoCities, ogni profilo di MySpace - c'è una buona possibilità che sia ancora accessibile tramite Archive.org. Stanno fondamentalmente dirigendo un museo di tutto Internet, finanziato da donazioni e sovvenzioni.
Quando il mio vecchio provider di hosting è scomparso (insieme ai miei backup perché ero SMART così), ho pensato che tutto quel contenuto era andato per sempre. A quanto pare la Wayback Machine era stata diligentemente istantanea il mio sito per anni. Archive.org abbastanza letteralmente salvato 6+ anni della mia storia blogging.
Se non hai mai donato loro, considera di farlo. Stanno preservando la nostra storia digitale collettiva.
Ecco come funziona un blog dal 2004 al 2010 - le tecnologie web hanno cambiato molto durante quel periodo. E, a quanto pare, ho cambiato il mio setup blogging almeno tre volte:
<div class="post"> all'interno di un <form> elemento (perché tutto era una forma allora)Dalla memoria ha usato Qualche cosa personalizzata allora SubText (La cosa blogging di Phil Haack). Seguito da Community Server (a TelligentCity name (optional, probably does not need a translation) cosa ASP.NET utilizzato per ospitare i siti, da ANTHER ex ASP,NET PM Rob Howard) . Tutti i quali avevano diversi modi di aggiornare e diversi modi di visualizzare i contenuti.
Questo significava che qualsiasi strumento di estrazione doveva essere abbastanza flessibile da gestire strutture HTML multiple. Un raschietto "one size si adatta a tutti" non lo avrebbe tagliato.
L'ho costruito io. ArchivioOrgImportatore per risolvere questo problema molto specifico. Si tratta di un .NET 9.0 applicazione console che:
Lo strumento segue un'architettura pipeline con tre fasi principali:
Archive.org CDX API → Download HTML → Convert to Markdown → Generate Tags → Output Files
Lo strumento utilizza quello di Archive.org API CDX per trovare tutte le istantanee archiviate del mio blog. Questa API restituisce un elenco di URL catturati con timestamp, tipi MIME e codici di stato HTTP.
// The CDX query builds a URL like this:
// https://web.archive.org/cdx/search/cdx?url=mostlylucid.co.uk/posts/&output=json&collapse=urlkey
La collapse=urlkey parametro è intelligente - restituisce solo l'ultima istantanea per ogni URL unico, che riduce notevolmente il numero di duplicati che è necessario affrontare.
Uso anche i modelli regex per filtrare gli URL. I miei vecchi post hanno seguito il modello /posts/[number].aspx, quindi:
{
"IncludePatterns": ["/posts/\\d+\\.aspx$"]
}
In questo modo ho solo afferrare post sul blog reale, non le pagine di archivio, pagine di categoria, o feed RSS.
Archive.org è un servizio pubblico. Non hanno il budget infrastrutturale di Google o Amazon. Quindi il downloader è deliberatamente conservatore:
// Default: 5 seconds between requests, single-threaded downloads
"RateLimitMs": 5000,
"MaxConcurrentDownloads": 1
Sì, questo significa scaricare centinaia di messaggi richiede un po '. Ma è la cosa giusta da fare. Lo strumento gestisce anche 429 (limite di tasso) risposte con grazia con backoff esponenziale.
C'è anche qualche pulizia necessaria per i file scaricati. Archive.org aggiunge script sulla barra degli strumenti e riscrive gli URL nell'HTML catturato. Il downloader rimuove tutto ciò:
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;
}
Il manico dei modelli regex:
<!-- BEGIN WAYBACK TOOLBAR INSERT -->...<!-- END WAYBACK TOOLBAR INSERT --> - la barra degli strumenti HTML<script> riferimenti ai tag playback.archive.orghttps://web.archive.org/web/20040527/ che vengono preparati a tutti i linkQuesto è dove le cose diventano interessanti. Ricordate che ho menzionato il mio blog cambiato struttura tre volte? Ecco come ho gestito.
L'estrazione primaria utilizza un selettore CSS:
{
"ContentSelector": "div.post"
}
Ma ecco una divertente stranezza - in alcune versioni del mio vecchio sito, il div.post era ALL'INTERNO di un <form> elemento. Il codice deve estrarre il contenuto PRIMA di rimuovere gli elementi indesiderati, altrimenti rimuovendo <form> tags nuke il contenuto effettivo del 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);
La ExtractMainContent metodo utilizza una ricerca gerarchica per trovare il contenuto:
// 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));
Lo strumento dispone anche di selettori di ripiego se quello primario fallisce:
div.blogpost, div.singlepost, article#content, #main-content, #PostBody.post-content, .entry-content, .article-content<body> se tutto il resto falliscePoi c'è una lunga lista di elementi da rimuovere dopo l'estrazione del contenuto:
{
"RemoveSelectors": [
"nav", "header", "footer", ".sidebar", ".advertisement",
".comments", ".social-share", ".related-posts", "script",
"style", "noscript", "iframe", "#commentform", ".postNav"
]
}
Una volta che abbiamo pulito il contenuto HTML, ReverseMarkdown gestisce il sollevamento pesante di converterlo in GitHub-saporato Markdown.
Ma l'output aveva bisogno di un po' di post-elaborazione. Il vecchio HTML spesso aveva un indentazione strana che confondeva i parser Markdown (le linee indentate diventano blocchi di codice!). Quindi c'è pulizia:
// Removes leading whitespace from non-code lines
// Preserves code block formatting (respects ``` fences)
// Removes excessive blank lines (max 2 consecutive)
L'output finale include il formato front matter del mio blog:
# Article Title
Article content here...
Ora per la parte divertente. I vecchi post del blog spesso non avevano tag a tutti, o tag che non hanno senso per la mia struttura attuale del sito. Così ho integrato Ollama per analizzare i contenuti e generare tag rilevanti.
La configurazione è semplice:
{
"Ollama": {
"BaseUrl": "http://localhost:11434",
"Model": "gemma3:4b",
"Temperature": 0.3,
"MaxTags": 5,
"Enabled": true
}
}
Uso gemma3:4b come è veloce e abbastanza piccolo per funzionare localmente senza molto trambusto. La bassa temperatura (0,3) mantiene l'uscita coerente • non vogliamo allucinazioni creative nei nostri tag. Nota: per il mio blog questo ha funzionato come i post erano brevi, se il vostro è più sicuro di guardare chunking e tecniche di summarizzazione per adattarsi alla finestra di contesto del vostro modello.
Ecco una limitazione pratica: LLM hanno finestre di contesto, e pompare un intero post del blog in loro per la generazione di tag è inutile. Lo strumento tronca contenuti a 3000 caratteri:
var truncatedContent = content.Length > 3000
? content[..3000] + "..."
: content;
Per la generazione di tag, i primi 3000 caratteri di solito contengono abbastanza contesto per capire che cosa il post è circa. Il prompt istruisce il modello per concentrarsi sulle categorie tech-specific:
Generate up to 5 tags, short (1-3 words), focusing on:
.NET, C#, ASP.NET, JavaScript, Docker, Database, API, Security, DevOps, Cloud
L'analisi della risposta è difensiva se Ollama restituisce spazzatura o tempi fuori, ci basta ottenere una lista di tag vuoti piuttosto che schiantare l'intero gasdotto.
Trovare la data di pubblicazione originale è sorprendentemente difficile. Il mio vecchio blog memorizzato date in luoghi diversi nel corso degli anni. Lo strumento prova strategie multiple:
.postfoot)article:published_time, DC.date.issued<time> elementi.date, .post-date, .entry-dateUn formato particolarmente fastidioso era il mio vecchio modello di blog "pubblicato giovedì 27 maggio 2004 11:21 PM" stringa. C'è specifica analisi per questo.
Il bit architettonico più cool è come il download e la conversione eseguire in parallelo utilizzando .NET Canali:
var channel = Channel.CreateBounded<string>(10);
// Producer: downloads and writes file paths to channel
// Consumer: reads file paths and converts to markdown
Ciò significa che la conversione inizia appena il primo download di file, piuttosto che aspettare che tutti i download per completare. La capacità limitata (10 elementi) fornisce contropressione - se la conversione cade indietro, il download rallenta per abbinare.
Siamo onesti su ciò che questo strumento non può fare:
E 'molto specifico per il mio blog I selettori, i modelli e l'estrazione della data sono tutti sintonizzati per la maggior partelucid.co.uk. Dovresti personalizzare tutto per un sito diverso.
Archive.org non ha tutto Alcune pagine non sono state archiviate, o sono state archiviate con immagini/CSS rotte.
Le immagini sono best-effort - Lo strumento cerca di scaricare le immagini dalla Wayback Machine, ma molti sono semplicemente andati per sempre.
Collegamenti morti ovunque - Collegamenti esterni dal 2004 punto a siti che non esistono più. Sto lavorando su una soluzione separata per questo (sostituzione automatica link Archive.org in un middleware - blog post in arrivo presto!).
I tag LLM sono imperfetti I tag generati da Ollama sono di solito sensibili, ma a volte mancano il marchio. Si raccomanda una recensione manuale.
Troncatura API CDX Per i siti con migliaia di pagine, l'API CDX può restituire risultati troncati. Il codice gestisce questo con grazia, ma si potrebbe perdere alcune pagine.
Se si desidera adattare questo per il proprio sito (è necessario personalizzare), i comandi sono:
# 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
Lo strumento supporta lo spegnimento grazioso (Ctrl+C) e può riprendere da dove ha lasciato i file dal momento che sono cache localmente.
Dopo aver eseguito questo sul mio vecchio blog, ho recuperato post risalenti a 1 gennaio 2004 E prima! Leggere attraverso di loro è un viaggio affascinante attraverso il tempo. Lo sviluppo Web discussioni da prima jQuery esisteva. Posts su tecnologie che ora sono completamente obsolete. E alcune opinioni imbarazzanti che ho tenuto come uno sviluppatore più giovane.
È possibile trovare tutti i messaggi importati utilizzando il Importato categoria tag.
Il contenuto importato ha un sacco di link morti che è solo la natura del 20-year-old contenuto web. I costruito una soluzione middleware che:
Costruire questo strumento è stato un progetto del fine settimana che si è trasformato in qualcosa di veramente utile. Se hai perso il contenuto da un vecchio blog, c'è una buona probabilità che Archive.org ce l'abbia. E se ti senti a tuo agio con C#, le tecniche qui (chiesa API CDX, estrazione di contenuti HTML, generazione Markdown, tag LLM) potrebbero essere adattate per il tuo progetto di recupero.
Il codice è a github.com/scottgal/mostlylucid.nugetpackages. Non è una libreria levigata è uno strumento appositamente costruito per la mia situazione specifica - ma potrebbe darvi idee per le vostre avventure d'archivio.
E sul serio, vai a donare ad Archive.org. Stanno facendo un lavoro importante.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.