提取网页表格,一键导出 CSV:一个 Bookmarklet 就够了

做数据分析、爬虫或竞品调研时,经常碰到网页上挂着一张现成的表格,手动复制粘贴到 Excel 不仅费时,还会丢掉格式、弄乱列对齐。本文分享一个 Bookmarklet(书签脚本),点击即可提取页面中的所有表格,自动转成 CSV 并写入剪贴板,直接粘贴进 Excel 就能用。

最终效果预览

在任意包含 <table> 的页面上点击书签:

  1. 弹窗告知发现多少个表格,每个表格的行列数
  2. 页面中的表格被蓝色边框高亮,视觉上确认命中范围
  3. CSV 数据自动复制到剪贴板,打开 Excel 直接粘贴即可
  4. 控制台以表格形式输出每个表格的数据结构,方便核对

Bookmarklet 是什么?

Bookmarklet 就是把一段 JavaScript 代码以 javascript: 协议的形式保存在书签中。点击书签时,浏览器会在当前页面上下文中执行这段代码。

优点:无需安装插件、无需 F12 开控制台、不依赖任何框架、所有现代浏览器都支持。


完整代码

以下就是提取表格的 Bookmarklet,新建书签,在"网址"栏粘贴全部内容即可。

javascript:(function () {
    var tables = document.querySelectorAll('table');
    if (!tables.length) {
        alert('当前页面未找到任何表格');
        return;
    }

    function clean(s) {
        if (s == null) return '';
        return String(s).replace(/\s+/g, ' ').trim();
    }

    function parseTable(table) {
        var rows = table.querySelectorAll('tr');
        var data = [];
        var colspans = [];
        rows.forEach(function (tr, r) {
            var cells = tr.querySelectorAll('th,td');
            var row = [];
            var ci = 0;
            while (colspans[ci] && colspans[ci].r > r) {
                row.push(colspans[ci].v);
                ci++;
            }
            cells.forEach(function (cell) {
                while (colspans[ci] && colspans[ci].r > r) {
                    row.push(colspans[ci].v);
                    ci++;
                }
                var txt = clean(cell.innerText);
                var cs = parseInt(cell.getAttribute('colspan') || '1', 10);
                var rs = parseInt(cell.getAttribute('rowspan') || '1', 10);
                for (var k = 0; k < cs; k++) {
                    row.push(txt);
                    if (rs > 1) {
                        colspans[ci] = { v: txt, r: r + rs };
                    }
                    ci++;
                }
            });
            while (colspans[ci] && colspans[ci].r > r) {
                row.push(colspans[ci].v);
                ci++;
            }
            data.push(row);
        });
        return data;
    }

    function toCSV(data) {
        var out = [];
        data.forEach(function (t, idx) {
            if (idx > 0) out.push('');
            out.push('=== Table ' + (idx + 1) + ' ===');
            t.forEach(function (row) {
                out.push(row.map(function (c) {
                    c = String(c == null ? '' : c);
                    if (/[",\n\r]/.test(c)) {
                        return '"' + c.replace(/"/g, '""') + '"';
                    }
                    return c;
                }).join(','));
            });
        });
        return out.join('\n');
    }

    function copy(text) {
        if (navigator.clipboard && navigator.clipboard.writeText) {
            navigator.clipboard.writeText(text).then(
                function () { console.log('✅ 表格已复制到剪贴板'); },
                function () { fallbackCopy(text); }
            );
        } else {
            fallbackCopy(text);
        }
    }
    function fallbackCopy(text) {
        var ta = document.createElement('textarea');
        ta.value = text;
        ta.style.position = 'fixed';
        ta.style.opacity = '0';
        document.body.appendChild(ta);
        ta.select();
        try { document.execCommand('copy'); } catch (e) {}
        document.body.removeChild(ta);
    }

    var all = [];
    tables.forEach(function (t) {
        t.style.outline = '2px solid #1976d2';
        all.push(parseTable(t));
    });
    var csv = toCSV(all);
    copy(csv);
    var summary = '📑 共发现 ' + tables.length + ' 个表格';
    if (tables.length === 1) {
        var rows = all[0].length;
        var cols = all[0][0] ? all[0][0].length : 0;
        summary += '\n行 x 列: ' + rows + ' x ' + cols;
    } else {
        summary += '\n' + all.map(function (d, i) {
            return '  #' + (i + 1) + ': ' + d.length + ' 行 x ' + (d[0] ? d[0].length : 0) + ' 列';
        }).join('\n');
    }
    summary += '\nCSV 已复制到剪贴板,可在控制台查看完整数据';
    alert(summary);
    console.log('🎯 表格提取完成,共 ' + tables.length + ' 个表格');
    all.forEach(function (d, i) {
        console.log('=== Table ' + (i + 1) + ' ===');
        console.table(d);
    });
    console.log('📋 CSV 内容:\n' + csv);
})();

使用方法

  1. 在浏览器中打开书签管理(Chrome 按 Ctrl+Shift+O
  2. 新建书签,名称建议设为 📑 提取表格
  3. 网址一栏粘贴上方完整代码(确保以 javascript: 开头)
  4. 打开任意包含表格的网页(如维基百科的统计页面、官网的价目表等)
  5. 点击该书签
  6. 打开 Excel / Google Sheets / 文本编辑器,按 Ctrl+V 粘贴

运行效果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


核心实现思路

1. 定位表格

var tables = document.querySelectorAll('table');

用原生 querySelectorAll 获取页面中所有 <table> 元素,一行搞定表格发现。无表格时提前 return 并提示,避免用户困惑。

2. 解析表格结构:colspan / rowspan 处理

真正的难点在这里。HTML 表格允许单元格跨列(colspan)和跨行(rowspan),解析时需要记录跨行单元格的"占位"信息。

实现方案是维护一个 colspans 数组,索引对应列位置,每个元素记录占位值 v 和有效行号 r

colspans[ci] = { v: '单元格文本', r: rowIndex + rowspan }

每处理新的一行时,先检查 colspans 中是否有仍在生效中的占位(r > 当前行号),有则先填充,再处理该行实际单元格。这样无论表格嵌套多复杂,输出永远是对齐的二维数组。

3. 导出 CSV

a,"b,c",d → a,"b,c",d
a"b → a,"b""",d

当单元格内容包含逗号、双引号或换行符时,用双引号包裹并将内部双引号转义为 "",严格遵循 RFC 4180 规范。

4. 复制到剪贴板

优先使用 navigator.clipboard.writeText(现代 API,异步),不支持时降级到传统的 document.execCommand('copy')(通过临时创建不可见 <textarea> 实现)。

5. 可视化反馈

  • 页面:每个命中的表格加上蓝色外框 outline: 2px solid #1976d2
  • 弹窗:表格总数 + 各表行列数
  • 控制台:console.table 输出结构化的表格数据 + 完整 CSV 文本

方案对比

方式 便捷度 功能完整度 适用场景
手动选中 → Ctrl+C → 粘贴 极少量手工操作
本 Bookmarklet ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ 日常快速提取,一劳永逸
浏览器插件 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 需要长期使用、批量处理、导出多格式

适当场景举例

  • 维基百科/数据平台:人口统计、经济指标等结构化数据表,一键转 CSV
  • 官网价目表:提取产品列表、套餐对比等定价信息
  • 内部后台页面:管理后台的列表页往往就是 <table>,导出第三方系统的数据
  • 竞品调研:电商搜索结果、招聘列表等,批量结构化采集

总结

网页中的 <table> 数据随处可见,手动复制粘贴的低效远比想象中浪费的时间多。这个不到 100 行的小书签,点一下就能完成"提取 → 结构化 → 复制"全流程,是一个典型的"花 5 分钟配置,省 N 小时重复劳动"的实用工具。


posted @ 2026-08-01 11:15  EXIORAN  阅读(17)  评论(0)    收藏  举报