web-data-extractor

A browser skill that extracts structured information from the current webpage, such as text with context, links, titles, tables, and lists.

In plain words
What is it for?
Use it to collect product details, prices, reviews, social-media content, tables, lists, or other structured data from a webpage.
Why use it?
It turns visible webpage content into JSON and triggers lazy-loaded content before extraction, reducing manual copying and incomplete results.

Skill for Claude CodeCodex

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add skills/clawcap/manobrowser/web-data-extractor
Any agent
npx skills add ClawCap/ManoBrowser --skill web-data-extractor
Clone the repo
git clone --depth 1 https://github.com/ClawCap/ManoBrowser

Made for: Claude Code, Codex.

Per session 67 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 3,616 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. Scan, not verified.
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 $0.00067 $0.03616
Opus 5 $0.00034 $0.01808
Sonnet 5 $0.00013 $0.00723
Haiku 4.5 $0.00007 $0.00362

Measured 2d ago against content hash 824c5ee3e755, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

web-data-extractor scanned grade A with 0 findings 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 2d 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.

Nothing flagged

None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.

web-data-extractor/SKILL.md · 156 lines

How it starts

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

网页数据提取器

概述

此skill通过执行内联的 DOM 提取脚本,从网页中提取结构化数据。通过以下流程:

  1. 直接调用 mcp__browser__chrome_execute_script 执行内联脚本(脚本内部会自动滚动页面触发所有懒加载内容)
  2. 使用 async () => await (脚本) 等待Promise完成并获取JSON结果
  3. 返回包含文本节点(带上下文)、链接、标题的结构化数据

🚨 最关键要求: 调用 mcp__browser__chrome_execute_script 工具时,必须完整传递步骤2中定义的 jsScript 参数,不能省略、简化或修改任何字符,否则会导致数据提取失败!

核心原则:自动触发数据加载,一次性提取所有结构化内容及上下文,智能过滤和优化数据。

前置要求

  • Chrome MCP服务器已启动并连接
  • 无需外部插件或扩展

何时使用此Skill

当用户提出以下请求时调用此skill:

  • 从当前网页提取数据
  • 抓取网站中的结构化信息
  • 获取产品详情、价格、评论或其他电商数据
  • 提取社交媒体帖子、评论或互动统计数据
  • 导出网页中的表格或列表数据
  • 获取任何可见的结构化内容的JSON格式

触发此skill的用户请求示例:

  • "提取这个页面的所有产品信息"
  • "把这个表格的数据转成JSON"
  • "抓取这个社交媒体帖子的内容"
  • "获取这个页面的结构化数据"
  • "这个页面上有什么数据?"
  • "提取当前网页的内容"

数据提取流程

步骤1:验证Chrome MCP工具可用性

在提取数据之前,确认以下工具可用:

  • mcp__browser__chrome_execute_script - 用于执行JavaScript脚本 如果工具不可访问,告知用户需要启动并连接Chrome MCP服务器。

步骤2:执行提取脚本获取DOM数据

🚨 关键要求:必须完整传递下面的 jsScript 参数,不能省略、简化或修改任何部分!

直接调用 mcp__browser__chrome_execute_script 工具执行内联的提取脚本:

工具参数(必须严格按照下面的完整参数调用)

  • tabId: 目标标签页ID(可选,不提供则使用当前活动标签页)
  • jsScript: 必须使用下面的完整脚本,一个字符都不能少async () => await ((async () => { 'use strict'; window.scrollTo({ top: 0, behavior: 'instant' }); await new Promise(resolve => setTimeout(resolve, 300)); const documentHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight); window.scrollTo({ top: documentHeight, behavior: 'smooth' }); await new Promise(resolve => setTimeout(resolve, 1500)); window.scrollTo({ top: 0, behavior: 'smooth' }); await new Promise(resolve => setTimeout(resolve, 1000)); function shouldSkipElement(element) { if (!element) return true; const skipClassPrefixes = ['vjs-', 'video-js', 'plyr', 'jwplayer', 'ad-', 'advertisement', 'cookie-', 'gdpr-']; if (element.className && typeof element.className === 'string') { const classes = element.className.toLowerCase(); if (skipClassPrefixes.some(prefix => classes.includes(prefix))) { return true; } } let parent = element.parentElement; let depth = 0; while (parent && depth < 3) { if (parent.className && typeof parent.className === 'string') { const parentClasses = parent.className.toLowerCase(); if (skipClassPrefixes.some(prefix => parentClasses.includes(prefix))) { return true; } } parent = parent.parentElement; depth++; } try { const style = window.getComputedStyle(element); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { return true; } } catch (e) { } return false; } function extractFilenameFromUrl(url) { if (!url) return url; try { const pathname = url.split('?')[0].split('#')[0]; const parts = pathname.split('/'); const filename = parts[parts.length - 1]; return filename || url; } catch (e) { return url; } } function extractImagesFromElement(element) { if (!element) return []; const images = []; const imgElements = element.querySelectorAll('img[src], img[data-src]'); for (let i = 0; i < imgElements.length; i++) { const img = imgElements[i]; const src = img.src || img.getAttribute('data-src'); if (src && src.trim() !== '') { images.push(extractFilenameFromUrl(src)); } } const allElements = [element, ...element.querySelectorAll('*')]; for (let i = 0; i < allElements.length; i++) { const el = allElements[i]; try { const style = window.getComputedStyle(el); const bgImage = style.backgroundImage; if (bgImage && bgImage !== 'none') { const matches = bgImage.match(/url\\(['"]?([^'"()]+)['"]?\\)/g); if (matches) { matches.forEach(match => { const url = match.replace(/url\\(['"]?([^'"()]+)['"]?\\)/, '$1'); if (url && url.trim() !== '' && !url.startsWith('data:')) { images.push(extractFilenameFromUrl(url)); } }); } } } catch (e) { } } return [...new Set(images)]; } function extractSVGPathsFromElement(element) { if (!element) return null; const paths = []; const pathElements = element.querySelectorAll('svg path[d]'); for (let i = 0; i < pathElements.length; i++) { const d = pathElements[i].getAttribute('d'); if (d && d.trim() !== '') { paths.push(d.trim()); } } return paths.length > 0 ? paths.join(' ') : null; } function extractTextWithContext(element) { if (!element) return []; if (element.tagName && ['SCRIPT', 'STYLE', 'NOSCRIPT', 'HEAD'].includes(element.tagName)) { return []; } let results = []; if (element.shadowRoot) { const shadowResults = extractTextWithContext(element.shadowRoot); results.push(...shadowResults); } if (element.childNodes && element.childNodes.length > 0) { for (let i = 0; i < element.childNodes.length; i++) { const node = element.childNodes[i]; if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent.trim(); if (text) { const parent = node.parentElement; const grandParent = parent ? parent.parentElement : null; const greatGrandParent = grandParent ? grandParent.parentElement : null; if (shouldSkipElement(parent) || shouldSkipElement(grandParent) || shouldSkipElement(greatGrandParent)) { continue; } const nodeData = { text: text }; if (parent && parent.className) { nodeData.className = parent.className; } if (grandParent && grandParent.className) { nodeData.parentClassName = grandParent.className; } if (greatGrandParent && greatGrandParent.className) { nodeData.grandParentClassName = greatGrandParent.className; } if (parent) { const parentImages = extractImagesFromElement(parent); if (parentImages.length > 0) { nodeData.parentImages = parentImages; } } if (grandParent) { const grandParentImages = extractImagesFromElement(grandParent); if (grandParentImages.length > 0) { nodeData.grandParentImages = grandParentImages; } } let svgPaths = null; if (parent) { svgPaths = extractSVGPathsFromElement(parent); } if (!svgPaths && grandParent) { svgPaths = extractSVGPathsFromElement(grandParent); } if (!svgPaths && greatGrandParent) { svgPaths = extractSVGPathsFromElement(greatGrandParent); } if (svgPaths && svgPaths.length < 1000) { nodeData.svg = svgPaths; } results.push(nodeData); } } else if (node.nodeType === Node.ELEMENT_NODE) { const childResults = extractTextWithContext(node); results.push(...childResults); } } } return results; } function deduplicateImagesGlobally(nodes) { const urlCounts = new Map(); nodes.forEach(node => { ['parentImages', 'grandParentImages'].forEach(field => { if (node[field]) { node[field].forEach(url => { urlCounts.set(url, (urlCounts.get(url) || 0) + 1); }); } }); }); const seenUrls = new Map(); nodes.forEach(node => { ['parentImages', 'grandParentImages'].forEach(field => { if (node[field]) { node[field] = node[field].filter(url => { const totalCount = urlCounts.get(url) || 0; if (totalCount <= 2) { return true; } const currentCount = seenUrls.get(url) || 0; if (currentCount < 2) { seenUrls.set(url, currentCount + 1); return true; } return false; }); if (node[field].length === 0) { delete node[field]; } } }); }); return nodes; } function deduplicateNodesByClassNames(nodes) { const seenKeys = new Set(); nodes.forEach(node => { const hasClassNames = node.className || node.parentClassName || node.grandParentClassName; if (hasClassNames) { const key = (node.className || '') + '|' + (node.parentClassName || '') + '|' + (node.grandParentClassName || ''); if (seenKeys.has(key)) { delete node.className; delete node.parentClassName; delete node.grandParentClassName; } else { seenKeys.add(key); } } }); return nodes; } let textNodes = extractTextWithContext(document.body); textNodes = deduplicateImagesGlobally(textNodes); textNodes = deduplicateNodesByClassNames(textNodes); const result = { url: window.location.href, title: document.title, textNodes: textNodes }; window.extractedData = result; console.log('✅ v0.05'); return JSON.stringify(result); })())
  • world: MAIN(在页面主上下文中执行)
  • timeout: 15000(超时时间15秒,因为包含智能滚动和数据提取)

Read the full file on GitHub · 156 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. 2d ago First seen · 156 lines · 67 tokens per session scan A 824c5ee3e755

Subscribe to this mod's changes

web-data-extractor is a skill published in the GitHub repository ClawCap/ManoBrowser (9 stars, last pushed 4mo ago), licensed MIT. It adds 67 tokens to every session and 3,616 once invoked, about $0.0003 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.

Related

Other skills, from other repositories

aipex-browser

AI-powered browser automation using the AIPex Chrome Extension via MCP bridge. Use this skill when the agent needs to control a Chrome browser — navigating pages, clicking elements, filling forms, capturing screenshots, managing tabs, or downloading content — by connecting to the AIPex MCP bridge.

AIPexStudio/AIPex · 61 tokens

browser-relay

Control the Chrome the user already has open and logged in through the Browser Relay CLI, without launching a separate automation browser or taking over the foreground tab. Use when an agent needs to work with existing sessions, cookies, extensions, SSO or intranet pages, or a browser on another machine. Prefer the…

reliefeai/browser-relay · 86 tokens

just-scrape

Search, scrape, crawl, extract structured data, and monitor web pages via the ScrapeGraph AI CLI. Use when the user asks to search the web, scrape a webpage, grab content from a URL, extract JSON from a site, crawl documentation or site sections, monitor a page for changes, inspect request history, check ScrapeGraph…

ScrapeGraphAI/just-scrape · 80 tokens

owb

Open Web Bridge (OWB) — drive the user's own real browser with the owb command. Read pages behind their existing logins, gather and cross-check information, fill forms, walk multi-step flows, debug their site, audit responsive/accessibility behavior, and capture or reverse-engineer network traffic. Use this whenever…

woniu9524/open-web-bridge · 143 tokens

website-explorer

Discover website capabilities from user behaviors. Learn APIs and automate workflows.

EndymionLee/PilotBrowseMCP · 17 tokens

website-explorer

通过用户行为发现网站能力,学习 API 并沉淀自动化流程.

EndymionLee/PilotBrowseMCP · 21 tokens