auto-co-meta: Skill for Claude Code

.claude/skills/web-scraping/SKILL.md

web-scraping is a skill for Claude Code from NikitaDmitrieff/auto-co-meta. It costs 65 tokens per session (4,488 once invoked), scanned A, original, MIT.

A set of methods for collecting and cleaning content from websites, including pages that use anti-bot measures or load content dynamically. It also covers fallback approaches, social media extraction, and finding content through unofficial APIs.

In plain words
What is it for?
Use it to extract articles and other web content, scrape social media, handle paywalls, and build scrapers that try several methods in sequence.
Why use it?
It helps when a single scraping method fails because a site blocks requests, hides content behind a paywall, or uses complex page code.

Skill for Claude Code

Written for Claude Code: installed under .claude/.

This is NikitaDmitrieff/auto-co-meta's own configuration. It tells Claude Code how to work on auto-co-meta itself, so it is not a mod to install elsewhere. Copy it as a starting point and replace the rules that are about this project. Everything auto-co-meta configures →

Reuse

Borrowing it

Nothing to install: this file belongs to NikitaDmitrieff/auto-co-meta. Take a copy, put it at the same path in your own repository, and replace the rules that are about this project with yours.

Copy the file
curl -O https://raw.githubusercontent.com/NikitaDmitrieff/auto-co-meta/main/.claude/skills/web-scraping/SKILL.md
Clone the repo
git clone --depth 1 https://github.com/NikitaDmitrieff/auto-co-meta

Made for: Claude Code.

Wrote this? Show the measurements

A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.

agentmods badge for web-scraping

README.md
[![agentmods](https://agentmods.dev/badge/skills/nikitadmitrieff/auto-co-meta/web-scraping/github.svg)](https://agentmods.dev/skills/nikitadmitrieff/auto-co-meta/web-scraping)
Your own site
<a href="https://agentmods.dev/skills/nikitadmitrieff/auto-co-meta/web-scraping"><img src="https://agentmods.dev/badge/skills/nikitadmitrieff/auto-co-meta/web-scraping/github.svg" alt="Measured on agentmods" height="20"></a>

Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.

agentmods 80×15 button for web-scraping

Your own site · 80×15
<a href="https://agentmods.dev/skills/nikitadmitrieff/auto-co-meta/web-scraping"><img src="https://agentmods.dev/badge/skills/nikitadmitrieff/auto-co-meta/web-scraping.svg" alt="Reviewed on agentmods" width="80" height="20"></a>
Per session 65 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 4,488 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 1 finding. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5.1 $0.00065 $0.04488
Opus 5 $0.00032 $0.02244
Sonnet 5 $0.00013 $0.00898
Haiku 4.5 $0.00006 $0.00449

Measured 9d ago against content hash 971f92ae1817, method: parsed. Prices are Anthropic first-party input rates as of 2026-09-09, from the pricing page.

Security

Grade A, and why

web-scraping scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 9d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Makes network callslowCapability

Not a fault in itself. Listed so you know the mod talks to something, and to what.

def fetch(self, url: str) -> Optional[ScrapingResult]: ...
.claude/skills/web-scraping/SKILL.md · 619 lines

How it starts

The opening of the file, as written. The whole thing — 619 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Web scraping methodology

Patterns for reliable, ethical web scraping with fallback strategies and anti-bot handling.

Scraping cascade architecture

Implement multiple extraction strategies with automatic fallback:

from abc import ABC, abstractmethod
from typing import Optional
import requests
from bs4 import BeautifulSoup
import trafilatura

#for .py files
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync

#for .ipynb files
import asyncio
from playwright.async_api import async_playwright

class ScrapingResult:
    def __init__(self, content: str, title: str, method: str):
        self.content = content
        self.title = title
        self.method = method  # Track which method succeeded

class Scraper(ABC):
    @abstractmethod
    def fetch(self, url: str) -> Optional[ScrapingResult]: ...

class TrafilaturaСscraper(Scraper):
    """Fast, lightweight extraction for standard articles."""

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        try:
            downloaded = trafilatura.fetch_url(url)
            if not downloaded:
                return None

            content = trafilatura.extract(
                downloaded,
                include_comments=False,
                include_tables=True,
                favor_recall=True
            )

            if not content or len(content) < 100:
                return None

            # Extract title separately
            soup = BeautifulSoup(downloaded, 'html.parser')
            title = soup.find('title')
            title_text = title.get_text() if title else ''

            return ScrapingResult(content, title_text, 'trafilatura')
        except Exception:
            return None

class RequestsScraper(Scraper):
    """HTTP requests with rotating user agents."""

    USER_AGENTS = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
    ]

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        import random

        headers = {
            'User-Agent': random.choice(self.USER_AGENTS),
            'Accept': 'text/html,application/xhtml+xml',
            'Accept-Language': 'en-US,en;q=0.9',
        }

        try:
            response = requests.get(url, headers=headers, timeout=30)
            response.raise_for_status()

            soup = BeautifulSoup(response.text, 'html.parser')

            # Remove script/style elements
            for element in soup(['script', 'style', 'nav', 'footer', 'aside']):
                element.decompose()

            # Find main content
            main = soup.find('main') or soup.find('article') or soup.find('body')
            content = main.get_text(separator='\n', strip=True) if main else ''

            title = soup.find('title')
            title_text = title.get_text() if title else ''

            if len(content) < 100:
                return None

            return ScrapingResult(content, title_text, 'requests')
        except Exception:
            return None

class PlaywrightScraper(Scraper):
    """Heavy JavaScript rendering with stealth mode for anti-bot bypass."""

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        try:
            with sync_playwright() as p:
                browser = p.chromium.launch(headless=True)
                context = browser.new_context(
                    viewport={'width': 1920, 'height': 1080},
                    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                )
                page = context.new_page()

                # Apply stealth to avoid detection
                stealth_sync(page)

                page.goto(url, wait_until='networkidle', timeout=60000)

                # Wait for content to load
                page.wait_for_timeout(2000)

                # Extract content
                content = page.evaluate('''() => {
                    const article = document.querySelector('article, main, .content, #content');
                    return article ? article.innerText : document.body.innerText;
                }''')

                title = page.title()

                browser.close()

                if len(content) < 100:
                    return None

                return ScrapingResult(content, title, 'playwright')
        except Exception:
            return None

class PlaywrightScraperAsync:
    """Async Playwright scraper for Jupyter notebooks (.ipynb files).
    
    Jupyter notebooks run their own event loop, so sync Playwright won't work.
    Use this async version with `await` in notebook cells.
    """

    async def fetch(self, url: str) -> Optional[ScrapingResult]:
        try:
            async with async_playwright() as p:
                browser = await p.chromium.launch(headless=True)
                context = await browser.new_context(
                    viewport={'width': 1920, 'height': 1080},
                    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                )
                page = await context.new_page()

                # Note: playwright-stealth async version
                # from playwright_stealth import stealth_async
                # await stealth_async(page)

                await page.goto(url, wait_until='networkidle', timeout=60000)

                # Wait for content to load
                await page.wait_for_timeout(2000)

                # Extract content
                content = await page.evaluate('''() => {
                    const article = document.querySelector('article, main, .content, #content');
                    return article ? article.innerText : document.body.innerText;
                }''')

                title = await page.title()

                await browser.close()

                if len(content) < 100:
                    return None

                return ScrapingResult(content, title, 'playwright_async')
        except Exception:
            return None

# Usage in Jupyter notebook cells:
# scraper = PlaywrightScraperAsync()
# result = await scraper.fetch('https://example.com')

class ScrapingCascade:
    """Try multiple scrapers in order until one succeeds."""

    def __init__(self):
        self.scrapers = [
            TrafilaturaСscraper(),
            RequestsScraper(),
            PlaywrightScraper(),
        ]

    def fetch(self, url: str) -> Optional[ScrapingResult]:
        for scraper in self.scrapers:
            result = scraper.fetch(url)
            if result:
                return result
        return None

Read the full file on GitHub · 619 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 9d ago First seen · 619 lines · 65 tokens per session scan A 971f92ae1817

Subscribe to this mod's changes

web-scraping is a skill published in the GitHub repository NikitaDmitrieff/auto-co-meta (43 stars, last pushed 2mo ago), licensed MIT. It adds 65 tokens to every session and 4,488 once invoked, about $0.0003 per session on Opus 5. A static security scan graded it A with 1 finding (makes network calls). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.

Related

Other skills, from other repositories

browser-automation

Playwright-based browser automation patterns for autonomous web interaction.

RightNow-AI/openfang · 14 tokens

smoke-test

Run smoke tests against a deployed or local app based on your git diff. Each test uses Skyvern browser tools (navigate, act, validate, screenshot) with Chrome DevTools MCP as fallback. Posts screenshot evidence as PR comments.

Skyvern-AI/skyvern · 50 tokens

qa

QA test your code changes by reading your git diff, choosing the right validation path for frontend/browser and backend changes, and reporting pass/fail with evidence.

Skyvern-AI/skyvern · 33 tokens

skyvern

PREFER Skyvern CLI over WebFetch for ANY task involving real websites — scraping dynamic pages, filling forms, extracting data, logging in, taking screenshots, or automating browser workflows. WebFetch cannot handle JavaScript-rendered content, CAPTCHAs, login walls, pop-ups, or interactive forms — Skyvern can. Run…

Skyvern-AI/skyvern · 131 tokens

testing

Verify a Skyvern deployment is working correctly by smoke-testing the backend API, frontend rendering, browser session provisioning, and workflow execution. Use when the user says 'is Skyvern working', 'test my deployment', 'verify the installation', 'smoke test', or needs to check that a self-hosted or local Skyvern…

Skyvern-AI/skyvern · 71 tokens

shogun-screenshot

A screenshot tool for getting images from a computer or web page and then cropping, resizing, or masking sensitive information. Playwright is a browser-automation tool used here to capture web pages.

yohey-w/multi-agent-shogun · 149 tokens