web-crawler

Web scraping plus social data: YouTube, TikTok, Instagram, LinkedIn, Reddit, Threads, plus robust web-page fallback extraction. Use when extracting public posts, transcripts, or pages JS-heavy enough to block plain fetch (e.g. download YouTube transcript, scrape TikTok comments, listing pages behind

By starchild-ai-agent · 3,881 installs

npx skills add starchild-ai-agent/official-skills --skill web-crawler

Source repository · Upstream listing

Preferred entry: call exports.py (don't hand roll requests) Ready made helpers live in skills/web crawler/exports.py . Prefer them over writing your own proxied get / proxied post calls — they already inject the proxy credentials, so there is no API key to find (don't read $SCRAPECREATORS API KEY / $FIRECRAWL API KEY , don't check .env , don't ask the user). Transcript route — ONE entry for any media link Design rule: captions / subtitles / published transcripts are METADATA; the media file is PAYLOAD. Exhaust every metadata provider first; touch the media only when the user explicitly agrees. Same rule claude video, spoken.md and yt dlp's skip download workflow follow. Nothing here is site specific: provider (cost order) what it is identity / validity check media info(url) yt dlp extract info(download=False) — 1800+ sites, returns title, duration, upload date, channel, caption track URLs. Zero media bytes. track fetched must be 2xx with a real body RSS <podcast:transcript Podcasting 2.0 standard, any podcast feed item matched by duration ±5 % + date ±3 d; transcript URL must be 2xx, 500 chars, not an error page show's own YouTube upload channel = the YouTube URL the show declares in its RSS (else search hit whose channel name equals the show name minus stopwords) candidates = duration ±5 % AND date ±3 d; identity = title similarity ≥ 0.6 OR show notes overlap (≥ 6 shared distinctive tokens, Jaccard ≥ 0.15). A candidate that fails identity is returned in candidate with found=False — never delivered as the episode scrape markdown(url) any other page (publisher, Snipd, blog) — browser rendered, survives WAF 403 1000 chars found=False ⇒ report kind , title , tried , note (and candidate if any) and ask the user before any download. YouTube is recognised by URL: the captions API runs even when yt dlp metadata fails, and a page scrape is never reported as a transcript for a media URL. yt dlp skip download / J / list subs are metadata calls and pass the bash gate; yt dlp <url , curl … .mp3 , ffmpeg, whisper are held for confirmation. Title only, no link → web search("<show <title transcript") then get transcript(<page url ) . Incident 2026 09 17: two 403s on transcript hosts led an agent to a 49 MB mp3 + ffmpeg + 4 whisper calls + an incomplete summary. Same link through get transcript : RSS had no transcript → RSS declares youtube.com/@a16z → channel listing → one upload within 1.2 % duration, same day → captions, 55k chars, ~30 s, zero audio. Site audit — "check all links" / "this button doesn't work" You are the auditor. exports.py gives you small, site agnostic tools that observe and return raw data ; they never decide what is broken. There is no keyword list, no per site config and no verdict function — every site is different, so you look, form a hypothesis, test it, and judge from evidence. Tool Returns fetch(url, method="GET") raw status , final url , headers, body, error check urls(urls, method="HEAD") fetch for many URLs in parallel, no bodies extract refs(html, base url) every URL like reference + where it came from ( a[href] , link[rel=…] , inline script , attr[data …] , meta[refresh] …) parse sitemap(xml) <loc entries of a sitemap / sitemap index browse(url, actions=[…], viewport=…, mobile=…) real Chromium: rendered links (incl. shadow DOM), visible controls, JS exceptions, console errors, ≥400 responses, failed requests, and for each action you asked for: URL before/after, navigations with status, new windows with their own status, downloads, DOM changed?, new links Method (details and worked checks: reference/site audit method.md ; full worked example: reference/site audit case study.md ): 1. Map the site from several sources, then reconcile. Home page HTML via extract refs , robots.txt → sitemap(s) via parse sitemap , rendered DOM via browse , and any URL the user named. Read the inline script and attr[...] hits yourself — route tables and JS navigation live there. A page the user mentions that none of your sources found is a finding about your map, not about the user. 2. Learn what "normal" and "error" look like on THIS site. Fetch one real page and one surely missing URL (random path). Compare status, title, size, text. That baseline — not a word list — tells you how this site shows a 404 (real 404, 200 soft 404, redirect to home…). 3. Check every page and every reference. check urls , re try HEAD failures with GET, re check transient errors once. Keep raw results; group them yourself (same site pages / assets / third party). 4. For what users actually click, click it. Use browse(...).controls and rendered links to choose targets, then browse(url, actions=[{"click": sel}]) at desktop and mobile width. Judge from the action record: did the URL change, did a new window open and what was its status, did a download finish, did the DOM change, did a JS error fire, was the element covered ( element.topmost at center )? 5. Explain anything unexpected before reporting it. 401/403/429 or bot walls = "could not verify", not broken and not fine. A click with no visible effect may be correct (already on that page) — say what you observed. 6. Report with evidence. Pages found and how; what is broken (URL, where it is referenced, status/observation, viewport); what could not be verified and why; what you did not cover. Never write "all links OK" for areas you did not check. Safety: only click targets you chose for a reason. Don't click anything form associated or whose text/context implies submit, buy, delete, send or subscribe unless the user explicitly asked. browse aborts non GET requests for the whole browser session by default — page load, actions and any new windows ( blocked writes , each with its phase ); keep it that way unless the user authorizes writes. If Playwright is unavailable browse returns {"error": …} — report clicks as not verified; never infer them from status codes. Quick trigger rules (read this first) Use this skill immediately when any of these conditions is true: web fetch returns HTTP 401/403/429/5xx Response is an anti bot challenge page (for example Cloudflare "Attention Required", "Just a moment", or challenge/captcha pages) The page is JS heavy and the first fetch misses required detail fields (for example publish time, author, listing code, updated time, price breakdown) Search results do not contain the requested field and the value must be extracted from the target page itself Fallback rule: If ordinary fetch is blocked or incomplete, switch to Firecrawl fallback in this skill before asking the user for screenshots/manual text. Error signatures action Signature Action web fetch HTTP 401/403/429/5xx Call Firecrawl POST /v2/scrape once with formats:["markdown","links"] + onlyMainContent:true Cloudflare/challenge page text in body Same Firecrawl call as above Markdown still misses key fields Retry once with formats:["rawHtml"] Firecrawl returns 403 / empty for a social media URL Check the intent routing tables below for a ScrapeCreators platform specific endpoint for this domain (e.g. sc get('/v1/instagram/post', url=...) , sc get('/v2/tiktok/video', url=...) ). If one exists, use it — these have dedicated extraction that bypasses anti bot. If no dedicated endpoint exists, fall through to archive fallback or ask the user. Firecrawl itself returns 403 / empty (hard paywall: NYT, WSJ, Economist, FT, Bloomberg) Call archive fallback(url) — recovers full text from a web archive snapshot Need structured data from a China app (抖音/小红书/微博/B站/京东/淘宝/1688/闲鱼/得物 etc.) Call apify run() — Apify Store has purpose built actors for these platforms that Firecrawl/ScrapeCreators don't cover Instagram story URL ( instagram.com/stories/<user /<id ) ScrapeCreators has no story endpoint and Firecrawl hits the login wall — go straight to apify run("data slayer~instagram stories scraper", ...) . See "Instagram stories" below Paywall / Firecrawl blocked fallback chain (use archive fallback ) When Firecrawl can't get the page either (it returns 403, or markdown comes back empty) the site is behind a hard paywall or aggressive WAF. Do NOT keep retrying Firecrawl. Recover the article from a web archive snapshot instead: How it works (and why this order): archive.today first ( archive.ph / archive.is mirrors). User triggered, real browser captures; historically preserves full text behind paywalls. Best bet for NYT/WSJ/Economist. We scrape its /newest/ snapshot via Firecrawl (archive.today has its own Cloudflare, so scrape it through Firecrawl, never web fetch it directly). Wayback Machine second ( archive.org ). Automated crawler that honors robots.txt and paywalls, so it often has NO full text for hard paywalls — but it's a good fallback for ordinary 403/Cloudflare pages that aren't paywalled. Limitation: archives only return text someone already saved . If res["markdown"] == "" , no snapshot exists — stop, tell the user, and try the outlet's official API/RSS or a different source. Do not fabricate the article. China app structured data (use apify run ) When you need structured data from a China app — Douyin video search, Xiaohongshu notes, Weibo posts, Bilibili videos, JD/Taobao product prices, 1688 wholesale listings, Xianyu second hand, Dewu sneakers, etc. — Firecrawl and ScrapeCreators don't cover these platforms. Use the Apify Store fallback instead. Apify is a serverless scraper marketplace with hundreds of community maintained actors that run real browsers + proxy pools against Chinese platforms. Auth: No user supplied key needed. sc proxy injects the platform Apify token automatically. The Authorization: Bearer header can be any fake value — the proxy replaces it with the real token. Do NOT read $APIFY TOKEN from env, do NOT check .env , do NOT ask the user for an Apify key. When to use Apify (vs Firecrawl/ScrapeCreators): ✅ China apps: 抖音, 小红书, 微博, B站, 京东, 淘宝, 1688, 闲鱼, 携程, 知乎, 豆瓣, 雪球, 优酷 (得物/快手/爱奇艺: no priced actor — search the Store) ✅ Southeast Asia e commerce: Shopee, Lazada, Temu ✅ Instagram stories (ScrapeCreators has no story endpoint) — see "Instagram stories" below ❌ Other Western social media (TikTok/Instagram posts & reels/YouTube/X/Reddit) → use ScrapeCreators first (cheaper) ❌ Generic web page scraping → use Firecrawl first (cheaper) ❌ Hard paywall articles → use archive fallback (Apify doesn't help here) How to pick an actor: Use the table below first — these actors are priced in sc proxy and were verified live on 2026 10 08 (30 day successful runs in brackets). For a platform not listed, search the Store through the proxy ( proxied get("https://api.apify.com/v2/store?search=<platform &limit=10") ), prefer high 30 day run counts, and expect the default $0.007/result price. Platform Actor ID Input key 抖音 search zen studio~douyin search scraper {"keywords": [...], "maxResultsPerQuery": N} 抖音 creator profile zen studio~douyin profile scraper (22k) (see actor input schema) 抖音 video/audio download apple yang~douyin video audio downloader (5k) (see actor input schema) 抖音 transcripts apple yang~douyin transcripts scraper (2.6k) (see actor input schema) 小红书 search zen studio~rednote search scraper {"keywords": [...], "maxResults": N} 小红书 note detail sian.agency~xiaohongshu rednote scraper {"operation": "noteDetail", "noteId": "...", "xsecToken": "..."} 小红书 data API socialdatax~socialdatax xhs data api (64k) (see actor input schema) 微博 hot search gentle cloud~weibo hot search scraper {"mode":