算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
scrapling【设计与创意】Fetches web pages and extracts structured data from them with Python, from a single HTTP request to a full crawl, including pages rendered by JavaScript and selectors that keep working after a site redesign. Use when a user asks to scrape a website, build a crawler or spider, extract product or listing data, follow pagination, scrape a JavaScript-ren…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
scrapling install downloads"
metadata:
author: terminal-skills
version: "1.0.0"
category: automation
tags: ["web-scraping", "crawler", "python", "browser-automation", "data-extraction"]
repository: https://github.com/D4Vinci/ScraplingScrapling is a Python scraping framework with three layers: fetchers that download pages (plain HTTP or a real browser), a fast parser with CSS, XPath and text search, and a spider engine for concurrent crawls with pause and resume. Its parser can store the fingerprint of an element and find it again after the page structure changes.
python -m venv .venv
source .venv/bin/activate
pip install "scrapling[fetchers]"
scrapling install
pip install scrapling alone installs only the parser; importing scrapling.fetchers or scrapling.spiders then raises ModuleNotFoundError. scrapling install downloads the browsers and their system dependencies; add --force to reinstall.
| Extra | Adds |
|---|---|
fetchers | HTTP and browser fetchers, spiders |
shell | scrapling extract commands and the interactive shell |
ai | MCP server (scrapling mcp) |
all | Everything above |
A Docker image with all extras and browsers is published as pyd4vinci/scrapling and ghcr.io/d4vinci/scrapling:latest.
| Class | Engine | Use for |
|---|---|---|
Fetcher | HTTP with browser TLS impersonation | Static HTML and JSON APIs; fastest |
DynamicFetcher | Chromium through Playwright | Content rendered by JavaScript |
StealthyFetcher | Patched Chromium with fingerprint spoofing | Sites that block automated browsers |
Start with Fetcher. Move to a browser only when the data is missing from the raw HTML. Each class has a session variant (FetcherSession, DynamicSession, StealthySession) that keeps cookies and, for browsers, keeps the browser open between requests.
from scrapling.fetchers import Fetcher, FetcherSession
page = Fetcher.get("https://quotes.toscrape.com/", impersonate="chrome", timeout=30)
print(page.status) # 200
print(page.css(".quote .author::text").getall()[:3])
with FetcherSession(impersonate="chrome", retries=3) as session:
first = session.get("https://quotes.toscrape.com/page/1/")
second = session.get("https://quotes.toscrape.com/page/2/")
Fetcher also has post, put and delete. timeout is in seconds here and in milliseconds for the browser fetchers.
from scrapling.fetchers import Fetcher
page = Fetcher.get("https://quotes.toscrape.com/")
quotes = page.css(".quote") # list of elements
quotes = page.xpath('//div[@class="quote"]')
quotes = page.find_all("div", class_="quote")
author = page.find_by_text("Albert Einstein", first_match=True)
first = quotes[0]
text = first.css(".text::text").get() # first match or None
tags = first.css(".tag::text").getall() # every match
href = page.css(".next a::attr(href)").get() # "/page/2/"
absolute = page.urljoin(href) # full URL
opening = first.css(".text::text").re_first(r"“(\w+ \w+)") # "The world"
similar = first.find_similar() # the other nine quote blocks
selector = first.generate_css_selector # property, not a method
markdown = page.markdown() # whole page as Markdown
css() and xpath() always return a list. Index it ([0]) or use .first before calling element methods such as get_all_text(). To parse HTML that is already on disk, use Selector from scrapling.parser.
from scrapling.fetchers import DynamicFetcher, DynamicSession
def open_second_page(browser_page):
browser_page.click("li.next a") # Playwright page API
browser_page.wait_for_selector(".quote")
page = DynamicFetcher.fetch(
"https://quotes.toscrape.com/js/",
headless=True,
network_idle=True,
wait_selector=".quote",
timeout=45000,
page_action=open_second_page,
)
print(page.url, len(page.css(".quote")))
with DynamicSession(headless=True, network_idle=True) as session:
for number in (1, 2, 3):
listing = session.fetch(f"https://quotes.toscrape.com/js/page/{number}/")
print(number, len(listing.css(".quote")))
Useful options: disable_resources=True (skip fonts, images, stylesheets), block_ads=True, proxy=, real_chrome=True (use the installed Chrome), cdp_url= (attach to a running browser), wait= (extra milliseconds after load).
StealthyFetcher.fetch() takes the same arguments plus solve_cloudflare=True, hide_canvas=True and block_webrtc=True. After a challenge is solved, pass wait_selector so the call returns the real content and not the interstitial.
from scrapling.spiders import Response, Spider
class QuotesSpider(Spider):
name = "quotes"
start_urls = ["https://quotes.toscrape.com/"]
allowed_domains = {"quotes.toscrape.com"}
concurrent_requests = 4
download_delay = 0.5
robots_txt_obey = True
async def parse(self, response: Response):
for quote in response.css(".quote"):
yield {
"text": quote.css(".text::text").get(),
"author": quote.css(".author::text").get(),
"tags": quote.css(".tag::text").getall(),
}
next_page = response.css(".next a::attr(href)").get()
if next_page:
yield response.follow(next_page)
result = QuotesSpider(crawldir="crawl-state").start()
print(len(result.items), result.completed, result.stats.requests_count)
result.items.to_jsonl("quotes.jsonl")
start() returns a CrawlResult with items, stats, completed and paused. Items export with to_json, to_jsonl, to_csv and to_xml.crawldir, Ctrl+C stops gracefully and the next run with the same directory resumes. The checkpoint is removed when the crawl completes.concurrent_requests_per_domain, max_blocked_retries, autothrottle_enabled, development_mode (replay cached responses while editing parse()), log_file.on_start, on_close, on_error, on_scraped_item. Override configure_sessions(self, manager) to mix HTTP and browser sessions and route requests with Request(url, sid="stealth").async for item in spider.stream().CrawlSpider, SitemapSpider, XMLFeedSpider, CSVFeedSpider, ShopifySpider, SiteToMarkdownSpider.from scrapling.fetchers import Fetcher
Fetcher.configure(
adaptive=True,
storage_args={"storage_file": "selectors.db", "url": "https://books.toscrape.com/"},
)
page = Fetcher.get("https://books.toscrape.com/")
cards = page.css("article.product_pod", auto_save=True) # first run: remember the element
cards = page.css("article.product_pod", adaptive=True) # later runs: relocate it if the selector fails
Matching is by similarity with a default threshold of 40 percent (percentage=40). Only the first matched element is stored.
pip install "scrapling[shell]"
scrapling extract get "https://quotes.toscrape.com/" quotes.md --css-selector ".quote"
scrapling extract fetch "https://quotes.toscrape.com/js/" quotes.txt --network-idle --wait-selector ".quote"
scrapling extract stealthy-fetch "https://quotes.toscrape.com/js/" quotes.html --solve-cloudflare
scrapling mcp
The output extension picks the format: .md Markdown, .txt text, .html raw HTML. --ai-targeted keeps only the main content and strips hidden elements. scrapling mcp runs the MCP server over STDIO; --http --host 127.0.0.1 --port 8000 switches to HTTP.
Request: "Get the title and price of every book on the first page of books.toscrape.com."
from scrapling.fetchers import Fetcher
page = Fetcher.get("https://books.toscrape.com/")
for book in page.css("article.product_pod")[:3]:
link = book.css("h3 a")[0]
price = float(book.css(".price_color::text").re_first(r"[\d.]+"))
print(link.attrib["title"], price, page.urljoin(link.attrib["href"]))
Result:
A Light in the Attic 51.77 https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html
Tipping the Velvet 53.74 https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html
Soumission 50.1 https://books.toscrape.com/catalogue/soumission_998/index.html
Request: "Crawl all the quotes on quotes.toscrape.com, follow the pagination, and save them as JSON lines."
Save the QuotesSpider class from the spider section as quotes_spider.py and run it:
python quotes_spider.py
head -n 1 quotes.jsonl
Result: ten pages are fetched and 100 items are written.
100 True 10
{"text":"“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”","author":"Albert Einstein","tags":["change","deep-thoughts","thinking","world"]}
Request: "The quotes on quotes.toscrape.com/js/ do not show up in my scraper."
from scrapling.fetchers import DynamicFetcher, Fetcher
print(len(Fetcher.get("https://quotes.toscrape.com/js/").css(".quote")))
page = DynamicFetcher.fetch("https://quotes.toscrape.com/js/", network_idle=True, wait_selector=".quote")
print(len(page.css(".quote")))
Result: the raw HTML holds no quotes; the rendered page holds ten.
0
10
parse() do not stop the crawl. They are logged as Spider error processing and the run still ends with completed=True. Assert on len(result.items) and read the log; an empty result usually means a broken selector.get() returns None when nothing matches, and css() returns an empty list. Guard before converting: float(None) inside parse() loses the whole page.robots_txt_obey is False and download_delay is 0 unless set. Turn the first on, set a delay, keep concurrent_requests low, and restrict allowed_domains.storage_args, fingerprints are saved inside the installed package directory and are lost when the environment is rebuilt. Point storage_file at a path in the project.percentage trades missed matches for wrong ones; verify the output.Fetcher, reuse one session for many pages, and set disable_resources=True.proxy="http://user:password@host:port" from an environment variable, never from source code. ProxyRotator from scrapling.fetchers rotates a list through proxy_rotator=.127.0.0.1 and set SCRAPLING_MCP_AUTH_TOKEN; it can fetch any URL on the caller's behalf.find_by_text() in 0.4.15 has no tag argument although older examples show one.