Skip to content

Write per-crawl stats: HTTP status counts + sitemap accounting for auditing#9

Open
MirjamOdile wants to merge 1 commit into
mainfrom
pr/03-per-crawl-stats
Open

Write per-crawl stats: HTTP status counts + sitemap accounting for auditing#9
MirjamOdile wants to merge 1 commit into
mainfrom
pr/03-per-crawl-stats

Conversation

@MirjamOdile

Copy link
Copy Markdown

Problem

Assessing a fleet's health needs each crawl's page liveness (HTTP status mix)
and a coverage denominator (how many URLs the sitemap declared, how many were
rule-eligible). Sampling that after the fact is slow (re-fetching ~1000 URLs
per spider) and drifts — the sitemap you fetch today isn't the one the crawl
saw. Scrapy already counts every response status during the crawl; the data
just isn't persisted anywhere.

Change

Every completed production crawl persists its own numbers to
data/<project>/_audit/crawl_stats/<spider>.json:

  • BaseDBSpiderMixin.closed() dumps item count, request count, and the HTTP
    status histogram Scrapy already tracks (filtered on the
    downloader/response_status_count/ prefix so nothing else leaks in).
  • Sitemap spiders additionally count, while parsing, the sitemap's page URLs
    (sitemap_total) and those that survive date/deny filters AND match an
    allow rule (eligible) — the coverage denominator, recorded from the real
    crawl at the crawl's own point in time. <sitemapindex> entries (sub-sitemap
    refs) are never counted as pages.

Guards, so a wrong number can never masquerade as a right one:

  • Item-capped runs never write — skipped by the CLOSESPIDER_ITEMCOUNT
    setting, not the close reason: a --limit/health run that finishes UNDER
    its cap still closes with reason == "finished" and must not overwrite a
    real crawl's stats.
  • Resumed (checkpoint) runs never claim a denominator — a resume restores
    the request queue from disk but every in-memory counter restarts at zero.
    Detection reads the scheduler's own persisted state
    (JOBDIR/requests.queue/active.json — non-empty exactly when a run
    continues an interrupted crawl); the writer then withholds
    sitemap_total/eligible and stamps "resumed": true, keeping leg-only
    items/status.
  • All write errors are caught — stats-writing can never fail a crawl.

Verification

17 unit tests: writer happy-path and negative paths (non-finished reasons,
item-capped runs, resumed runs, write failure), sitemap counter fidelity
(match-all fallback, sitemapindex exclusion, relative-loc resolution), and
resume detection state matrix. Two additional round-trip tests (auto-skipped
in trees without a consumer) prove the file parses back into liveness/coverage
readings. Full unit suite green.

Known limitations (documented, deliberate)

  • A resumed crawl produces leg-only items/status (marked "resumed": true).
    The full fix — accumulating counters across legs in the checkpoint dir — is
    a worthwhile follow-up, but must handle the dupefilter-clearing corruption
    recovery (which makes a resumed leg re-parse sitemaps → double-count risk).
  • Incremental (DeltaFetch) re-crawls shrink the sample: the histogram reflects
    only newly fetched URLs.

Every completed crawl now persists its own numbers to
data/<project>/_audit/crawl_stats/<spider>.json:

- BaseDBSpiderMixin.closed() dumps item count, request count, and the
  HTTP status histogram Scrapy already tracks — giving an audit EXACT
  page liveness for free instead of a slow sampled re-fetch pass. Only
  on reason=="finished", so partial/test (--limit) runs can't overwrite
  a real crawl's numbers.
- Sitemap spiders additionally count, while parsing, the sitemap's page
  URLs (sitemap_total) and those that survive date/deny filters AND
  match an allow rule (eligible) — the coverage denominator, recorded
  from the real crawl so nothing re-fetches the sitemap. <sitemapindex>
  entries (sub-sitemap refs) are never counted as pages.

See docs/requests/06-per-crawl-stats.md (ships with the quality tool
PR) for the full write-up.
@iRanadheer iRanadheer force-pushed the pr/03-per-crawl-stats branch from 9c290a8 to 6e07c21 Compare July 10, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant