Skip to main content

Every web site provides APIs.

Project description

toapi

CI PyPI Python License

Turn any website into a JSON API — declaratively.

toapi lets you point at a web page, declare the fields you want with CSS selectors, and get back a clean JSON API. No crawler to babysit, no database to maintain — pages are fetched and parsed on demand, with built‑in caching.

Install

pip install toapi

Requires Python 3.10+.

Quickstart

from htmlparsing import Attr, Text
from toapi import Api, Item

api = Api()


@api.site("https://news.ycombinator.com")
@api.list(".athing")
@api.route("/posts", "/news")
@api.route("/posts?page={page}", "/news?p={page}")
class Post(Item):
    title = Text(".titleline > a")
    url = Attr(".titleline > a", "href")


api.run(host="127.0.0.1", port=5000)

Run it:

python app.py

Then visit http://127.0.0.1:5000/posts and you get:

{
  "Post": [
    {"title": "Mathematicians Crack the Cursed Curve", "url": "https://www.quantamagazine.org/..."},
    {"title": "Stuffing a Tesla Drivetrain into a 1981 Honda Accord", "url": "https://jalopnik.com/..."}
  ]
}

How it works

   ┌────────────┐    ┌────────────┐    ┌────────────┐
   │  /posts    │ ─▶ │  fetch     │ ─▶ │  parse     │ ─▶  JSON
   │  (route)   │    │  (cache)   │    │  (Item)    │
   └────────────┘    └────────────┘    └────────────┘
  1. Route@api.route("/posts", "/news") maps your API path to a source URL.
  2. Fetch — pages are fetched with requests (or a headless browser if you pass browser=) and cached in memory.
  3. Parse — each Item extracts fields with CSS selectors via htmlparsing.
  4. Serve — Flask returns the result as JSON; subsequent calls hit the cache.

Features

  • Declarative — describe data, not scraping logic.
  • Routes — map clean API paths to messy source URLs with {param} placeholders.
  • Multi-site — merge several websites behind one API.
  • Cleaning hooks — define clean_<field> methods to post-process values.
  • Caching — pages and parsed results are cached automatically.
  • Headless browser — pass Api(browser="/path/to/geckodriver") for JS-heavy sites.

Cleaning values

Add a clean_<fieldname> method on the Item to transform a value before it's returned:

@api.site("https://news.ycombinator.com")
@api.route("/posts", "/news")
class Page(Item):
    next_page = Attr(".morelink", "href")

    def clean_next_page(self, value):
        return f"/posts?{value.split('?', 1)[1]}"

Development

git clone https://github.com/elliotgao2/toapi.git
cd toapi
uv sync          # install deps into .venv
uv run pytest    # run tests
uv run ruff check .

We use uv for packaging and ruff for lint + format. Pre-commit hooks keep both clean:

uv run pre-commit install

Contributing

Pull requests are welcome. For non-trivial changes, please open an issue first to discuss what you'd like to change. Make sure uv run pytest and uv run ruff check . pass before submitting.

License

MIT © Elliot Gao

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

toapi-2.2.1.tar.gz (420.5 kB view details)

Uploaded Source

Built Distribution

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

toapi-2.2.1-py3-none-any.whl (6.8 kB view details)

Uploaded Python 3

File details

Details for the file toapi-2.2.1.tar.gz.

File metadata

  • Download URL: toapi-2.2.1.tar.gz
  • Upload date:
  • Size: 420.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.9

File hashes

Hashes for toapi-2.2.1.tar.gz
Algorithm Hash digest
SHA256 3cedacfa999c755dee927a356e941d208df41577982dd8d45249964133ec0094
MD5 fb154c721aa5ff683164bed14cc14bcc
BLAKE2b-256 6b89554a3f5fc693568822458f54464832eb7d8f94eac11edf01cc0822b3314e

See more details on using hashes here.

File details

Details for the file toapi-2.2.1-py3-none-any.whl.

File metadata

  • Download URL: toapi-2.2.1-py3-none-any.whl
  • Upload date:
  • Size: 6.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.9

File hashes

Hashes for toapi-2.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d43c329075c8793211398dd93ae560b87be1984f00cb3e9085f8d8e369cfe3a4
MD5 1b896c23ba2f7b662f02b2316b1bbe00
BLAKE2b-256 7a964382f24c3e6a7eff9feebe901e79a6a7dcc81eead67db20e702fb8b4c88e

See more details on using hashes here.

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