TITLE:JavaScriptでHtmlのtable要素をCSVに変換する

Posted by at 2012-07-25

JavaScriptでHtmlのtable要素をCSVに変換する

概要

Html の table 要素を CSV(character-separated values) に変換する Bookmarklet です。colspan、rowspan で結合するセルがある場合は、同文字列を挿入しています。

  • 区切り文字はタブ(TSV)
  • ブックマークレットなどに登録して表のあるページで実行
  • table要素をダブルクリックすると、tableがtextareaに置換されて、内容がCSVになりコピー可能
    • 子要素にtableが存在する場合は、ダブルクリックに反応しない
  • textareaをダブルクリックすると、元のtableに戻る
  • <thead><tfoot>は気にしていない(例えば、tbodyの前にあるtfootが、そのまま先の行としてCSVになる)
  • <capthion>は無視
  • <colgroup>のspan属性には未対応

ソースコード

// ==UserScript==
// @name        <table> to CSV
// @namespace   http://terai.xrea.jp/
// @include     http://*
// @description Html table -> CSV(textarea)
// @version     1.0.3
// ==/UserScript==
(function() {
  function table2csv(table) {
    var tr = table.getElementsByTagName("tr"), i, j, xoff, text, cells, td, array = [], lenr = tr.length, lenc;
    for(i=0; i<lenr; i++) {
      //前行のセルのcolspanで、すでにこの行は初期化されている場合がある
      if(array[i] == null) array[i] = [];
      cells = tr.item(i).cells;
      lenc = cells.length;
      for (j=0; j<lenc; j++) {
        td = cells.item(j);
        //タグの削除、タブをスペースに置換、両側trim、ダブルクオートの二重化
        text = td.innerHTML.replace(/<.*?>/mg, "").replace(/\t/g," ").replace(/(^\s+)|(\s+$)/g, "").replace(/\"/, '""');
        //前の行のrowspanですでにこのセルが使用されている場合はxoffだけ移動
        xoff = 0;
        while(array[i][j+xoff] != null) {
          xoff++;
        }
        array[i][j+xoff] = text;
        for(k=1; k<td.colSpan; k++) {
          array[i][j+xoff+k] = text;
        }
        for(l=1; l<td.rowSpan; l++) {
          if(array[i+l] == null) array[i+l] = [];
          for(k=0; k<td.colSpan; k++) {
            array[i+l][j+xoff+k] = text; //同文字列 "〃";
          }
        }
      }
    }
    return convertArray2CSV(array);
  }
  function convertArray2CSV(array) {
      var lenr = array.length,
          lenc = array[0].length,
          csv  = "", csvRow, k, l;
      for(k=0; k<lenr; k++) {
          csvRow = "";
          for(l=0; l<lenc; l++) {
              //内容をダブルクオートで囲んでタブ区切りで追加
              csvRow += "\t\""+array[k][l]+"\"";
          }
          if(csvRow != "") {
              csvRow = csvRow.substring(1,csvRow.length);
          }
          csv += csvRow+"\n";
      }
      return csv;
  }
  var listener = function(e) {
    var tx = document.createElement("textarea");
    tx.value = table2csv(this);
    //textareaのサイズは適当
    tx.style.width  = '80%';
    tx.style.height = "240px";
    tx.originalTable  = this;
    this.tx = tx;
    this.parentNode.replaceChild(this.tx, this);
    tx.addEventListener("dblclick", function(e) {
      this.parentNode.replaceChild(this.originalTable, this);
    }, false);
  },
  table = document.getElementsByTagName("table"), i = 0, len = table.length;
  for(; i<len; i++) {
    //tableがネストしている(子tableが存在する)場合は、クリックしても無視する
    if(table[i].getElementsByTagName("table").length>0) continue;
    table[i].addEventListener("dblclick", listener, false);
  }
}());

Bookmarklet

  • 名前(任意)
    • table2csv
  • アドレス(YUI Compressor で圧縮)
    (function(){function f(t){var q=t.getElementsByTagName("tr"),n,m,s,r,u,h,p=[],o=q.length,g;for(n=0;n<o;n++){if(p[n]==null){p[n]=[]}u=q.item(n).cells;g=u.length;for(m=0;m<g;m++){h=u.item(m);r=h.innerHTML.replace(/<.*?>/mg,"").replace(/\t/g," ").replace(/(^\s+)|(\s+$)/g,"").replace(/\"/,'""');s=0;while(p[n][m+s]!=null){s++}p[n][m+s]=r;for(k=1;k<h.colSpan;k++){p[n][m+s+k]=r}for(l=1;l<h.rowSpan;l++){if(p[n+l]==null){p[n+l]=[]}for(k=0;k<h.colSpan;k++){p[n+l][m+s+k]=r}}}}return e(p)}function e(o){var m=o.length,n=o[0].length,h="",j,i,g;for(i=0;i<m;i++){j="";for(g=0;g<n;g++){j+='\t"'+o[i][g]+'"'}if(j!=""){j=j.substring(1,j.length)}h+=j+"\n"}return h}var d=function(h){var g=document.createElement("textarea");g.value=f(this);g.style.width="80%";g.style.height="240px";g.originalTable=this;this.tx=g;this.parentNode.replaceChild(this.tx,this);g.addEventListener("dblclick",function(i){this.parentNode.replaceChild(this.originalTable,this)},false)},c=document.getElementsByTagName("table"),b=0,a=c.length;for(;b<a;b++){if(c[b].getElementsByTagName("table").length>0){continue}c[b].addEventListener("dblclick",d,false)}}());
  • アドレス(テスト用にtable2csv.jsを読み込む)
    javascript:(function(){var s=document.createElement("script");s.charset="UTF-8";s.src="http://terai.xrea.jp/data/javascript/table2csv.js?"+(new Date()).getTime();document.body.appendChild(s)}());
  • アドレス(テスト用にtable2csv.min.jsを読み込む)
    javascript:(function(){var s=document.createElement("script");s.charset="UTF-8";s.src="http://terai.xrea.jp/data/javascript/table2csv.min.js?"+(new Date()).getTime();document.body.appendChild(s)}());

テスト用table

  • rowspan, colspan
    ABCD
    123
    456
    7
  • footer
    ファイル数サイズ総行数コメント行実行数空行数コメント率
    footer
    合計1695291916174370770458480412521
    平均3131310314555017444.13%

参考リンク

コメント

  • 表計算ソフトなどの場合は、tableタグをコピペするだけでspanを考慮した表の取込みが出来るので、このスクリプトを使って CSV, TSV に変換して読み込む意味はあまりないかも。ただ、自分の使いたいソフトには、Htmlでのtable取り込み機能がなく、csvを読み込んで表にする機能と、連続する同文字列セルの結合が可能なので作成してみました。取り込み作業は終わったのでもう必要ないけど、せっかくなので公開しておきます。 -- aterai
  • innerTextではなく innerHTML.replace(/<.*?>/mg, "") を使用して、FireFoxでも動作するように修正。 -- aterai
  • @version 1.0.3: セル中改行などに対応。 -- aterai