Skip to main content

PyStates

A lightweight, elegant Python CLI tool for fetching clean, distraction-free web articles.

Python License: MIT Trafilatura

---

📖 Description

PyStates is a fast, minimalist command-line utility that downloads full-length articles from major publications (The New Yorker, Wired, The Atlantic, and hundreds more) and extracts clean, readable text using the industry-leading Trafilatura library.

It automatically removes ads, navigation menus, sidebars, comments, and boilerplate, then saves the result as either beautifully formatted Markdown (.md) or clean Plain Text (.txt).

Designed for researchers, writers, journalists, students, and power users who want to archive high-quality content locally — without distractions or paywalls interfering with readability.

pystates -u https://www.newyorker.com/magazine/2025/01/20/the-new-ai-era

✨ Features

  • 🚀 Blazing Fast — Downloads and processes articles in under 3 seconds
  • 🛡️ 403 Bypass — Smart custom User-Agent headers to reliably avoid common blocks
  • 🧼 Pristine Extraction — Powered by Trafilatura for the cleanest possible text
  • 🚫 Furniture Removal — "related articles", newsletter and promo blocks are pruned before extraction, so the file contains only the article (override with --no-prune)
  • 📝 Four Output Formats — Markdown (.md), Plain Text (.txt), Word (.docx) and PDF (.pdf)
  • 🧹 Intelligent Filename Sanitization — Generates safe OS filenames from article titles without using re
  • ✍️ Custom Filenames — Override auto-generated names with the -o flag
  • 🐍 Modern Python Stack — Built for Python 3.10+ with clean architecture
  • 📦 One-Command Installation — pip install -e . for instant local development
  • 🌐 Broad Compatibility — Works with virtually any news, magazine, or blog site supported by Trafilatura

🚀 Installation (Local Dev Mode)

# 1. Clone the repository
git clone https://github.com/Maksum867/PyStates.git
cd pystates

# 2. Install in editable/development mode
pip install -e .

Optional: Word and PDF output

md and txt work out of the box. Word and PDF output need extra dependencies:

pip install "pystates-CLI[word]"   # .docx  (python-docx)
pip install "pystates-CLI[pdf]"    # .pdf   (reportlab)
pip install "pystates-CLI[word,pdf]"  # both

If a format is requested without its dependency installed, the CLI exits with a clear message telling you which extra to install.

Verify Installation

pystates --help

Requirements

  • Python ≥ 3.9
  • pip

All dependencies (requests, trafilatura) are installed automatically via pyproject.toml. argparse ships with Python.

🖥️ Usage

Command Syntax

pystates -u URL [OPTIONS]

CLI Flags

Short Long Required Default Description
-u --url Yes — The full URL of the article to fetch and process
-f --format No md Output format: md, txt, docx (Word) or pdf
-o --output No (auto) Custom output filename. Any directory component is stripped; a matching extension is normalised
-d --output-dir No . Directory to write the file into
-q --quiet No off Suppress progress output (useful in cron jobs and CI)
--no-prune No off Keep "related articles" / newsletter blocks that are stripped by default
--version No — Print the version and exit

Examples

1. Basic usage (Markdown, auto-generated filename)

pystates -u "https://www.newyorker.com/magazine/2025/01/20/the-new-ai-era"

→ Creates the-new-ai-era.md

2. Save as plain text

pystates -u "https://www.wired.com/story/the-future-of-coding/" -f txt

→ Creates the-future-of-coding.txt

3. Custom output filename

pystates -u "https://www.theatlantic.com/ideas/archive/2025/07/climate-change/" \
         -o "climate_report_july_2025" \
         -f md

→ Creates climate_report_july_2025.md

pystates \
  --url "https://www.newyorker.com/culture/cultural-comment/the-end-of-the-internet-as-we-know-it" \
  --format txt \
  --output "end_of_internet"

5. Export to Word or PDF

pystates -u "https://www.newyorker.com/books/page-turner/what-is-biography-for" -f docx
pystates -u "https://en.wikipedia.org/wiki/Ada_Lovelace" -f pdf

→ Creates What_Is_Biography_For.docx / Ada_Lovelace.pdf, with a proper title, byline (author · date · publication) and one paragraph per source paragraph.

6. View all options

pystates --help

Sample Output (Markdown)

# The New AI Era

*By Jane Smith · 2025-01-20 · The New Yorker*

<https://www.newyorker.com/culture/cultural-comment/the-end-of-the-internet-as-we-know-it>

---

Artificial intelligence is no longer a distant promise...

## A New Paradigm

The implications are profound...

📁 Project Structure

PyStates/
├── pystates/
│   ├── __init__.py          # Package metadata & version
│   ├── __main__.py          # Entry point for `python -m pystates`
│   ├── cli.py               # Argument parsing (argparse)
│   ├── exporters.py         # Filename sanitization + md/txt/docx/pdf export
│   └── parser.py            # HTTP fetching (custom UA) + Trafilatura parsing
│   └── tests/               # 75 pytest tests (unit + real-HTTP integration)
├── .github/
│   ├── workflows/
│   │   ├── tests.yml        # pytest matrix, lint, packaging
│   │   └── publish.yml      # build + publish to PyPI (Trusted Publishing)
│   └── dependabot.yml       # automatic dependency updates
├── pyproject.toml           # Modern packaging + entry points
├── CHANGELOG.md
├── .gitignore
└── README.md

⚠️ Known limitations

  • JavaScript-only pages are not supported. PyStates downloads the HTML the server sends; sites that render the article in the browser (client-side React apps) may return a listing page or a paywall stub instead of the article. When the extracted text is suspiciously short, the CLI prints a warning on stderr and still writes the file.
  • Bot protection blocks some sites. With a plain HTTP client, sites behind Cloudflare/Akamai may answer 403/401. PyStates reports the HTTP error and exits with status 1.
  • PDF fonts. A Unicode TTF (DejaVu/Liberation) is used when present on the system, so Cyrillic and CJK render correctly on most Linux images and macOS; elsewhere ReportLab falls back to Helvetica, which only covers Latin-1.
  • The default User-Agent is a fixed desktop Chrome string. It is not rotated, and it is not a "403 bypass".

🧪 Development & Testing

git clone https://github.com/Maksum867/PyStates.git
cd PyStates
pip install -e ".[dev]"
python -m pytest            # 75 tests, no network access required
ruff check . && ruff format --check .

The suite includes a real end-to-end run against a local http.server, so it verifies the whole pipeline (download → extract → sanitize → write) without touching the internet.

Releasing

  1. Bump the version in pyproject.toml and pystates/__init__.py.
  2. Update CHANGELOG.md.
  3. Publish a GitHub Release tagged vX.Y.Z.
  4. .github/workflows/publish.yml runs the tests, builds the distributions and publishes to PyPI via Trusted Publishing (no API token needed).

📜 License

This project is released under the MIT License.

See LICENSE for the full text.


PyStates — preserving the web's best writing, one clean file at a time.

Built with care by the open-source community.

Metadata

Release files for pystates-CLI 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pystates-CLI 0.2.0
File Size Uploaded Provenance Actions
pystates_cli-0.2.0.tar.gz 26.7 kB With provenance Details for pystates_cli-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pystates-CLI 0.2.0
File Interpreter ABI Platform Actions
pystates_cli-0.2.0-py3-none-any.whl Python 3 none any Details for pystates_cli-0.2.0-py3-none-any.whl

Total release size: 51.0 kB

Release files / pystates_cli-0.2.0.tar.gz

Download URL pystates_cli-0.2.0.tar.gz
Size 26.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9209ea788c7cce611f2b825e1e4d36090fe5143f80a3f90ea63a821b9f534bab
BLAKE2b-256 checksum
How to use checksums
7a9b1f88ca53cbbddb6f807b1d185c3902c1ccd66a14ff23604859b54c969cc3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Release files / pystates_cli-0.2.0-py3-none-any.whl

Download URL pystates_cli-0.2.0-py3-none-any.whl
Size 24.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1bbaa5f3e10c0fde53184871da99837396bef42f1a3ef057926fc8224fa7644c
BLAKE2b-256 checksum
How to use checksums
4a3bce914515ab4232cae8eaee92d901f550a62df6f92260d6cc994f1f111fbf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

Security for pystates-CLI 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pystates_cli-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page