Claude · Automation

Scrape leads in plain English: the Claude Code skill that replaces your scraper subscription

Install ScrapeGraphAI, add one skill to Claude Code, and ask for your leads in plain English. The exact script, the schema that locks your columns, and CSV output ready for your CRM.

QQuentin Megevand
August 5, 2026 · 8 min read

Why this setup

You need forty prospects with their email, role and company. You pay a tool per thousand rows, you export, and three weeks later the site rebuilds its HTML: your extraction returns empty columns. Classic scraping breaks because it depends on the structure of the page, never on its meaning.

ScrapeGraphAI flips the logic. The library fetches the page, hands it to a model, and asks what to pull out of it. You no longer describe where the data sits, you describe the data you want. No CSS selectors, no XPath, and an extraction that survives a template redesign.

Open source under the MIT license, written in Python, 29,000 stars on GitHub. Wired into Claude Code through a small custom skill, it becomes a sentence you type in your terminal: "get me every founder on this page with their email".

This guide gives you the full sequence, verified on scrapegraphai 2.1.6 and Claude Code 2.1.222: the engine, the skill, the script it runs, and the one detail that makes the output genuinely importable into a CRM.

Claude AI Lab

The Claude AI Lab is my Skool community where I share my Claude systems and the more advanced modules. Access is $67/month.

Join the Lab →
29,000
GitHub stars, MIT license
2.1.6
stable version tested
3.12
minimum Python version
$0
in licensing, you only pay tokens
What you need
1
Python 3.12 or newer. The library refuses to install below that.
2
An API key for the model that reads the pages. An OpenAI key is enough, and one page costs a fraction of a cent. Ollama locally if you want zero variable cost.
3
Claude Code installed. It is the thing that will take your plain English requests.
4
Ten minutes. The install takes most of it.
1

Install the engine

🔗 github.com/ScrapeGraphAI/Scrapegraph-ai

The trap is on the first line. macOS still ships a python3 on 3.9, and the library requires 3.12 as a minimum. So the venv has to be created with an explicit version, otherwise the install fails before downloading anything.

python3.12 -m venv ~/scraper-env
~/scraper-env/bin/pip install scrapegraphai
~/scraper-env/bin/playwright install chromium

If python3.12 does not exist on your machine, use brew install python@3.12 on macOS, or the equivalent package on your distribution.

The second command installs about a hundred packages (LangChain, Playwright, the OpenAI and Ollama clients). The third downloads the browser that will load the pages, roughly 95 MB. Without it, collection fails at the moment it tries to render the page.

Then the model key, in your shell and in your ~/.zshrc so it survives across sessions:

export OPENAI_API_KEY="your-key"
Detail that matters

Everything lives in a dedicated venv, never in your system Python. The path ~/scraper-env/bin/python is the one the skill will call: it is the only thing Claude Code needs to know about.

2

Create the Claude Code skill

📄 ~/.claude/skills/scraper/SKILL.md

A Claude Code skill is a folder holding a SKILL.md file. In ~/.claude/skills/, it is available across all your projects. In .claude/skills/ at the root of a project, it only lives inside that project.

mkdir -p ~/.claude/skills/scraper

The contents of ~/.claude/skills/scraper/SKILL.md:

---
name: scraper
description: Plain English web extraction with ScrapeGraphAI. Use when asked to
  scrape leads, emails, listings, job openings, or any kind of list from a web
  page.
---

# Scraper

For any extraction request from a URL, run
`~/scraper-env/bin/python ~/scraper-env/scrape.py "<request>" "<url>"`.
The script returns JSON.

Then:
- render the result as a markdown table
- write a CSV alongside it as soon as there are more than ten rows
- never rewrite an extracted value and never fill a blank

Two things matter in this file. The description is what Claude reads to decide whether to use the skill, so it needs the words you actually type: scrape, leads, emails, list. The name is optional and defaults to the folder name.

That last instruction line earns its place: without it, a helpful model tends to complete a partial record with whatever looks probable. On a prospecting file, that is precisely what you do not want.

No restart needed

The ~/.claude/skills/ folder is watched during the session, so a skill you create right now is live immediately. The only case that needs a restart is when that folder did not exist yet when you launched Claude Code. Type /skills to confirm it is picked up.

3

The script that extracts

🐍 ~/scraper-env/scrape.py

The skill decides when to extract, the script does the work. Twenty five lines are enough.

import json, os, sys
from typing import List, Optional
from pydantic import BaseModel
from scrapegraphai.graphs import SmartScraperGraph

class Row(BaseModel):
    name: str
    role: Optional[str] = None
    email: Optional[str] = None
    company: Optional[str] = None
    link: Optional[str] = None

class Result(BaseModel):
    rows: List[Row]

config = {
    "llm": {
        "api_key": os.environ["OPENAI_API_KEY"],
        "model": "openai/gpt-4o-mini",
    },
    "headless": True,
    "verbose": False,
}

request, url = sys.argv[1], sys.argv[2]
graph = SmartScraperGraph(prompt=request, source=url, config=config, schema=Result)
print(json.dumps(graph.run(), ensure_ascii=False, indent=2))

To test it without going through Claude Code:

~/scraper-env/bin/python ~/scraper-env/scrape.py \
  "every team member with their role and email" \
  "https://example.com/team"

The schema parameter is the piece that changes everything. Without it, the model names the keys however it feels: email on one page, mail on the next, contact on the third. With it, the output is constrained to the Pydantic model, so the columns are identical on every run and two extractions stack into the same file without any cleanup.

Why the fields are optional

Optional[str] = None keeps the whole extraction from failing because one record has no email. You get the row with a gap in it, which beats an error on the entire batch.

4

Talk to your scraper

⌨️ claude

With the skill in place, you never touch the Python again. You open Claude Code in any folder and you ask.

🎯
Founders and emails
"Get me every founder on this page with their email"
🏢
Decision makers
"Pull every decision maker from this company list into a CSV"
💼
Job openings
"Scrape every opening on this careers page with the title and city"
🏠
Listings
"Take every listing here with the price, size and neighborhood"
📍
Local records
"Get the name, phone and rating of every business on this results page"
👥
Directories
"Extract the name, title and company of every person listed"

When you have no starting URL, the library can search on its own. SearchGraph takes the request without a source, queries a search engine and aggregates the first pages. The number of results is set in the config, three by default:

from scrapegraphai.graphs import SearchGraph

config["max_results"] = 10
graph = SearchGraph(
    prompt="recruitment agencies in Lyon with their contact email",
    config=config,
    schema=Result,
)
Split it right

One request equals one list. "The founders, and also the investors, and the revenue" returns mush. Three separate extractions return three clean files.

5

Make the file usable

🧹 before the CRM import

A raw export is not a prospecting file. Four habits make it workable.

🧹Before importing anything
5
Dedupe on email. Two pages often list the same person. Ask for identical rows to be merged before the export, not after.
6
Keep the source. Add a column with the originating URL. In six months you will want to know where a contact came from, and it is what lets you replay the extraction.
7
Check five rows by hand. Pick them at random and compare against the page. A model that invents an email does it plausibly, so it stays invisible inside a total of two hundred rows.
8
Validate the addresses before the first send. A fresh file always carries dead emails, and a high bounce rate damages your domain reputation for months.

On cost, gpt-4o-mini reads a page for a fraction of a cent: a two hundred row file built from twenty pages stays under a dollar. To remove the variable cost entirely, the library also runs locally on Ollama. You swap the llm block for {"model": "ollama/llama3.2", "model_tokens": 8192, "format": "json"} and drop the API key. The trade off is extraction quality on dense pages, worth testing on your own targets before switching.

On boundaries, stay on public pages that load without an account. A page behind a login is out of scope, technically and contractually. And for B2B prospecting in Europe, professional data collected publicly can be used as long as you state where it came from and honor opt outs on first request.

The test that settles it

Run the same extraction twice, a few days apart. If the columns are identical and only the rows move, your setup is reliable and you can put it on a schedule.

What you actually gain

This setup does not turn you into a scraper. It removes the friction between "I need this list" and "I have the file". Three minutes instead of half a day, and no subscription running through the months where you extract nothing.

The engine reads the meaning of the page, so the extraction survives redesigns. The schema locks the columns, so files stack. The skill takes Python out of the equation, so you state in plain English the list you want.

Worth remembering

Three pieces, installed once: the Python 3.12 venv that carries the library, the twenty five line script that constrains the output, and the SKILL.md file that turns your sentences into extractions. Everything else is phrasing.

Want to go further?

And day-to-day, I post one reel a day on Instagram: @quentin_iamarketing