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

<!--category-- Imported, .NET, Archive.org -->
<datetime class="hidden">2025-11-24T12:00</datetime>

Ви, можливо, помітили сотні. [" Новий " блог](https://www.mostlylucid.net/blog/category/Imported) Останнім часом вони зовсім не нові, вони старі, як, наприклад, 2004 р. я нарешті зробив інструмент, щоб врятувати свій контент з цифрового кладовища, який був моїм старим блогом у centalluciad.co.uk.

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

Перед тим, як я занурюся в технічні речі, я повинен дати величезний крик для [Інтернет- архів](https://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. **Пізніші роки**: Ще одна реструктура з дещо різними інструментами вибору

З пам' яті він використовував якусь нетипову річ тоді [Підтекст](http://beletsky.net/2010/09/subtext-open-source-blogging-engine.html) (Щодо блогу у Філа Хака). Послідовно на сервері громад (а) [Телігент](https://community.telligent.com/) ASP.NET використовувався для сайтів, автором яких був ASP, NET PM Rob Говард). Всі вони мали різні способи оновлення та різні способи відображення змісту.

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

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

Я побудований. [Архіватор OrgImporter](https://github.com/scottgal/mostlylucid.nugetpackages/tree/main/Mostlylucid.ArchiveOrg) щоб розв' язати цю дуже специфічну проблему. Це консольна програма .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](https://github.com/internetarchive/wayback/tree/master/wayback-cdx-server) Щоб знайти всі архівовані знімки мого блогу. Цей API поверне список отриманих адрес URL з часовими штампами, типами MIME і кодами стану HTTP.

```csharp
// 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`Так.

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

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

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

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

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

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

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

```csharp
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:

```json
{
  "ContentSelector": "div.post"
}
```

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

```csharp
// 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` метод використовує ієрархічний пошук для пошуку вмісту:

```csharp
// 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>` якщо всі інші зазнають невдачі

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

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

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

Після того, як у нас буде чистий зміст HTML, [BackMarkdown](https://github.com/mysticmind/reversemarkdown-net) керує важким підйомом на GitHub-flavored Markdown.

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

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

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

```markdown
# Article Title

<datetime class="hidden">2004-05-27T23:21</datetime>
<!--category-- mostlylucidcouk, Imported, SomeCategory -->

Article content here...
```

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

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

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

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

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

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

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

```csharp
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:

```csharp
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 може повернути обрізані результати. Код використовує це граціозно, але ви можете пропустити деякі сторінки.

## Запуск

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

```bash
# 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 року](/blog/365) і раніше! читання крізь них є захопливою подорожжю у часі. обговорення веб-розробки ще до появи Джоурі. а також деякі незручні погляди, які я вважаю молодшими розробниками.

Ви можете знайти всі імпортовані дописи за допомогою [Імпортовано](/blog/category/Imported) мітка категорії.

## Що наступне

В імпортованому контенті є код мертвих зв'язків, це просто природа 20-річного веб- контенту. [побудова розв' язання середньої програми](/blog/the-war-on-404) те:

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

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

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

Код в [gitub.com/scottgal/ methlylucid. nuget packages](https://github.com/scottgal/mostlylucid.nugetpackages/tree/main/Mostlylucid.ArchiveOrg)Это не библиотека, что создала цель для моей личной ситуации, но это может дать тебе идеи по твоим собственным приключениям.

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