Back to "Повторення мого старого блогу за допомогою Archive.org і безлічу C#"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

.NET Archive.org Imported

Повторення мого старого блогу за допомогою Archive.org і безлічу C#

Monday, 24 November 2025

Ви, можливо, помітили сотні. " Новий " блог Останнім часом вони зовсім не нові, вони старі, як, наприклад, 2004 р. я нарешті зробив інструмент, щоб врятувати свій контент з цифрового кладовища, який був моїм старим блогом у centalluciad.co.uk.

Чому Archive.org є абсолютно блискучим

Перед тим, як я занурюся в технічні речі, я повинен дати величезний крик для Інтернет- архів Ця некомерційна організація тихо ацтекує веб- сайт з 1996 року, зберігаючи мільярди веб- сторінок, які в іншому випадку були б назавжди втрачені.

Подумайте про це на секунду. кожен допис блогу, який ви написали 2005 року, кожні сторінки GeoCities, кожен профіль MySpace - існує пристойний шанс, що він все ще доступний за допомогою Archive.org.

Після того, як мій старий постачальник послуг- господар зник (майже з моїми резервними копіями через те, що я була SMART ось так), я подумав, що весь цей вміст зник назавжди. Виявилося, що комп' ютер Вейбард вже роками ретельно фотографував мій сайт. Архів.org досить буквально зберіг 6+ років мого блогу.

Якщо ви ніколи не жертвували їм, подумайте про це, вони зберігають нашу колективну цифрову історію.

Проблема: мій блог був центром Меси

Ось що можна сказати про запуск блогу у період з 2004 по 2010 рік - веб-технології змінили LETS протягом того часу, і, очевидно, я змінив налаштування блогу щонайменше тричі:

  1. Перші дні (2004): Деякі речі housebrew ASP.NET з контентом, загорненим всередину <div class="post"> всередині a <form> елемент (оскільки все було формою в той час)
  2. Середній період: різна структура шаблонів, дати у різних місцях
  3. Пізніші роки: Ще одна реструктура з дещо різними інструментами вибору

З пам' яті він використовував якусь нетипову річ тоді Підтекст (Щодо блогу у Філа Хака). Послідовно на сервері громад (а) Телігент ASP.NET використовувався для сайтів, автором яких був ASP, NET PM Rob Говард). Всі вони мали різні способи оновлення та різні способи відображення змісту.

Це означало, що будь-який інструмент для видобування достатньо гнучкий, щоб працювати з декількома структурами HTML. "один розмір відповідає всім" скребкам не збирався обрізати його.

Введіть інструмент архівуванняOrgImporter

Я побудований. Архіватор OrgImporter щоб розв' язати цю дуже специфічну проблему. Це консольна програма .NET 9. 0, яка:

  1. Повага до обмежень використання Archive.org - Вони некомерційно працюють на пожертвах, тож робити їх сервери було б жахливо.
  2. Звантажує застарілі сторінки між придатними для налаштування датами
  3. Видобуває вміст блогу з декількох структур HTMLName
  4. Створює чисту розмітку у моєму форматі блогу
  5. Використовувати Ollama для створення корисних міток - тому що чому б не кинути в нього трохи магії LLM?

Як це працює

Інструмент слідує за архітектурою трубопроводу з трьома основними фазами:

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

Фаза 1: опитування архіву

Інструмент використовує Archive.org CDX API Щоб знайти всі архівовані знімки мого блогу. Цей API поверне список отриманих адрес URL з часовими штампами, типами MIME і кодами стану 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

The collapse=urlkey Параметр є кмітливим - він повертає лише найсвіжіший знімок для кожної унікальної адреси URL, що значно зменшує кількість дублікатів, з якими вам слід працювати.

Крім того, я використовую шаблони regex для фільтрування адрес URL. Мої старі дописи слідували за шаблоном /posts/[number].aspxТак.

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

Таким чином, я перехоплюю лише справжні дописи блогів, а не сторінки архівів, сторінки категорій або подачі RSS.

Фаза 2: бути добрим громадянином з обмеженням

Archive.org - це державна служба, у них немає бюджету інфраструктури на Google або Amazon.

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

Так, це означає, що звантаження сотень дописів триває деякий час, але це правильно. Інструмент також керує 429 (рівне обмеження) відповідьми з експоненціальним зворотним зв' язком.

Крім того, потрібне деяке спорожнення звантажених файлів. Archive.org додає скрипти панелі інструментів і перезаписує адреси URL у отриманому HTML. Вилучувачі все це надсилають:

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;
}

Керування шаблонами формального виразу:

  • <!-- BEGIN WAYBACK TOOLBAR INSERT -->...<!-- END WAYBACK TOOLBAR INSERT --> - пенал HTML
  • <script> посилання на мітки playback.archive.org
  • Архівувати метадані коментарі
  • Префікси адрес на зразок https://web.archive.org/web/20040527/ те, що підготовлятися до всіх посилань

Фаза 3: Видобування вмісту страшить

Тут все стає цікаво, пам'ятаєте, я згадував, що мій блог тричі змінював структуру?

Основне видобування використовує засіб вибору CSS:

{
  "ContentSelector": "div.post"
}

Але ось весела викрутка в деяких версіях мого старого сайту div.post був ДУЖЕ ВАЖЛИВИЙ <form> element. Код має видобути вміст B перш ніж вилучати небажані елементи, у іншому випадку вилучити <form> tags буде 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);

The ExtractMainContent метод використовує ієрархічний пошук для пошуку вмісту:

// 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));

Крім того, у інструменті передбачено інструмент вибору зворотного повернення, якщо перший з цих інструментів зазнає невдачі:

  1. div.blogpost, div.singlepost, article
  2. #content, #main-content, #PostBody
  3. .post-content, .entry-content, .article-content
  4. Нарешті, тільки <body> якщо всі інші зазнають невдачі

Крім того, існує довгий список елементів, які вилучаються після видобування матеріалу:

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

Покоління відмітки фази 4:

Після того, як у нас буде чистий зміст HTML, BackMarkdown керує важким підйомом на GitHub-flavored Markdown.

Але на виході потрібні були кінцеві обробки. Старий HTML часто мав дивні відступи, які могли б спантеличити аналізаторів Markdown (встановлені рядки стають блоками коду!) Отже, існує чистка:

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

Остаточний вивід містить передній формат даних мого блогу:

# Article Title




Article content here...

Фаза 5: створення міток LLM- Polid

Тепер весела частина: дописи старих блогів часто взагалі не мали міток, або міток, які не мали сенсу для моєї поточної структури сайта, тому я інтегрував Олламу з аналізу вмісту і створення відповідних міток.

Налаштування прості:

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

Я користуюся gemma3:4b Низька температура (0,3) зберігає виведені дані послідовними, ми не хочемо творчих галюцинацій у наших тегах. Примітка; для мого блогу це працювало, як пости є короткими, якщо ваша більш довша, щоб дивитися на прийоми розбиття і узагальнення, що вміщуються у контекстному вікні вашої моделі.

Робота з довшими дописами

Це практичне обмеження: LLM мають контекстні вікна і перекачує до них весь допис блог для створення міток. Інструмент truncatecates вміст до 3000 символів:

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

Для створення теґів перші 3000 символів, зазвичай, містять достатньо контексту, щоб зрозуміти суть повідомлення. За допомогою запиту можна вказати моделі, що слід зосередитися на категоріях, специфічних для технічного типу:

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

Опрацювання полягає в тому, що якщо Ольма поверне сміття або відключиться, ми отримаємо порожній список теґів, замість того, щоб розбити весь трубопровод.

Видобування дат: Багатогранний підхід

Знайти початкову дату публікації на диво складно. Мій старий блог зберіг дати у різних місцях протягом років. Інструмент виконає декілька стратегій:

  1. Налаштований вибір (e.g., .postfoot)
  2. Метатеґи: article:published_time, DC.date.issued
  3. HTML5 <time> елементи
  4. Загальні класи дат: .date, .post-date, .entry-date
  5. Шаблони формального виразу у форматі без обробки HTML (ISO, відокремлений похилою рискою тощо)
  6. Повернення: Дата знімка архіву

Особливо дратує те, що мій старий шаблон блогу " поклав у четвер 27 травня 2004 року.

Окуляр трубопроводу

Найхолоднішим архітектурним бітом є робота з звантаження і перетворення паралельно за допомогою каналів. NET:

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

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

Це означає, що перетворення починатиметься одразу після першого звантаження файлів, а не очікування на завершення всіх звантажень. Обмежена місткість (10 елементів) забезпечує зворотний тиск, - якщо перетворення відступає, звантаження звантажується так, щоб збігався.

Обмеження і піймання

Давайте будемо чесними щодо того, що цей інструмент не може зробити:

  1. Вона дуже специфічна для мого блогу. Все эти часы, сделаны для qualcid.co.uk.Тебе нужно объединить все для разного места.

  2. Archive.org не має всього ♫ Деякі сторінки не були архівовані, або були архівовані зі зламаними CSS/images.

  3. Зображення найкращі для зображеньName - Інструмент намагається завантажити зображення з Машини Вейвера, але багато з них просто зникають назавжди.

  4. Повсюди мертві посилання - Зовнішні посилання з 2004 року вказують на сайти, яких вже немає. Я працюю над окремим розв' язком для цього (автоматично Archive.org пов' язує заміну за допомогою поля посередині блогу, що наближається найближчим часом!).

  5. Мітки LLM недосконалі ▸ Створені теґи зазвичай є розсудливими, але іноді їх не помічають. Рекомендуємо переглянути їх вручну.

  6. truncation CDX API ♫ За сайтами з тисячами сторінок, API CDX може повернути обрізані результати. Код використовує це граціозно, але ви можете пропустити деякі сторінки.

Запуск

Якщо ви бажаєте пристосувати цей параметр до вашого власного сайта (потрібна його налаштування), цими командами будуть:

# 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

Інструмент підтримує граційне завершення роботи (Ctrl+C) і може поновлюватися з того місця, де його не було позначено, оскільки файли зберігаються локально у кеші.

Результати

Після того, як я запустив це у своєму старому блозі, я відновив дописи, що датуються до 1 січня 2004 року і раніше! читання крізь них є захопливою подорожжю у часі. обговорення веб-розробки ще до появи Джоурі. а також деякі незручні погляди, які я вважаю молодшими розробниками.

Ви можете знайти всі імпортовані дописи за допомогою Імпортовано мітка категорії.

Що наступне

В імпортованому контенті є код мертвих зв'язків, це просто природа 20-річного веб- контенту. побудова розв' язання середньої програми те:

  1. Визначення 404- х для старих адрес URL
  2. Автоматично знайти найближчий знімок Archive.org
  3. Переспрямувати або показати застарілу версію

Перенесення вгору

Побудова цього інструменту була проектом на вихідні, який перетворився на щось справді корисне. Якщо ви втратили вміст зі старого блогу, його можна було б використати у архіві.org. І, якщо вам зручно використовувати C#, методи (перемотування CCDX API, видобування вмісту HTML, створення Markdown, створення LLM taging) можна адаптувати до вашого власного проекту відновлення.

Код в gitub.com/scottgal/ methlylucid. nuget packagesЭто не библиотека, что создала цель для моей личной ситуации, но это может дать тебе идеи по твоим собственным приключениям.

І серйозно, ідіть з пожертвами на Archive.org, вони виконують важливу роботу.

logo

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