【问题标题】:How can I convert an HTML table to CSV?如何将 HTML 表格转换为 CSV?
【发布时间】:2010-11-27 01:09:12
【问题描述】:

如何将 HTML 表格 (<table>) 的内容转换为 CSV 格式?是否有执行此操作的库或 linux 程序?这类似于在 Internet Explorer 中复制表格,然后将它们粘贴到 Excel 中。

【问题讨论】:

标签: html csv html-table


【解决方案1】:

这是我仅使用 tr 和 sed 的方法:

< table.txt tr -d '\n' | #delete newlines
sed 's/<tr[^>]*>/\n/g' | #convert tr tags into newlines to break data into table rows
sed 's/<[^>]*td>/,/g' | #convert closing td tags into commas
sed 's/<[^>]*>//g' #delete all other html tags

示例输入(来自试图使用 MsoNormal 呈现 HTML 表格的 Outlook 电子邮件):

<table class=3D"MsoNormalTable" border=3D"0" cellspacing=3D"0" cellpadding=3D"0" width=3D"420" style=3D"width:315.0pt;border-collapse:collapse">
<tbody>
<tr style=3D"height:15.0pt">
<td width=3D"107" nowrap=3D"" style=3D"width:80.0pt;padding:0in 0in 0in 0in;height:15.0pt">
</td>
<td width=3D"107" nowrap=3D"" valign=3D"bottom" style=3D"width:80.0pt;padding:0in 0in 0in 0in;height:15.0pt">
</td>
<td width=3D"64" nowrap=3D"" valign=3D"bottom" style=3D"width:48.0pt;padding:0in 0in 0in 0in;height:15.0pt">
</td>
<td width=3D"79" nowrap=3D"" valign=3D"bottom" style=3D"width:59.0pt;padding:0in 0in 0in 0in;height:15.0pt">
</td>
<td width=3D"64" nowrap=3D"" valign=3D"bottom" style=3D"width:48.0pt;padding:0in 0in 0in 0in;height:15.0pt">
</td>
</tr>
<tr style=3D"height:6.75pt">
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:6.75pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:6.75pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:6.75pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:6.75pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:6.75pt"></td>
</tr>
<tr style=3D"height:15.0pt">
<td nowrap=3D"" valign=3D"bottom" style=3D"border:solid windowtext 1.0pt;padding:0in 0in 0in 0in;height:15.0pt">
<p class=3D"MsoNormal" align=3D"center" style=3D"text-align:center"><b><span style=3D"color:black">ID</span></b><b><span style=3D"color:black"><o:p></o:p></span></b></p>
</td>
<td nowrap=3D"" valign=3D"bottom" style=3D"border:solid windowtext 1.0pt;border-left:none;padding:0in 0in 0in 0in;height:15.0pt">
<p class=3D"MsoNormal" align=3D"center" style=3D"text-align:center"><b><span style=3D"color:black">Price<o:p></o:p></span></b></p>
</td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
</tr>
<tr style=3D"height:15.0pt">
<td nowrap=3D"" valign=3D"bottom" style=3D"border:solid windowtext 1.0pt;border-top:none;padding:0in 0in 0in 0in;height:15.0pt">
<p class=3D"MsoNormal" align=3D"center" style=3D"text-align:center"><span style=3D"color:black">064159Q</span><span style=3D"color:black"><o:p></o:p></span></p>
</td>
<td nowrap=3D"" valign=3D"bottom" style=3D"border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0in 0in 0in 0in;height:15.0pt">
<p class=3D"MsoNormal" align=3D"center" style=3D"text-align:center"><span style=3D"color:black">121.85<o:p></o:p></span></p>
</td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
</tr>
<tr style=3D"height:15.0pt">
<td nowrap=3D"" valign=3D"bottom" style=3D"border:solid windowtext 1.0pt;border-top:none;padding:0in 0in 0in 0in;height:15.0pt">
<p class=3D"MsoNormal" align=3D"center" style=3D"text-align:center"><span style=3D"color:black">2420128</span><span style=3D"color:black"><o:p></o:p></span></p>
</td>
<td nowrap=3D"" valign=3D"bottom" style=3D"border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0in 0in 0in 0in;height:15.0pt">
<p class=3D"MsoNormal" align=3D"center" style=3D"text-align:center"><span style=3D"color:black">10.00<o:p></o:p></span></p>
</td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
<td nowrap=3D"" valign=3D"bottom" style=3D"padding:0in 0in 0in 0in;height:15.0pt"></td>
</tr>
</tbody>
</table>

样本输出:


,,,,,
,,,,,
ID,Price,,,,
064159Q,121.85,,,,
2420128,10.00,,,,

有关该方法的讨论,请参阅 Non-greedy regex matching in sed。

【讨论】:

    【解决方案2】:

    根据您的需要,您可以简单地:

    var table ='';var selector='#customers';
    document.querySelectorAll(`${selector} tr th`).forEach(h=>table+=`${h.innerText.trim()};`);table=table.trim();table+='\r\n';
    document.querySelectorAll(`${selector} tr`).forEach(tr=>{tr.querySelectorAll('td').forEach(td=>table+=`${td.innerText.trim()};`);table+='\r\n';});
    

    更改“选择器”以定位您的表格,执行“表格”后将拥有您的 csv 内容

    此外,您还可以:

    var a = document.createElement('a');a.href=`data:text/csv;base64,${btoa(table)}`;a.download="table.csv";a.click();
    

    下载“表格”的内容

    【讨论】:

      【解决方案3】:

      读取HTML文件并使用Ruby的CSV和nokogiri输出到.csv。

      基于@audiodude's answer,但做了以下修改:

      • 从文件中读取以获取 HTML。这对于长 HTML 表格很方便,但如果您的 HTML 表格很小,则可以轻松修改为仅使用静态字符串。
      • 使用CSV 的内置库将Array 转换为CSV 行。
      • 输出到.csv 文件,而不是仅仅打印到STDOUT。
      • 获取表头 (th) 和表体 (td)。
      # Convert HTML table to CSV format.
      
      require "nokogiri"
      
      html_file_path = ""
      
      html_string = File.read( html_file_path )
      
      doc = Nokogiri::HTML( html_string )
      
      CSV.open( Rails.root.join( Time.zone.now.to_s( :file ) + ".csv" ), "wb" ) do |csv|
        doc.xpath( "//table//tr" ).each do |row|
          csv << row.xpath( "th|td" ).collect( &:text ).collect( &:strip )
        end
      end
      

      【讨论】:

        【解决方案4】:

        假设您设计了一个包含table 的 HTML 页面,我会推荐此解决方案。对我来说就像魅力一样:

        $(document).ready(() => {
          $("#buttonExport").click(e => {
            // Getting values of current time for generating the file name
            const dateTime = new Date();
            const day      = dateTime.getDate();
            const month    = dateTime.getMonth() + 1;
            const year     = dateTime.getFullYear();
            const hour     = dateTime.getHours();
            const minute   = dateTime.getMinutes();
            const postfix  = `${day}.${month}.${year}_${hour}.${minute}`;
        
            // Creating a temporary HTML link element (they support setting file names)
            const downloadElement = document.createElement('a');
        
            // Getting data from our `div` that contains the HTML table
            const dataType  = 'data:application/vnd.ms-excel';
            const tableDiv  = document.getElementById('divData');
            const tableHTML = tableDiv.outerHTML.replace(/ /g, '%20');
        
            // Setting the download source
            downloadElement.href = `${dataType},${tableHTML}`;
        
            // Setting the file name
            downloadElement.download = `exported_table_${postfix}.xls`;
        
            // Trigger the download
            downloadElement.click();
        
            // Just in case, prevent default behaviour
            e.preventDefault();
          });
        });
        

        礼貌:http://www.kubilayerdogan.net/?p=218

        您可以在此处将文件格式编辑为.csv:

        downloadElement.download = `exported_table_${postfix}.csv`;
        

        【讨论】:

        • 这在 IE 中有效吗?当我单击导出按钮时,什么也没有发生。
        【解决方案5】:

        这是Yuvai's answer 的更新版本,它可以正确处理需要引用的字段(即数据中包含逗号、双引号或跨多行的字段)

        #!/usr/bin/env python3
        from html.parser import HTMLParser
        import sys
        import re
        
        class HTMLTableParser(HTMLParser):
            def __init__(self, row_delim="\n", cell_delim=","):
                HTMLParser.__init__(self)
                self.despace_re = re.compile("\s+")
                self.data_interrupt = False
                self.first_row = True
                self.first_cell = True
                self.in_cell = False
                self.row_delim = row_delim
                self.cell_delim = cell_delim
                self.quote_buffer = False
                self.buffer = None
        
            def handle_starttag(self, tag, attrs):
                self.data_interrupt = True
                if tag == "table":
                    self.first_row = True
                    self.first_cell = True
                elif tag == "tr":
                    if not self.first_row:
                        sys.stdout.write(self.row_delim)
                    self.first_row = False
                    self.first_cell = True
                    self.data_interrupt = False
                elif tag == "td" or tag == "th":
                    if not self.first_cell:
                        sys.stdout.write(self.cell_delim)
                    self.first_cell = False
                    self.data_interrupt = False
                    self.in_cell = True
                elif tag == "br":
                    self.quote_buffer = True
                    self.buffer += self.row_delim
        
            def handle_endtag(self, tag):
                self.data_interrupt = True
                if tag == "td" or tag == "th":
                    self.in_cell = False
                if self.buffer != None:
                    # Quote if needed...
                    if self.quote_buffer or self.cell_delim in self.buffer or "\"" in self.buffer:
                        # Need to quote! First, replace all double-quotes with quad-quotes
                        self.buffer = self.buffer.replace("\"", "\"\"")
                        self.buffer = "\"{0}\"".format(self.buffer)
                    sys.stdout.write(self.buffer)
                    self.quote_buffer = False
                    self.buffer = None
        
            def handle_data(self, data):
                if self.in_cell:
                    #if self.data_interrupt:
                    #   sys.stdout.write(" ")
                    if self.buffer == None:
                        self.buffer = ""
                    self.buffer += self.despace_re.sub(" ", data).strip()
                    self.data_interrupt = False
        
        parser = HTMLTableParser() 
        parser.feed(sys.stdin.read())
        

        此脚本的一项改进可能是添加对指定不同行分隔符(或自动计算平台正确的分隔符)和不同列分隔符的支持。

        【讨论】:

          【解决方案6】:

          这里是一个没有任何外部库的简单解决方案:

          https://www.codexworld.com/export-html-table-data-to-csv-using-javascript/

          它对我有用,没有任何问题

          【讨论】:

            【解决方案7】:

            这是一个使用pQuery 和Spreadsheet::WriteExcel 的示例:

            use strict;
            use warnings;
            
            use Spreadsheet::WriteExcel;
            use pQuery;
            
            my $workbook = Spreadsheet::WriteExcel->new( 'data.xls' );
            my $sheet    = $workbook->add_worksheet;
            my $row = 0;
            
            pQuery( 'http://www.blahblah.site' )->find( 'tr' )->each( sub{
                my $col = 0;
                pQuery( $_ )->find( 'td' )->each( sub{
                    $sheet->write( $row, $col++, $_->innerHTML );
                });
                $row++;
            });
            
            $workbook->close;
            

            该示例只是将它找到的所有 tr 标记提取到一个 excel 文件中。您可以轻松地对其进行定制以获取特定的 table 甚至触发每个 table 标记的新 excel 文件。

            需要考虑的其他事项:

            • 您可能需要选择 td 标签来创建 excel 标头。
            • 您可能会遇到有关行跨度和列跨度的问题。

            要查看是否使用了 rowspan 或 colspan,您可以:

            pQuery( $data )->find( 'td' )->each( sub{ 
                my $number_of_cols_spanned = $_->getAttribute( 'colspan' );
            });
            

            【讨论】:

              【解决方案8】:

              只是为了补充这些答案(因为我最近一直在尝试类似的事情) - 如果 Google 电子表格 是您选择的电子表格程序。只需做这两件事。

              1. 将 表格开始/结束标签 周围的所有内容从 html 文件中剥离出来,并将其重新保存为另一个 html 文件。

              2. 将该 html 文件直接导入到谷歌电子表格中,您的信息将被精美地导入 (重要提示:如果您在表格中使用内联样式,它们将被导入为好吧!)

              为我节省了大量时间并找出不同的转化。

              【讨论】:

              【解决方案9】:

              这是我为完成这项任务而编写的一个简短的 Python 程序。它是在几分钟内写的,所以它可能会变得更好。不确定它将如何处理嵌套表(可能会做坏事)或多个表(可能它们会一个接一个出现)。它不处理colspan 或rowspan。 享受吧。

              from HTMLParser import HTMLParser
              import sys
              import re
              
              
              class HTMLTableParser(HTMLParser):
                  def __init__(self, row_delim="\n", cell_delim="\t"):
                      HTMLParser.__init__(self)
                      self.despace_re = re.compile(r'\s+')
                      self.data_interrupt = False
                      self.first_row = True
                      self.first_cell = True
                      self.in_cell = False
                      self.row_delim = row_delim
                      self.cell_delim = cell_delim
              
                  def handle_starttag(self, tag, attrs):
                      self.data_interrupt = True
                      if tag == "table":
                          self.first_row = True
                          self.first_cell = True
                      elif tag == "tr":
                          if not self.first_row:
                              sys.stdout.write(self.row_delim)
                          self.first_row = False
                          self.first_cell = True
                          self.data_interrupt = False
                      elif tag == "td" or tag == "th":
                          if not self.first_cell:
                              sys.stdout.write(self.cell_delim)
                          self.first_cell = False
                          self.data_interrupt = False
                          self.in_cell = True
              
                  def handle_endtag(self, tag):
                      self.data_interrupt = True
                      if tag == "td" or tag == "th":
                          self.in_cell = False
              
                  def handle_data(self, data):
                      if self.in_cell:
                          #if self.data_interrupt:
                          #   sys.stdout.write(" ")
                          sys.stdout.write(self.despace_re.sub(' ', data).strip())
                          self.data_interrupt = False
              
              
              parser = HTMLTableParser() 
              parser.feed(sys.stdin.read()) 
              

              【讨论】:

              • 谢谢,这很有用!不过,我使用了分隔符“,”而不是“\t”。
              • 谢谢@botismarius。我添加了将分隔符指定为参数的选项。
              【解决方案10】:

              这基于atomicules' answer,但更简洁,还处理th(标题)单元格以及td 单元格。我还添加了strip 方法来消除多余的空格。

              CSV.open("output.csv", 'w') do |csv|
                doc.xpath('//table//tr').each do |row|
                  csv << row.xpath('th|td').map {|cell| cell.text.strip}
                end
              end
              

              将代码封装在 CSV 块内可确保文件正确关闭。


              如果你只想要文本而不需要将它写入文件,你可以使用这个:

              doc.xpath('//table//tr').inject('') do |result, row|
                result << row.xpath('th|td').map {|cell| cell.text.strip}.to_csv
              end
              

              【讨论】:

                【解决方案11】:

                很抱歉复活了一个古老的线程,但我最近想这样做,但我想要一个 100% 可移植的 bash 脚本来做到这一点。所以这是我只使用 grep 和 sed 的解决方案。

                下面的内容很快就被淘汰了,因此可以做得更优雅,但我才真正开始使用 sed/awk 等...

                curl "http://www.webpagewithtableinit.com/" 2>/dev/null | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' | sed 's/^[\ \t]*//g' | tr -d '\n' | sed 's/<\/TR[^>]*>/\n/Ig'  | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' | sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' | sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig'
                

                如您所见,我使用 curl 获得了页面源,但您也可以轻松地从其他地方输入表格源。

                解释如下:

                使用 cURL 获取 URL 的内容,将 stderr 转储为 null(无进度表)

                curl "http://www.webpagewithtableinit.com/" 2>/dev/null 
                

                .

                我只想要表格元素(只返回带有 TABLE、TR、TH、TD 标签的行)

                | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH'
                

                .

                删除行首的所有空格。

                | sed 's/^[\ \t]*//g' 
                

                .

                删除换行符

                | tr -d '\n\r' 
                

                .

                用换行符替换&lt;/TR&gt;

                | sed 's/<\/TR[^>]*>/\n/Ig'  
                

                .

                删除 TABLE 和 TR 标签

                | sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' 
                

                .

                删除^&lt;TD&gt;、^&lt;TH&gt;、&lt;/TD&gt;$、&lt;/TH&gt;$

                | sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' 
                

                .

                用逗号替换&lt;/TD&gt;&lt;TD&gt;

                | sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig'
                

                .

                请注意,如果任何表格单元格包含逗号,您可能需要先对其进行转义,或使用不同的分隔符。

                希望这对某人有所帮助!

                【讨论】:

                • 好主意,但不适用于很多 HTML 文件。我的数据在表中单独一行(即&lt;td&gt;\ncell value\n&lt;/td&gt;),这个脚本最终删除了这些数据,因为它删除了每一行没有表标签的行。
                • 很酷的东西。即使在数据字段中有一些新行,它也对我有用。干得好!
                • 稍作调整对我来说效果很好,真的为我节省了几个小时!
                • @Drendar 我喜欢你的回答,但我不能将它用于dsebd.org/market_summary.php 这个链接。
                【解决方案12】:

                这是一个非常古老的线程,但可能像我这样的人会碰到它。 我为audiodude 的脚本添加了一些内容,以便从文件中读取 html,而不是将其添加到代码中,以及控制标题行打印的另一个参数。

                脚本应该这样运行

                ruby <script_name> <file_name> [<print_headers>]
                

                代码是:

                require 'nokogiri'
                
                print_header_lines = ARGV[1]
                
                File.open(ARGV[0]) do |f|
                
                  table_string=f
                  doc = Nokogiri::HTML(table_string)
                
                  doc.xpath('//table//tr').each do |row|
                    if print_header_lines
                      row.xpath('th').each do |cell|
                        print '"', cell.text.gsub("\n", ' ').gsub('"', '\"').gsub(/(\s){2,}/m, '\1'), "\", "
                      end
                    end
                    row.xpath('td').each do |cell|
                      print '"', cell.text.gsub("\n", ' ').gsub('"', '\"').gsub(/(\s){2,}/m, '\1'), "\", "
                    end
                    print "\n"
                  end
                end
                

                【讨论】:

                  【解决方案13】:

                  使用 Perl,您可以使用 HTML::TableExtract 模块从表中提取数据,然后使用 Text::CSV_XS 创建 CSV 文件或使用 Spreadsheet::WriteExcel 创建 Excel 文件。

                  【讨论】:

                    【解决方案14】:

                    基于audiodude's answer,但使用内置 CSV 库进行了简化

                    require 'nokogiri'
                    require 'csv'
                    
                    doc = Nokogiri::HTML(table_string)
                    csv = CSV.open("output.csv", 'w')
                    
                    doc.xpath('//table//tr').each do |row|
                        tarray = [] #temporary array
                        row.xpath('td').each do |cell|
                            tarray << cell.text #Build array of that row of data.
                        end
                        csv << tarray #Write that row out to csv file
                    end
                    
                    csv.close
                    

                    我确实想知道是否有任何方法可以获取 Nokogiri 节点集 (row.xpath('td')) 并一步将其作为数组写入 csv 文件。但我只能通过遍历每个单元格并构建每个单元格内容的临时数组来解决这个问题。

                    【讨论】:

                      【解决方案15】:

                      OpenOffice.org 可以查看 HTML 表格。只需在 HTML 文件上使用打开命令,或在浏览器中选择并复制表格,然后在 OpenOffice.org 中选择性粘贴。它将向您查询文件类型,其中之一应该是 HTML。选择它,瞧!

                      【讨论】:

                        【解决方案16】:

                        我不确定是否有为此准备的库,但如果您愿意使用一点 Perl,您可以使用 Text::CSV 和 HTML::Parser 做一些事情。

                        【讨论】:

                        • 这正是我给出的答案。 +1 :-)
                        【解决方案17】:
                        【解决方案18】:

                        这是一个使用 nokogiri 的 ruby​​ 脚本 -- http://nokogiri.rubyforge.org/nokogiri/

                        require 'nokogiri'
                        
                        doc = Nokogiri::HTML(table_string)
                        
                        doc.xpath('//table//tr').each do |row|
                          row.xpath('td').each do |cell|
                            print '"', cell.text.gsub("\n", ' ').gsub('"', '\"').gsub(/(\s){2,}/m, '\1'), "\", "
                          end
                          print "\n"
                        end
                        

                        为我的基本测试用例工作。

                        【讨论】:

                        • 谢谢,这对于一个 32M 的文件非常有用,该文件有 220k 行从数据库转储到 php 表中。 (对于大多数电子表格来说显然不是一个选项!)
                        • 这就是我如此喜欢红宝石的原因。
                        【解决方案19】:

                        这个方法不是真正的库或程序,但您可以进行临时转换

                        • 将表格的 HTML 放入名为 something.xls 的 text 文件中
                        • 用电子表格打开
                        • 另存为 CSV。

                        我知道这适用于 Excel,并且我相信我已经使用 OpenOffice 电子表格做到了。

                        但您可能更喜欢 Perl 或 Ruby 脚本...

                        【讨论】:

                        • LibreOffice Calc 会执行此操作,但我发现以这种方式读取大(几 MB)html 表格文件非常慢。因此,对于小文件或一次性使用,这是最简单的方法,但如果您有大量文件要转换脚本,那就太好了。
                        • pavium,你救了我的命!
                        • 在 LibreOffice Calc 4.4.3.2 中为我工作
                        • 这不适用于包含在一个单元格内生成多行的单元格的表格\
                        • 相关:在您的网络浏览器中选择表格数据并复制。打开只处理纯文本的编辑器,例如 Notepad.exe 或 BBEdit.app 和 Paste。将文档另存为 .csv。它可能是制表符分隔的,因此您可以在电子表格应用程序中打开它并导出为格式正确的 CSV。
                        猜你喜欢
                        • 2011-02-07
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2010-09-17
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-11-03
                        相关资源
                        最近更新 更多