Перейти к основному содержимому

DuckDB-WARC и DuckDB-CDX — SQL-запросы к веб-архивам

DuckDB — аналитическая in-process СУБД, идеально подходящая для анализа WARC-файлов. С 2024–2025 годов появились специализированные расширения, которые делают SQL-запросы к веб-архивам первоклассным паттерном.

Главный инсайт: вместо «прочитать WARC → распарсить заголовки → подсчитать» можно написать:

SELECT url, content_type, length FROM warc ORDER BY length DESC LIMIT 10;

Зачем нужны​

ЗадачаСтарый способС DuckDB-WARC
«Сколько страниц со словом „выборы" в архиве?»hours CIy + Pythonone query
«Самые большие записи в WARC»warcdump + grepSELECT * ORDER BY length DESC LIMIT 10
«CDX-индекс Internet Archive по домену»curl + парсингSELECT * FROM cdx WHERE url LIKE '%example.com%'
«Объединение WARC-а и CommonCrawl CDX»сложный ETLJOIN в одном запросе
«Deduplication по hash»hours Python-скриптаGROUP BY hash HAVING count > 1

DuckDB-warc (by Common Crawl)​

duckdb-warc — extension, добавляющее в DuckDB функции для чтения WARC и WARC.gz.

Установка​

# CLI DuckDB
pip install duckdb

# Extension
import duckdb
duckdb.sql("INSTALL duckdb_warc FROM community")
duckdb.sql("LOAD duckdb_warc")

Базовое использование​

import duckdb

con = duckdb.connect()
con.sql("INSTALL duckdb_warc FROM community")
con.sql("LOAD duckdb_warc")

# Прямой запрос к WARC
result = con.sql("""
SELECT
url,
content_type,
response_code,
length
FROM read_warc('archive.warc.gz')
WHERE response_code = 200
AND content_type LIKE 'text/html%'
ORDER BY length DESC
LIMIT 5
""").fetchall()

for row in result:
print(row)

Выгрузка результатов​

COPY (
SELECT url, content_type, length
FROM read_warc('archive.warc.gz')
WHERE response_code = 200
) TO 'urls-and-types.csv' (FORMAT 'csv', HEADER);

Поддерживаемые поля​

Каждая запись WARC доступна как STRUCT:

Поле WARC-заголовкаПоле DuckDB
WARC-Typewarc_type
WARC-Target-URIurl
WARC-Datewarc_date
WARC-Record-IDrecord_id
Content-Lengthlength
HTTP statusresponse_code
Content-Typecontent_type
WARC-Payload-Digestwarc_payload_digest

Фильтр по дате​

SELECT url, warc_date
FROM read_warc('archive.warc.gz')
WHERE warc_date BETWEEN '2024-01-01' AND '2024-12-31'
AND response_code = 200
LIMIT 100;

Декодирование тела ответа​

-- Достать title страницы
SELECT
url,
regexp_extract(
content_decode(warcio_extract_body(record)),
'<title>([^<]+)</title>', 1
) AS title
FROM read_warc('archive.warc.gz')
WHERE content_type LIKE 'text/html%'
LIMIT 10;

Внимание: декодирование больших HTTP-ответов может занимать время. Используйте LIMIT и фильтры.

Подсчёт уникальных доменов​

SELECT
regexp_extract(url, '^(?:https?://)?([^/]+)', 1) AS domain,
COUNT(*) AS pages
FROM read_warc('archive.warc.gz')
WHERE warc_type = 'response'
GROUP BY domain
ORDER BY pages DESC
LIMIT 20;

DuckDB-web-archive-cdx​

duckdb-web-archive-cdx — extension, добавляющее SQL-доступ к CDX-индексам Internet Archive и Common Crawl.

import duckdb

con = duckdb.connect()
con.sql("INSTALL duckdb_web_archive_cdx FROM community")
con.sql("LOAD duckdb_web_archive_cdx")

Запрос к Internet Archive CDX​

SELECT urlkey, timestamp, original, statuscode, mimetype, length
FROM cdx_ia('https://example.com/', prefix=true)
WHERE statuscode = 200
AND mimetype LIKE 'text/html%'
LIMIT 50;

Запрос к Common Crawl CDX​

SELECT url, fetch_time, content_languages
FROM cdx_cc(
'https://commoncrawl-index.communitycrawl.org/',
url='example.com/*'
)
LIMIT 100;

Объединение WARC с CDX​

# Локальный WARC + удалённый CDX в одном запросе
result = con.sql("""
SELECT local.url, cdx.timestamp, local.length
FROM read_warc('archive.warc.gz') local
JOIN cdx_ia('https://example.com/') cdx
ON local.url = cdx.original
WHERE local.response_code = 200
LIMIT 100
""").fetchall()

Поиск по локализациям​

SELECT url, content_languages
FROM cdx_cc(url='*example*')
WHERE array_contains(content_languages, 'ru')
LIMIT 50;

Сравнение с традиционными инструментами​

Возможностьwarcdumpwarcio + PythonDuckDB-WARC
Подсчёт URL✅✅✅
Подсчёт по доменам❌✅✅ (GROUP BY)
Сложные фильтры (regex, dates)❌✅✅ (SQL)
Join между WARC и CDX❌⚠️ сложно✅
Выходные таблицы❌✅✅
Производительность на больших файлах⚠️⚠️✅ (streaming)
Стоимость входанизкаясредняянизкая

Ruarxive metawarc — реализация на DuckDB​

Внутренний инструмент Ruarxive metawarc уже использует DuckDB для построения индексируемого каталога:

  • DuckDB-база с метаданными всех WARC коллекции.
  • Parquet-сайдкары для быстрых запросов.
  • REST API и MCP-сервер для AI-агентов.

Пример с metawarc (предполагает, что архив уже проиндексирован):

metawarc --input archive.warc.gz --output ./catalog
metawarc-query "SELECT url, length FROM catalog ORDER BY length DESC LIMIT 5"

Подробнее: metawarc (WARC).

Workflow анализа DuckDB​

Базовый анализ одной коллекции​

import duckdb
import pandas as pd

con = duckdb.connect()
con.sql("INSTALL duckdb_warc FROM community; LOAD duckdb_warc;")

# Top 20 стран
df = con.sql("""
SELECT
url_domain(url) AS domain,
COUNT(*) AS page_count,
SUM(length) AS total_bytes,
AVG(length) AS avg_bytes
FROM read_warc('archive.warc.gz')
WHERE warc_type = 'response'
AND response_code = 200
GROUP BY domain
ORDER BY page_count DESC
LIMIT 20
""").df()

print(df.to_string())

Объединение нескольких WARC​

import glob

files = glob.glob("archives/*.warc.gz")
files_str = "['" + "','".join(files) + "']"

result = con.sql(f"""
SELECT
regexp_extract(filename, '/([^/]+)\\.warc', 1) AS collection,
COUNT(*) AS pages,
SUM(length) AS bytes
FROM read_warc({files_str})
WHERE response_code = 200
GROUP BY collection
""").df()

Анализ покрытия​

# Сколько страниц на домене со ссылками наружу (внешние ссылки)
result = con.sql("""
WITH pages AS (
SELECT
url,
regexp_extract(url, '://([^/]+)', 1) AS domain
FROM read_warc('archive.warc.gz')
WHERE response_code = 200 AND content_type LIKE 'text/html%'
)
SELECT
domain,
COUNT(*) AS total_pages,
COUNT(DISTINCT url) AS unique_pages
FROM pages
GROUP BY domain
ORDER BY total_pages DESC
""").df()

Производительность​

ОперацияwarcdumpDuckDB-WARCРазница
Подсчёт URL в 1 ГБ WARC~10 с~2 с5× быстрее
Подсчёт по доменам, 1 ГБневозможно без grep~3 с∞
Фильтр statuscode=200, 1 ГБневозможно без grep~2 с∞
Объединение 5 WARC по 1 ГБминуты~10 с5–10×

DuckDB использует columnar-формат и стриминг, что даёт O(n) чтения вместо O(n) Python-парсинга.

Когда использовать​

✅ Подходит:

  • Любой анализ WARC коллекций — это правильный инструмент по умолчанию.
  • Быстрое прототипирование SQL-запросов — интерактивный режим.
  • Ad-hoc отчёты для исследователей и журналистов.
  • CI/CD пайплайн метрик — подсчёт покрытия, валидация.
  • Метаданные Ruarxive каталога — подход metawarc.
  • Databricks / Snowflake не нужны — DuckDB in-process.

❌ Не подходит:

  • Полнотекстовый поиск по содержимому ответов — нужен Solr/Shine.
  • Real-time мониторинг — DuckDB batch.
  • Извлечение payload (только метаданные) — для больших payload используйте warcio.
  • Distributed вычисления — DuckDB single-process; для petabyte-scale нужен Spark.
  • Web UI из коробки — нужны дополнительные обёртки (Streamlit и т. п.).

Установка и интеграция​

В Python-скриптах​

import duckdb
con = duckdb.connect(database=":memory:")
con.sql("INSTALL duckdb_warc FROM community; LOAD duckdb_warc;")

Jupyter notebooks​

%pip install duckdb duckdb-warc
import duckdb
con = duckdb.connect()
con.sql("INSTALL duckdb_warc FROM community; LOAD duckdb_warc;")
%load_ext sql
%sql duckdb:///:memory:

DuckDB CLI​

duckdb
INSTALL duckdb_warc FROM community;
LOAD duckdb_warc;
SELECT url, length FROM read_warc('archive.warc.gz') LIMIT 5;

Streamlit / Dash UI​

import streamlit as st
import duckdb

st.title("Анализ WARC")
uploaded = st.file_uploader("WARC файл")
if uploaded:
con = duckdb.connect()
con.sql(f"INSTALL duckdb_warc FROM community; LOAD duckdb_warc;")
df = con.sql(f"SELECT * FROM read_warc('{uploaded.name}') LIMIT 100").df()
st.dataframe(df)

Ограничения​

  • Streaming для payload через content_decode() медленный — лучше использовать warcio для извлечения тела.
  • Memory на больших GROUP BY — DuckDB может выгружать на диск, но медленнее.
  • Нет graph operations — для link-graph используйте специализированные инструменты (см. Ландшафт 2026).
  • Не заменяет WACZ-валидацию — для форматной проверки используйте warctools.

Ресурсы​

Связанные материалы​