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.
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.
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 →Install the engine
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"
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.
Create the Claude Code skill
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.
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.
The script that extracts
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.
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.
Talk to your scraper
With the skill in place, you never touch the Python again. You open Claude Code in any folder and you ask.
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,
)
One request equals one list. "The founders, and also the investors, and the revenue" returns mush. Three separate extractions return three clean files.
Make the file usable
A raw export is not a prospecting file. Four habits make it workable.
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.
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.
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?
In the Lab, I share my Claude and n8n automations, from idea to something that runs while you sleep.
A dedicated session or program, tailored to your tools and use cases.
And day-to-day, I post one reel a day on Instagram: @quentin_iamarketing