Skip to main content

Build reproducible Letterboxd list datasets with resilient scraping, list algebra, filtering, validation, and auditable exports.

Project description

Letterboxd List Toolkit

CI

A typed Python library and CLI for building reproducible, validated, and auditable Letterboxd list datasets from public lists.

The toolkit combines resilient scraping, list algebra, rating filters, caching, concurrency, validation, deterministic CSV exports, and production-sized workflows such as Top 10 films by country and official language.

This project is not affiliated with Letterboxd. It works with public pages and should be used conservatively and responsibly.

Why this project exists

Large Letterboxd exports fail in ways that can look successful:

  • public pages may return HTTP 403 from hosted runners;
  • pagination often ends with 404 instead of an empty page;
  • filtered-list URLs have a strict path order;
  • broad markdown link extraction can turn footer or comment links into films;
  • ratings and page markup change over time;
  • a green workflow can still produce an empty or incomplete CSV;
  • popularity criteria often come from community-maintained snapshot lists.

The toolkit encodes the lessons from real production-sized runs as parsers, validation gates, tests, diagnostics, and repeatable workflows.

Features

  • Python API and command-line interface.
  • Scrape one or more public Letterboxd lists.
  • Union seed lists, intersect inclusion lists, and subtract exclusion lists.
  • Apply inclusive or exclusive rating boundaries.
  • Hide TV, shorts, and documentaries through Letterboxd filters.
  • Resolve title, year, canonical URL, and current average rating.
  • Use Jina Reader as a read-only fallback when direct list pages are unavailable.
  • Cache film metadata and resolve records concurrently.
  • Export Letterboxd-compatible, audit, unresolved, and summary files.
  • Abort on suspicious candidate counts, excessive unresolved records, duplicates, or empty output.
  • Generate country and official-language datasets with an eight-shard GitHub Actions workflow.
  • Ship inline type information for editors and type checkers.

Installation

From GitHub

Until the first PyPI release is published:

python -m pip install "git+https://github.com/danyelbarboza/letterboxd-list-toolkit.git"

For the optional country and language workflow dependencies:

python -m pip install "letterboxd-list-toolkit[country] @ git+https://github.com/danyelbarboza/letterboxd-list-toolkit.git"

From PyPI

After the first tagged release:

python -m pip install letterboxd-list-toolkit

The distribution is named letterboxd-list-toolkit. The Python import package remains letterboxd_scraper for compatibility.

Python quick start

from letterboxd_scraper import build_list

result = build_list(
    seed_lists=[
        "https://letterboxd.com/user/list/example/",
    ],
    min_rating=3.0,
    max_rating=3.5,
    concurrency=8,
    output_directory="output/example",
    basename="example",
)

print(f"Selected {len(result.selected)} films")
print(result.output_paths.import_csv)

The returned result keeps all records in memory:

result.candidates
result.resolved
result.unresolved
result.selected
result.list_results
result.output_paths

A completed result can be exported again without making another network request:

result.to_letterboxd_csv("exports/movies.csv")
result.to_audit_csv("exports/movies_audit.csv")

See the complete Python API guide.

CLI quick start

Create a TOML configuration and run:

letterboxd-toolkit examples/popular-not-beloved.toml

The legacy command remains available:

letterboxd-scraper examples/popular-not-beloved.toml

Both commands execute the same pipeline.

Configuration

[query]
seed_lists = [
  "https://letterboxd.com/cinemageekyt/list/letterboxd-500k-watched-club/",
]
include_lists = []
exclude_lists = []
filters = ["hide-tv", "hide-shorts", "hide-documentaries"]
min_rating = 3.00
max_rating = 3.50
min_rating_inclusive = true
max_rating_inclusive = true
max_pages_per_list = 25

[http]
concurrency = 8
max_attempts = 6
timeout_seconds = 45
min_request_interval_seconds = 0.05
use_jina_fallback = true

[cache]
enabled = true
directory = ".cache/letterboxd"
ttl_hours = 24

[validation]
expected_min_candidates = 1200
expected_max_candidates = 1600
max_unresolved_ratio = 0.02
require_nonempty_output = true

[output]
directory = "output/popular-not-beloved"
basename = "popular_not_beloved"
include_audit_csv = true
include_unresolved_json = true
include_summary_json = true

Outputs

A standard run can write:

output/popular-not-beloved/
├── popular_not_beloved.csv
├── popular_not_beloved_audit.csv
├── popular_not_beloved_summary.json
└── popular_not_beloved_unresolved.json

The import CSV uses the columns accepted by Letterboxd:

Title,Year,LetterboxdURI

The audit CSV adds current rating and parser provenance.

List algebra

The candidate set is calculated as:

union(seed_lists)
∩ include_lists[0]
∩ include_lists[1]
...
− union(exclude_lists)

For example:

all narrative films ∩ over-10k-watches − over-100k-watches

approximates a 10,000-to-100,000 watch-count band at the source lists' snapshot dates.

See examples/watch-band.toml.example.

Rating boundaries

Include exactly 3.00 and 3.50:

min_rating = 3.00
max_rating = 3.50
min_rating_inclusive = true
max_rating_inclusive = true

Select ratings strictly above 3.50:

min_rating = 3.50
min_rating_inclusive = false

Country and official-language workflow

The repository contains a manual GitHub Actions workflow that:

  1. discovers Letterboxd country and language filters;
  2. maps official and regional official languages;
  3. runs country processing across eight parallel shards;
  4. combines shard outputs deterministically;
  5. validates counts, canonical URLs, duplicates, and known false positives;
  6. writes import, audit, country, language, unresolved, and continent outputs.

See Country Export and Validated Failure Modes.

Validation philosophy

A scraper should fail loudly when its assumptions stop being true.

Candidate-count guards are especially important for community-maintained lists. A blocked response, malformed filtered URL, or parser regression must not silently become an empty dataset with a successful exit code.

Recommended validation:

[validation]
expected_min_candidates = 1200
expected_max_candidates = 1600
max_unresolved_ratio = 0.02
require_nonempty_output = true

Update expected counts when the source list legitimately changes.

Data interpretation

The output is not an official Letterboxd database dump.

  • Popularity lists are snapshots. Their counts depend on the maintainer's most recent update.
  • Ratings are collected at scrape time. Films may later enter or leave a rating range.
  • Community lists can contain omissions or mistakes. Inspect audit outputs.
  • Content filters depend on Letterboxd classifications. Review samples before publishing a derived list.

Development

python -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev,country]'
make check

Individual checks:

ruff check src tests
ruff format --check src tests
mypy src
pytest --cov=letterboxd_scraper --cov-report=term-missing
python -m build
twine check dist/*

CI validates Python 3.11 and 3.12, builds a wheel and source distribution, installs the wheel in an isolated environment, and smoke-tests both command names.

Releases

The package uses semantic versioning and records changes in CHANGELOG.md.

Publishing is prepared through GitHub Releases and PyPI Trusted Publishing:

  1. configure a PyPI trusted publisher for this repository and the pypi environment;
  2. create a release whose tag matches the package version, such as v0.2.0;
  3. GitHub Actions builds, validates, and publishes the distributions.

Responsible use

Use conservative concurrency, caching, and request intervals. Do not overload Letterboxd or attempt to bypass authentication or access controls. Only process public pages, respect applicable terms and local law, and prefer reproducible snapshots over continuous high-frequency collection.

Documentation

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

letterboxd_list_toolkit-0.2.0.tar.gz (25.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

letterboxd_list_toolkit-0.2.0-py3-none-any.whl (26.7 kB view details)

Uploaded Python 3

File details

Details for the file letterboxd_list_toolkit-0.2.0.tar.gz.

File metadata

  • Download URL: letterboxd_list_toolkit-0.2.0.tar.gz
  • Upload date:
  • Size: 25.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for letterboxd_list_toolkit-0.2.0.tar.gz
Algorithm Hash digest
SHA256 08d4fa61635ce8508601697a2d128d2725c696136f29f2bd86a216ecedba2013
MD5 99212c443a7982be396f40d8329aa087
BLAKE2b-256 db6a0ce6e29167afdd3c8df90aa721c1d30da34df5a57c8413310b3fe270943b

See more details on using hashes here.

Provenance

The following attestation bundles were made for letterboxd_list_toolkit-0.2.0.tar.gz:

Publisher: publish.yml on danyelbarboza/letterboxd-list-toolkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file letterboxd_list_toolkit-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for letterboxd_list_toolkit-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ce21e0db2328aa487a1115ca5949a2361e8b4242a870fdd9747cb941703183ab
MD5 62d79214b18603c7cdb5fe5638f9c4f7
BLAKE2b-256 2cca929df37f2e769bd7429f7ee56e385503e312886305d50a431d4c2f32e806

See more details on using hashes here.

Provenance

The following attestation bundles were made for letterboxd_list_toolkit-0.2.0-py3-none-any.whl:

Publisher: publish.yml on danyelbarboza/letterboxd-list-toolkit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page