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
| Function | Purpose |
|---|---|
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. |
BaseScraper | Base class all scrapers subclass. |