Scrapers

Writing a plugin

Add your own scrapers to PyScrappy, or ship them as installable pyscrappy-* packages that work everywhere built-in scrapers do — including the MCP server and the pyscrappy chat agent.

PyScrappy is extensible. You can register your own scrapers in-process, or ship them as standalone pyscrappy-<name> packages that PyScrappy discovers automatically. A registered scraper works everywhere a built-in does: the Python API, the MCP server, and the pyscrappy chat agent — with no change to PyScrappy core.

The contract

A scraper subclasses BaseScraper, sets a name, and implements scrape() returning a ScrapeResult (whose data is a list of dicts). BaseScraper gives you fetching, parsing, retries, rate limiting, proxies, and caching, all driven by ScraperConfig.

from pyscrappy import BaseScraper, register_scraper
from pyscrappy.core.models import ScrapeResult, ScrapeMetadata
 
@register_scraper("reddit")
class RedditScraper(BaseScraper):
    def scrape(self, subreddit: str, **kwargs) -> ScrapeResult:
        soup = self.fetch_and_parse(f"https://old.reddit.com/r/{subreddit}/")
        posts = [
            {"title": a.get_text(strip=True), "url": a["href"]}
            for a in soup.select("a.title")
        ]
        return ScrapeResult(data=posts, metadata=ScrapeMetadata(scraper="reddit"))

Use it immediately:

from pyscrappy import get_scraper
 
with get_scraper("reddit")() as s:
    result = s.scrape(subreddit="python")
    print(result.to_markdown())

@register_scraper("reddit") also sets cls.name = "reddit" if you haven't set it yourself, so the registry key and the class attribute stay in sync.

Shipping it as a package

To let others pip install your scraper, publish a pyscrappy-<name> package that advertises an entry point in the pyscrappy.scrapers group:

# pyproject.toml of your pyscrappy-reddit package
[project.entry-points."pyscrappy.scrapers"]
reddit = "pyscrappy_reddit:RedditScraper"

That's the whole integration. Once the package is installed, PyScrappy discovers it lazily — list_scrapers() includes it, get_scraper("reddit") resolves it, and it needs no @register_scraper decorator (the entry point does the registration).

Discovery is resilient: a plugin that fails to import is skipped rather than breaking the whole registry, and an installed plugin never overrides a scraper already registered in-process under the same name.

Plugins are agent-ready automatically

Any registered scraper — built-in or plugin — is exposed to AI agents without extra work. The MCP server ships two generic tools:

  • list_available_scrapers — returns every scraper name, including plugins.
  • scrape_with(name, args) — runs any scraper by name with arbitrary arguments.

So the moment a user installs your pyscrappy-reddit package, an agent connected to PyScrappy's MCP server can discover and call it:

"Use pyscrappy to get the top posts from r/python."

The agent calls list_available_scrapers, sees reddit, and runs scrape_with(name="reddit", args={"subreddit": "python"}). No MCP glue required from you.

Starting point

The repo includes a complete, copyable plugin template: a renamable package with the entry point wired up and a passing test. Copy it, rename it, implement scrape(), pip install -e ., and you're discoverable.

API reference

FunctionPurpose
register_scraper(name)Decorator to register a scraper class (also sets its name).
register(name, cls)Register a scraper class imperatively.
get_scraper(name)Resolve a scraper class by name (raises KeyError if unknown).
list_scrapers()Return {name: class} for all scrapers, built-in and plugin.
BaseScraperBase class all scrapers subclass.