使用纯JavaScript将HTML表格导出为CSV

89

我正在尝试在我的网站中添加一个CSV下载选项的功能。它应该将网站中的HTML表格转换为CSV内容并使其可下载。我通过互联网寻找了一些有用的插件,如http://www.dev-skills.com/export-html-table-to-csv-file/,但它使用PHP脚本来进行下载部分。我想知道是否有一个纯JavaScript库可用于使用服务器端软件(如node.js)而不使用PHP来执行此功能?


2
可能是在jQuery中导出为CSV的重复问题。 - Italo Borssatto
https://www.codexworld.com/export-html-table-data-to-csv-using-javascript/ - Stack Underflow
10个回答

125

应该能在每个现代浏览器上运行,不需要jQuery或任何依赖项,这里是我的实现:

// Quick and simple export target #table_id into a csv
function download_table_as_csv(table_id, separator = ',') {
    // Select rows from table_id
    var rows = document.querySelectorAll('table#' + table_id + ' tr');
    // Construct csv
    var csv = [];
    for (var i = 0; i < rows.length; i++) {
        var row = [], cols = rows[i].querySelectorAll('td, th');
        for (var j = 0; j < cols.length; j++) {
            // Clean innertext to remove multiple spaces and jumpline (break csv)
            var data = cols[j].innerText.replace(/(\r\n|\n|\r)/gm, '').replace(/(\s\s)/gm, ' ')
            // Escape double-quote with double-double-quote (see https://dev59.com/vmMm5IYBdhLWcg3wKsrO)
            data = data.replace(/"/g, '""');
            // Push escaped string
            row.push('"' + data + '"');
        }
        csv.push(row.join(separator));
    }
    var csv_string = csv.join('\n');
    // Download it
    var filename = 'export_' + table_id + '_' + new Date().toLocaleDateString() + '.csv';
    var link = document.createElement('a');
    link.style.display = 'none';
    link.setAttribute('target', '_blank');
    link.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv_string));
    link.setAttribute('download', filename);
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

然后添加您的下载按钮/链接:

<a href="#" onclick="download_table_as_csv('my_id_table_to_export');">Download as CSV</a>

CSV文件带有时间戳,并且与默认的Excel格式兼容。

评论后更新:添加了第二个参数“separator”,它可以用来配置另一个字符,如;。如果您的用户在世界不同地区下载您的csv文件,则可以使用另一个默认分隔符进行配置,以获取更多信息,请参见:https://superuser.com/a/606274/908273


5
太好了! 将csv.push(row.join(';')); 更改为csv.push(row.join(',')); 后,它对我有用了。 - Shivanshu
1
我刚在查找有关 Excel 默认分隔符的信息,它取决于地区,因此确实有可能对某些人来说需要在代码中更改分隔符。请参阅:https://superuser.com/a/606274/908273 - Calumah
2
我不得不将 var rows = document.querySelectorAll('table#' + table_id + ' tr'); 更改为 var rows = document.querySelectorAll('#' + table_id + ' tr'); 才能使其正常工作,但现在一切都好了。 - Grintor
这个很好用,我需要添加一个替换非断行空格字符的方法,像这样:var data = cols[j].innerText.replace(/\u00a0/g, ''); 然后现有的数据 = data.replace(/(\r\n|\n|\r)/gm, '').replace(/(\s\s)/gm, ' ')。我尝试编辑答案,但编辑队列已满。 - Chris
对我来说,它似乎在网格的筛选行中添加了一个空行? - c-sharp-and-swiftui-devni
显示剩余6条评论

45

只使用 jQuery、原生 Javascripttable2CSV 库:

export-to-html-table-as-csv-file-using-jquery

将此代码放入要在 head 部分加载的脚本中:

 $(document).ready(function () {
    $('table').each(function () {
        var $table = $(this);

        var $button = $("<button type='button'>");
        $button.text("Export to spreadsheet");
        $button.insertAfter($table);

        $button.click(function () {
            var csv = $table.table2CSV({
                delivery: 'value'
            });
            window.location.href = 'data:text/csv;charset=UTF-8,' 
            + encodeURIComponent(csv);
        });
    });
})

注意:

需要 jQuerytable2CSV:请在上述脚本之前添加两个库的脚本引用。

table选择器仅作为示例使用,可以根据您的需求进行调整。

仅在支持完整Data URI的浏览器中工作:Firefox、Chrome和Opera,不适用于仅支持将二进制图像数据嵌入页面的IE。

为了实现完全的浏览器兼容性,您需要使用稍微不同的方法,该方法需要使用服务器端脚本来echo CSV。


1
嗨...感谢您的回答...但是当我尝试从您提供的链接下载tabletoCsv文件时,它显示错误“页面未找到”。 - sam
谢谢回复!!!我非常感激你的帮助!!在大量搜索后,我还发现了另一个有趣的选项 http://www.datatables.net/examples/ - sam
table2csv.com是一款命令行程序,正确的链接应该在Italo的答案中:http://www.kunalbabre.com/projects/table2CSV.php。 - Kokizzu
1
我已经尝试了上面大部分的方法,但最简单的是这个:http://jordiburgos.com/post/2014/excellentexport-javascript-export-to-excel-csv.html - Buddhika Ariyaratne
1
有没有设置文件名的方法? - FutoRicky
显示剩余3条评论

15

这里有一个非常简单、免费且开源的解决方案在http://jordiburgos.com/post/2014/excellentexport-javascript-export-to-excel-csv.html

首先从https://github.com/jmaister/excellentexport/releases/tag/v1.4下载 javascript 文件和示例文件。

HTML 页面如下所示。

确保 JavaScript 文件与 HTML 文件在同一文件夹中,或者相应地更改 HTML 文件中脚本的路径。

<html>
<head>
    <title>Export to excel test</title>
    <script src="excellentexport.js"></script>
    <style>
        table, tr, td {
            border: 1px black solid;
        }
    </style>
</head>
<body>
    <h1>ExcellentExport.js</h1>

    Check on <a href="http://jordiburgos.com">jordiburgos.com</a> and  <a href="https://github.com/jmaister/excellentexport">GitHub</a>.

    <h3>Test page</h3>

    <br/>

    <a download="somedata.xls" href="#" onclick="return ExcellentExport.excel(this, 'datatable', 'Sheet Name Here');">Export to Excel</a>
    <br/>

    <a download="somedata.csv" href="#" onclick="return ExcellentExport.csv(this, 'datatable');">Export to CSV</a>
    <br/>

    <table id="datatable">
        <tr>
            <th>Column 1</th>
            <th>Column "cool" 2</th>
            <th>Column 3</th>
        </tr>
        <tr>
            <td>100,111</td>
            <td>200</td>
            <td>300</td>
        </tr>
        <tr>
            <td>400</td>
            <td>500</td>
            <td>600</td>
        </tr>
        <tr>
            <td>Text</td>
            <td>More text</td>
            <td>Text with
                new line</td>
        </tr>
    </table>

</body>

我已经尝试了许多其他方法,但使用这个非常容易。


1
可以使用按钮代替锚点吗? - Hooli
我有一个使用JavaScript动态生成的表格。显然,这个解决方案不起作用。在返回的ExcellentExport.csv(this, 'datatable')中,“this”是指什么? - Nguai al

9

您不需要在服务器端使用PHP脚本。只需在客户端中完成,使用支持数据URI的浏览器:

data:application/csv;charset=utf-8,content_encoded_as_url

数据URI将类似于:
data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333

您可以通过以下方式调用此URI:
  • 使用window.open
  • 或设置window.location
  • 或通过锚点的href属性
  • 添加下载属性,它将在Chrome中起作用,仍需在IE中进行测试。
要测试,请简单地复制上面的URI并粘贴到您的浏览器地址栏中。或在HTML页面中测试下面的锚点:
<a download="somedata.csv" href="data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333">Example</a>

要创建内容并从表格中获取值,可以使用table2CSV,该工具由MelanciaUK提供,并执行以下操作:

var csv = $table.table2CSV({delivery:'value'});
window.location.href = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(csv);

6

(1) 这是本地 JavaScript 的解决方案。它适用于大多数现代浏览器。

function export2csv() {
  let data = "";
  const tableData = [];
  const rows = document.querySelectorAll("table tr");
  for (const row of rows) {
    const rowData = [];
    for (const [index, column] of row.querySelectorAll("th, td").entries()) {
      // To retain the commas in the "Description" column, we can enclose those fields in quotation marks.
      if ((index + 1) % 3 === 0) {
        rowData.push('"' + column.innerText + '"');
      } else {
        rowData.push(column.innerText);
      }
    }
    tableData.push(rowData.join(","));
  }
  data += tableData.join("\n");
  const a = document.createElement("a");
  a.href = URL.createObjectURL(new Blob([data], { type: "text/csv" }));
  a.setAttribute("download", "data.csv");
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
table {
  border-collapse: collapse;
}

td, th {
  border: 1px solid #aaa;
  padding: 0.5rem;
  text-align: left;
}

td {
  font-size: 0.875rem;
}

.btn-group {
  padding: 1rem 0;
}

button {
  background-color: #fff;
  border: 1px solid #000;
  margin-top: 0.5rem;
  border-radius: 3px;
  padding: 0.5rem 1rem;
  font-size: 1rem;
}

button:hover {
  cursor: pointer;
  background-color: #000;
  color: #fff;
}
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Author</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>jQuery</td>
      <td>John Resig</td>
      <td>The Write Less, Do More, JavaScript Library.</td>
    </tr>
    <tr>
      <td>React</td>
      <td>Jordan Walke</td>
      <td>React makes it painless to create interactive UIs.</td>
    </tr>
    <tr>
      <td>Vue.js</td>
      <td>Yuxi You</td>
      <td>The Progressive JavaScript Framework.</td>
    </tr>
  </tbody>
</table>

<div class="btn-group">
  <button onclick="export2csv()">csv</button>
</div>

(2) 如果你需要一个纯 JavaScript 库,FileSaver.js 可以帮助你保存用于触发文件下载的代码片段。此外,FileSaver.js 不会负责构建导出内容,你必须自己按照所需格式构建内容。


6

现代解决方案

这里提出的大多数解决方案都会在td元素内部包含嵌套表格或其他元素时出现问题。我经常在表格中使用其他元素,但只想导出最顶层的表格。我从Calumah的代码中取出了一些并添加了现代的vanilla ES6 JS。

使用textContent比innerText更好,因为innerText将返回td元素内部的任何HTML。但是,即使是textContent也会返回嵌套元素的文本。更好的解决方案是在td上使用自定义数据属性,并从中提取CSV的值。

Happy coding!

function downloadAsCSV(tableEle, separator = ','){
    let csvRows = []
    //only get direct children of the table in question (thead, tbody)
    Array.from(tableEle.children).forEach(function(node){
        //using scope to only get direct tr of node
        node.querySelectorAll(':scope > tr').forEach(function(tr){
            let csvLine = []
            //again scope to only get direct children
            tr.querySelectorAll(':scope > td').forEach(function(td){
                //clone as to not remove anything from original
                let copytd = td.cloneNode(true)
                let data
                if(copytd.dataset.val) data = copytd.dataset.val.replace(/(\r\n|\n|\r)/gm, '')
                else {
                    Array.from(copytd.children).forEach(function(remove){
                        //remove nested elements before getting text
                        remove.parentNode.removeChild(remove)   
                    })
                    data = copytd.textContent.replace(/(\r\n|\n|\r)/gm, '')
                }
                data = data.replace(/(\s\s)/gm, ' ').replace(/"/g, '""')
                csvLine.push('"'+data+'"')
            })
            csvRows.push(csvLine.join(separator))
        })
    })
    var a = document.createElement("a")
    a.style = "display: none; visibility: hidden" //safari needs visibility hidden
    a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvRows.join('\n'))
    a.download = 'testfile.csv'
    document.body.appendChild(a)
    a.click()
    a.remove()
}

编辑:将cloneNode()更新为cloneNode(true),以获取内部内容


4

使用上面的答案,但根据我的需求进行了修改。

我使用了以下函数,并将其导入到我需要下载csv文件的REACT文件中。

我的th元素中有一个span标签。添加了对大多数函数/方法的注释。

import { tableToCSV, downloadCSV } from './../Helpers/exportToCSV';


export function tableToCSV(){
  let tableHeaders = Array.from(document.querySelectorAll('th'))
    .map(item => {
      // title = splits elem tags on '\n',
      // then filter out blank "" that appears in array.
      // ex ["Timestamp", "[Full time]", ""]
      let title = item.innerText.split("\n").filter(str => (str !== 0)).join(" ")
      return title
    }).join(",")

  const rows = Array.from(document.querySelectorAll('tr'))
  .reduce((arr, currRow) => {
    // if tr tag contains th tag.
    // if null return array.
    if (currRow.querySelector('th')) return arr

    // concats individual cells into csv format row.
    const cells = Array.from(currRow.querySelectorAll('td'))
      .map(item => item.innerText)
      .join(',')
    return arr.concat([cells])
  }, [])

return tableHeaders + '\n' + rows.join('\n')
}

export function downloadCSV(csv){
  const csvFile = new Blob([csv], { type: 'text/csv' })
  const downloadLink =  document.createElement('a')
  // sets the name for the download file
  downloadLink.download = `CSV-${currentDateUSWritten()}.csv`
  // sets the url to the window URL created from csv file above
  downloadLink.href = window.URL.createObjectURL(csvFile)
  // creates link, but does not display it.
  downloadLink.style.display = 'none'
  // add link to body so click function below works
  document.body.appendChild(downloadLink)

  downloadLink.click()
}

当用户点击导出为 CSV 时,它会触发 React 中的以下函数。
  handleExport = (e) => {
    e.preventDefault();
    const csv = tableToCSV()
    return downloadCSV(csv)
  }

示例 HTML 表格元素。

  <table id="datatable">
        <tbody>
          <tr id="tableHeader" className="t-header">
            <th>Timestamp
              <span className="block">full time</span></th>
            <th>current rate
              <span className="block">alt view</span>
            </th>
            <th>Battery Voltage
              <span className="block">current voltage
              </span>
            </th>
            <th>Temperature 1
              <span className="block">[C]</span>
            </th>
            <th>Temperature 2
              <span className="block">[C]</span>
            </th>
            <th>Time & Date </th>
          </tr>

        </tbody>
        <tbody>
          {this.renderData()}
        </tbody>
      </table>
    </div>

谢谢你写出来了,你能否在downloadCSV()函数内部调用的currentDateUSWritten()函数中添加功能呢? - Preston Badeer

2

我发现有一个库可以做到这一点。请看这里的示例:

https://editor.datatables.net/examples/extensions/exportButtons.html

除了上面的代码之外,以下Javascript库文件也被加载以供此示例使用:
在HTML中,包含以下脚本:
jquery.dataTables.min.js   
dataTables.editor.min.js   
dataTables.select.min.js
dataTables.buttons.min.js  
jszip.min.js    
pdfmake.min.js
vfs_fonts.js  
buttons.html5.min.js    
buttons.print.min.js

通过添加脚本来启用按钮,例如:

<script>
$(document).ready( function () {
    $('#table-arrays').DataTable({
        dom: '<"top"Blf>rt<"bottom"ip>',
        buttons: ['copy', 'excel', 'csv', 'pdf', 'print'],
        select: true,
    });
} );
</script>

由于某些原因,Excel导出结果为损坏文件,但可以修复。或者,禁用Excel并使用CSV导出。


0
在我的情况下,我需要最基本的功能来抓取HTML表格数据并将其转换为CSV文件。这些值中没有任何干扰字符,所以我不需要考虑任何转义或编码问题。我也不喜欢声明只使用一次的变量,除非它们在表达所持有的数据方面很有价值。
我无法让下载机制在Stacksnippet中工作,但这里有一个可工作的jsfiddle演示
my_report更改为目标表格的id。 将my_scraped_report.csv更改为所需的下载文件的文件名。
function exportToCSV() {
    // The following was inspired by https://dev59.com/yWUp5IYBdhLWcg3wEETd#56370447
    const rows = document.querySelectorAll('table#my_report tr'),
          link = document.createElement('a');

    link.download = 'my_scraped_report.csv';
    link.href = URL.createObjectURL(
        new Blob(
            [
                Array.from(rows).map(row => {
                    return Array.from(row.querySelectorAll('td, th')).map(col => col.innerText).join(',');
                }).join('\n')
            ],
            { type: 'text/plain' }
        )
    );
    link.click();
}

被嘲笑的HTML代码:
<button onclick="exportToCSV()" style="margin-bottom: 30px">CSV</button>
<table id="my_report" border="1">
    <thead>
        <tr class="vertical-text" role="row">
            <th></th>
            <th>11/10/2023</th>
            <th>12/10/2023</th>
            <th>13/10/2023</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>One</td>
            <td class=" text-center">456</td>
            <td class=" text-center">298</td>
            <td class=" text-center">498</td>
        </tr>
        <tr>
            <td>Two</td>
            <td class=" text-center">123</td>
            <td class=" text-center">321</td>
            <td class=" text-center">466</td>
        </tr>
        <tr>
            <td>Three</td>
            <td class=" text-center">372</td>
            <td class=" text-center">193</td>
            <td class=" text-center">922</td>
        </tr>
    </tbody>
</table>

-2

我使用了Calumah上面发布的函数,但是我在他代码中遇到了一个问题。

行与分号连接在一起。

csv.push(row.join(';'));

但是生成的链接内容类型为"text/csv"

在Windows中可能没有问题,但在Mac的Excel中会出现问题。我将数组连接方式更改为逗号,这样就完美解决了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接