# Pyfetcher

> Agentic web infrastructure: fetch, scrape, extract, download with MCP server, browser fingerprinting, and crawl-scrape-download pipeline

- **Type:** MCP server
- **Install:** `agentstack add mcp-pr1m8-pyfetcher`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [pr1m8](https://agentstack.voostack.com/s/pr1m8)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [pr1m8](https://github.com/pr1m8)
- **Source:** https://github.com/pr1m8/pyfetcher
- **Website:** https://pr1m8.github.io/pyfetcher/

## Install

```sh
agentstack add mcp-pr1m8-pyfetcher
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# fetchkit

**Agentic web infrastructure for autonomous fetching, scraping, and content acquisition.**

Give AI agents the power to fetch, scrape, extract, and download anything on the web -- with realistic browser fingerprints, structured outputs, and a full crawl-scrape-download pipeline backed by Postgres and MinIO.

[](https://pypi.org/project/fetchkit/)
[](https://pypi.org/project/fetchkit/)
[](https://pr1m8.github.io/pyfetcher/)
[](https://github.com/pr1m8/pyfetcher/actions/workflows/ci.yml)
[](https://github.com/pr1m8/pyfetcher/blob/main/LICENSE)
[](https://github.com/astral-sh/ruff)
[](https://pdm-project.org)
[](#development)
[](#mcp-server-ai-agent-integration)

---

[MCP Server](#mcp-server-ai-agent-integration) | [Quick Start](#quick-start) | [Pipeline](#pipeline) | [CLI](#cli) | [Documentation](https://pr1m8.github.io/pyfetcher/) | [Examples](examples/)

## Why fetchkit?

**The problem**: AI agents need to interact with the web -- fetch pages, extract data, download files -- but existing tools aren't designed for autonomous operation. They lack structured outputs, realistic browser fingerprints, and pipeline orchestration.

**fetchkit solves this** by providing:

1. **MCP Server** -- 16 tools that any AI agent (Claude, LangChain, LangGraph) can call directly. Structured Pydantic outputs, not raw HTML.
2. **Realistic browser identity** -- 11 profiles with consistent UA + Client Hints + Sec-Fetch-\* headers. TLS fingerprinting via curl_cffi. Cloudflare bypass.
3. **Full pipeline** -- Event-driven crawl -> scrape -> download backed by Postgres job queues and MinIO object storage.
4. **Deep downloader integration** -- yt-dlp and gallery-dl Python APIs with progress hooks and metadata extraction.

```bash
pip install 'fetchkit[mcp]'     # AI agent integration
pip install 'fetchkit[full]'    # Everything
```

## Highlights

```bash
pip install fetchkit                   # Core: fetch, scrape, headers
pip install 'fetchkit[mcp]'            # + MCP server for AI agents
pip install 'fetchkit[pipeline]'       # + Postgres job queue + MinIO storage
pip install 'fetchkit[full]'           # Everything including yt-dlp, Playwright, etc.
```

### Fetch with realistic browser headers

```python
from pyfetcher import fetch

response = fetch("https://example.com")
print(response.status_code, response.ok)
# Sends Chrome-like headers with Client Hints, Sec-Fetch-*, UA rotation automatically
```

### Scrape anything

```python
from pyfetcher.scrape import extract_links, extract_text, extract_readable_text

links = extract_links(html, base_url="https://example.com")  # all links with internal/external tags
titles = extract_text(html, "h1")                             # CSS selector extraction
article = extract_readable_text(html)                         # strips scripts, nav, ads
```

### 4 HTTP backends -- pick the right one for the job

```python
from pyfetcher import FetchRequest, fetch

response = fetch("https://example.com")                                        # httpx (default, HTTP/2)
response = fetch(FetchRequest(url="https://example.com", backend="aiohttp"))   # aiohttp (pure async)
response = fetch(FetchRequest(url="https://example.com", backend="curl_cffi")) # TLS fingerprinting
response = fetch(FetchRequest(url="https://example.com", backend="cloudscraper")) # Cloudflare bypass
```

### Download media with yt-dlp & gallery-dl

```python
from pyfetcher.downloaders.ytdlp import YtdlpDownloader
from pyfetcher.downloaders.gallerydl import GalleryDlDownloader

# Video/audio with progress tracking
yt = YtdlpDownloader()
info = await yt.extract_info("https://youtube.com/watch?v=...")    # metadata only
results = await yt.download("https://youtube.com/watch?v=...",     # full download
    output_dir="./media", progress_callback=lambda p: print(p.status))

# Image galleries (170+ supported sites)
gdl = GalleryDlDownloader()
results = await gdl.download("https://imgur.com/gallery/...", output_dir="./images")
```

### MCP Server -- give AI agents web superpowers

```bash
pyfetcher-mcp                  # stdio for Claude Desktop / Claude Code
pyfetcher-mcp --http 8000      # HTTP for LangChain / remote agents
```

```python
# LangChain integration
from langchain_mcp_adapters import MultiServerMCPClient
client = MultiServerMCPClient({"pyfetcher": {"transport": "http", "url": "http://localhost:8000/mcp"}})
tools = await client.get_tools()  # 16 structured tools ready for any agent
```

## Features

### Core Library

| Feature             | Description                                                                                                                               |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Browser Headers** | 11 profiles (Chrome/Firefox/Safari/Edge) across 5 platforms. Consistent UA + Client Hints + Sec-Fetch-\*. Market-share-weighted rotation. |
| **4 Backends**      | `httpx` (default, HTTP/2), `aiohttp` (async), `curl_cffi` (TLS fingerprint), `cloudscraper` (CF bypass)                                   |
| **Rate Limiting**   | Per-domain + global token bucket with configurable burst                                                                                  |
| **Retry**           | Exponential backoff via Tenacity with configurable status codes                                                                           |
| **Scraping**        | CSS selectors, link harvesting, form parsing, table extraction                                                                            |
| **Metadata**        | HTML meta, Open Graph, JSON-LD, microdata, RDFa, Dublin Core                                                                              |
| **CLI**             | `pyfetcher fetch`, `scrape`, `headers`, `user-agent`, `robots`, `download`                                                                |
| **TUI**             | Interactive Textual terminal UI for building and inspecting requests                                                                      |

### Infrastructure (optional extras)

| Feature          | Extra           | Description                                                                 |
| ---------------- | --------------- | --------------------------------------------------------------------------- |
| **Pipeline**     | `[pipeline]`    | Event-driven Crawl -> Scrape -> Download via Postgres LISTEN/NOTIFY         |
| **Database**     | `[db]`          | SQLAlchemy 2.0 async + Alembic. Jobs, pages, media, hosts, feeds, URL dedup |
| **Object Store** | `[store]`       | MinIO/S3 via aioboto3. Upload, download, presigned URLs                     |
| **Downloaders**  | `[downloaders]` | yt-dlp (progress hooks, info_dict) + gallery-dl (170+ sites)                |
| **Extractors**   | `[extractors]`  | trafilatura + readability-lxml fallback, html2text, markdownify             |
| **Media**        | `[media]`       | Audio (mutagen), video (pymediainfo), image (exifread), PDF (pypdf)         |
| **Browser**      | `[browser]`     | Playwright + stealth for JS-heavy sites                                     |
| **Feeds**        | `[feeds]`       | RSS/Atom monitoring with adaptive polling                                   |
| **Crawler**      | `[pipeline]`    | URL frontier, spider + router, dedup, politeness, sitemap discovery         |

## Installation

```bash
pip install fetchkit
```

All optional extras:

```bash
pip install 'fetchkit[tui]'            # Textual TUI
pip install 'fetchkit[curl]'           # curl_cffi TLS fingerprinting
pip install 'fetchkit[cloudscraper]'   # Cloudflare bypass
pip install 'fetchkit[db]'             # Postgres + SQLAlchemy + Alembic
pip install 'fetchkit[store]'          # MinIO/S3 object storage
pip install 'fetchkit[pipeline]'       # db + store (full pipeline)
pip install 'fetchkit[downloaders]'    # yt-dlp + gallery-dl
pip install 'fetchkit[extractors]'     # trafilatura, readability, html2text
pip install 'fetchkit[media]'          # Audio/video/image/PDF metadata
pip install 'fetchkit[browser]'        # Playwright + stealth
pip install 'fetchkit[feeds]'          # RSS/Atom feed parsing
pip install 'fetchkit[full]'           # Everything
```

## Quick Start

### Fetch

```python
from pyfetcher import fetch, afetch, FetchRequest
import asyncio

# Sync
response = fetch("https://example.com")
print(response.status_code, response.ok)

# Async
response = asyncio.run(afetch("https://example.com"))
```

### Browser Profiles & Headers

```python
from pyfetcher.headers.browser import BrowserHeaderProvider
from pyfetcher.headers.rotating import RotatingHeaderProvider
from pyfetcher.headers.ua import random_user_agent
from pyfetcher.fetch.service import FetchService

# Fixed profile (Chrome on Windows)
service = FetchService(header_provider=BrowserHeaderProvider("chrome_win"))

# Rotating profiles weighted by real-world market share
service = FetchService(header_provider=RotatingHeaderProvider())

# Just need a user-agent string?
ua = random_user_agent(browser="firefox", platform="macOS")
```

### Scraping

```python
from pyfetcher.scrape import (
    extract_links, extract_text, extract_table,
    extract_forms, extract_readable_text,
)
from pyfetcher.scrape.robots import parse_robots_txt, is_allowed

# CSS selectors
titles = extract_text(html, "h1.title")
rows = extract_table(html, "table.data")

# Links with internal/external classification
links = extract_links(html, base_url=url, same_domain_only=True)

# Forms with field extraction
forms = extract_forms(html, base_url=url)
print(forms[0].action, forms[0].to_dict())

# Robots.txt
rules = parse_robots_txt(robots_content)
allowed = is_allowed(rules, "/admin", user_agent="MyBot")
```

### Rate-Limited Fetching

```python
from pyfetcher.fetch.service import FetchService
from pyfetcher.ratelimit.limiter import DomainRateLimiter, RateLimitPolicy

limiter = DomainRateLimiter(
    default_policy=RateLimitPolicy(requests_per_second=2.0, burst=5),
    domain_policies={
        "api.example.com": RateLimitPolicy(requests_per_second=0.5),
    },
)
service = FetchService(rate_limiter=limiter)
```

### Content Extraction

```python
from pyfetcher.extractors.content import extract_article_text
from pyfetcher.extractors.convert import html_to_markdown, html_to_plaintext

# Article text (trafilatura with readability-lxml fallback)
article = extract_article_text(html, url="https://example.com/post")

# HTML -> Markdown
md = html_to_markdown(html)
```

### yt-dlp & gallery-dl

```python
from pyfetcher.downloaders.ytdlp import YtdlpDownloader
from pyfetcher.downloaders.gallerydl import GalleryDlDownloader

# yt-dlp with progress tracking
yt = YtdlpDownloader()
info = await yt.extract_info("https://youtube.com/watch?v=dQw4w9WgXcQ")
results = await yt.download(url, output_dir="./videos",
    progress_callback=lambda p: print(f"{p.status}: {p.percent}"))

# gallery-dl for image galleries (170+ supported sites)
gdl = GalleryDlDownloader()
results = await gdl.download("https://imgur.com/gallery/...", output_dir="./images")
```

## CLI

```bash
# Fetch with any backend
pyfetcher fetch https://example.com
pyfetcher fetch https://example.com -o json -b curl_cffi

# Preview generated headers
pyfetcher headers --profile chrome_win
pyfetcher headers --browser firefox -o json
pyfetcher headers --list

# Scrape content
pyfetcher scrape https://example.com --css "h1"
pyfetcher scrape https://example.com --links -o json
pyfetcher scrape https://example.com --text
pyfetcher scrape https://example.com --meta

# Random user-agents
pyfetcher user-agent --browser chrome --count 5
pyfetcher user-agent --mobile

# Check robots.txt
pyfetcher robots https://example.com -p /admin

# Download files
pyfetcher download https://example.com/file.pdf ./file.pdf
```

## Pipeline

The event-driven pipeline connects three stages via Postgres LISTEN/NOTIFY:

```
Seeds / RSS / Sitemap
       |
  [Crawl Stage]  ──NOTIFY──>  [Scrape Stage]  ──NOTIFY──>  [Download Stage]
       |                             |                             |
       v                             v                             v
  pages table                 pages (enriched)              media_assets
  + new crawl jobs            + download jobs               + MinIO objects
```

### Setup

```bash
make infra-up     # Start Postgres + MinIO
make migrate      # Run Alembic migrations
make pipeline     # Start all workers
```

### Programmatic

```python
from pyfetcher.pipeline.runner import PipelineRunner
from pyfetcher.config import PyfetcherConfig

runner = PipelineRunner(PyfetcherConfig(
    crawl_concurrency=10,
    scrape_concurrency=20,
    download_concurrency=5,
))
await runner.start()
```

### Custom Spiders

```python
from pyfetcher.crawler.spider import Spider, SpiderResult

spider = Spider(name="my-spider")

@spider.router.add(r"/blog/\d{4}/")
async def handle_post(url, response):
    return SpiderResult(
        discovered_urls=[...],
        items=[{"title": "...", "content": "..."}],
    )
```

## MCP Server (AI Agent Integration)

fetchkit ships as an **MCP server**, making all its capabilities available to AI agents (Claude, LangChain, LangGraph, and any MCP-compatible client). This turns fetchkit into **autonomous agentic infrastructure** -- LLMs can fetch, scrape, extract, and download without custom code.

### Why MCP?

Traditional scraping requires writing code for every site. With fetchkit's MCP server, an AI agent can:

- **Autonomously research topics** by fetching pages, extracting content, and following links
- **Audit websites** by checking metadata, robots.txt, sitemaps, and page structure
- **Extract structured data** from any page using CSS selectors, table parsing, or article extraction
- **Download media** with progress tracking and checksum verification
- **Generate realistic requests** using browser profiles that pass bot detection

All 16 tools return **structured Pydantic models** so the LLM gets clean, typed data -- not raw HTML.

### Quick Start

```bash
pip install 'fetchkit[mcp]'

# Run as stdio server (Claude Desktop / Claude Code)
pyfetcher-mcp

# Run as HTTP server (LangChain / remote agents)
pyfetcher-mcp --http 8000

# Or via Makefile
make mcp          # stdio
make mcp-http     # HTTP on port 8000
```

### Available Tools (16)

| Tool                | What it does                                                       |
| ------------------- | ------------------------------------------------------------------ |
| `fetch_url`         | Fetch any URL with browser headers, returns status + body + timing |
| `fetch_multiple`    | Batch fetch with concurrency control                               |
| `scrape_css`        | Extract content via CSS selectors                                  |
| `scrape_links`      | Harvest links with internal/external classification                |
| `scrape_text`       | Extract readable text (strips scripts, nav, etc.)                  |
| `scrape_metadata`   | Title, description, Open Graph, favicons                           |
| `scrape_forms`      | Parse forms with fields and default values                         |
| `scrape_table`      | Extract HTML table data as rows                                    |
| `check_robots`      | Check robots.txt rules for any path                                |
| `parse_sitemap`     | Parse XML sitemaps                                                 |
| `generate_headers`  | Preview full browser header sets                                   |
| `list_profiles`     | Show all 11 browser profiles                                       |
| `random_user_agent` | Generate random realistic UAs                                      |
| `extract_article`   | Article text + markdown via trafilatura                            |
| `convert_html`      | HTML -> markdown or plaintext

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [pr1m8](https://github.com/pr1m8)
- **Source:** [pr1m8/pyfetcher](https://github.com/pr1m8/pyfetcher)
- **License:** MIT
- **Homepage:** https://pr1m8.github.io/pyfetcher/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-pr1m8-pyfetcher
- Seller: https://agentstack.voostack.com/s/pr1m8
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
