php5 DomDocumentでHTMLを読ませてfirefoxで行っているように

tableにtbody等を補完するプログラムを作っています
が激しく遅いので高速に動くように調整をしたいです。

宜しくお願いします。

注意)
基本はプログラム修正で高速化したいですが
他の方法(PHPの別のライブラリ、PEAR、PECL)を駆使してもいいです。
レンタルサーバなのでFirefoxのHTMLパーサ使えば、というのはなしで

以下現状)

//tdの親はtr
$cnt += setHtmlFill($x,'//td[name(..)!="tr"]','tr');
//thの親はtr
$cnt += setHtmlFill($x,'//th[name(..)!="tr"]','tr');
//trの親はtbody or thead
$expression = '//tr[name(..)!="tbody" and name(..)!="thead"]';
$cnt += setHtmlFill($x,$expression,'tbody');
//tbodyの親はtable
$cnt += setHtmlFill($x,'//tbody[name(..)!="table"]','table');

private function setHtmlFill($x,$path,$tag) {
$cnt = 0;

while ($es = $x->query($path)) {
if (!$es->length) {
break;
}
$cnt++;

$e = $es->item(0);
$parent = $e->parentNode;

$add = $x->document->createElement($tag);
$childs = $parent->childNodes;

$del = array();
foreach ($childs as $child) {
$add->appendChild($child->cloneNode(true));
$del[] = $child;
}
foreach ($del as $child) {
$child->parentNode->removeChild($child);
}
$parent->appendChild($add);
}

return $cnt;
}

回答の条件
  • 1人2回まで
  • 登録:2008/09/27 12:58:26
  • 終了:2008/10/04 13:00:03

回答(3件)

id:youku554 No.1

youku554回答回数95ベストアンサー獲得回数02008/09/27 22:49:07

id:hisugawa

php5での質問でしたが、mysql関連のリンクですね

mysqlのSQL文の補完とphp5のDomDocumentの話で関連があるか判別できませんでした

php5+domでのtbody補完、又はphp5でのtbody補完等で関連があるなら

その点を教えてください、無いなら解答として理解できません。

2008/09/28 01:27:55
id:nake No.2

nake回答回数87ベストアンサー獲得回数02008/09/28 19:07:23

ポイント27pt

Keith Devens - PHP XML Libraryを使えばたぶんできると思います。

http://techblog.ecstudio.jp/tech-tips/xml_unserializer.html

http://keithdevens.com/software/phpxml

id:hisugawa

xml array を参考にしてと言うことですね

http://jp2.php.net/manual/ja/ref.xml.php

この辺を参考にすれば高速にtbody補完できるかも

確認してみます

2008/09/29 01:16:09
id:tobeoscontinue No.3

tobeoscontinue回答回数212ベストアンサー獲得回数522008/09/29 16:17:47

ポイント26pt

>激しく遅いので高速に動くように調整をしたいです。

小さなデータでは確認できなかったのでコードから考えられるものを列挙してみました。

$x->query($path)を減らす。

$cnt += setHtmlFill($x,'//td[name(..)!="tr"]','tr');

$cnt += setHtmlFill($x,'//th[name(..)!="tr"]','tr');

$cnt += setHtmlFill($x,'//tbody[name(..)!="table"]','table');

はあまりマッチしないと思われるので外してみる。

$x->query($path)は重い処理と思われるので。


$x->query($path)では全てを列挙しているのに、$es->item(0)と最初のものしか使っていない。

そのため数回の$x->query($path)を実行している。

cloneNodeではなく挿げ替え

$child->cloneNode(true)は重そうと思われるので挿げ替えるようにする。

function setHtmlFill($x,$path,$tag) {
  $cnt = 0;

  $es = $x->query($path);
  $add = NULL;
  foreach ($es as $e) {

    $parent = $e->parentNode;
    if ($add == NULL || !$add->parentNode->isSameNode($parent)) {
      $add = $x->document->createElement($tag);
      $parent->appendChild($add);
      $cnt++;
    }
    $add->appendChild($parent->removeChild($e));
  }
  return $cnt;
}

$esに格納されている子は連続して同一の親であると仮定しています。

そうでない場合は$parentに$addがあるかどうかを調べる必要があるのでもっと長くなります。


tableにtbodyを補完するということであれば"table"タグを列挙して子が"tr"であれば"tbody"へ挿げ替えるとした方がオーバーヘッドが少ないように思います。

// $parentタグを列挙して子が$childであれば$middleへ挿げ替える
function insertTag($doc, $parent, $middle, $child)
{
  $list = $doc->getElementsByTagName($parent);
  foreach ($list as $table)
    if ($table->hasChildNodes()) {
      $childs = $table->childNodes;
      $add = $doc->createElement($middle);
      $i = 0;
   // $tableをremoveChild()していると$childsの状態はダイナミックに変更されるのでforは使えない
      while ($i < $childs->length)
        if ($childs->item($i)->nodeName == $child)
          $add->appendChild($table->removeChild($childs->item($i)));
      else $i++;
      if ($add->hasChildNodes())
        $table->appendChild($add);
    }
}

insertTag($doc, 'table', 'tbody', 'tr');

echo $doc->saveHTML();

大きなデータが無いので速度については未確認です。

また親(table)の直下が子(tr)など実際のデータとはそぐわない場合があるかもしれません。

id:hisugawa

ありがとうございます

table-tbody以外は多すぎで時間がかかるので

確認対象をtable-tbodyに絞りました

時間を計っていませんが大幅に短縮できました。

$cnt += $this->insertTag($d,'table',array('tbody','thead','tfoot'));

function insertTag($d, $parent, $middle) {

// if (!is_array($middle)) {

// $wk = array();

// $wk[] = $middle;

// $middle = $wk;

// }

$es = $d->getElementsByTagName($parent);

$mod = 0;

foreach ($es as $e) {

if ($e->hasChildNodes()) {

$childs = $e->childNodes;

$add = $d->createElement($middle[0]);

$i = 0;

//$eをremoveChild()していると

//$childsの状態はダイナミックに変更されるのでforは使えない

while ($i < $childs->length) {

if (!in_array($childs->item($i)->nodeName,$middle)) {

$add->appendChild($e->removeChild($childs->item($i)));

} else {

$i++;

}

}

if ($add->hasChildNodes()) {

$e->appendChild($add);

$mod++;

}

}

}

return $mod;

}

追記:

2番目の方の情報を元に

xmlreaderまわりを確認してみましたが

高速にはなりませんでした、saxが高速でもdomを全部phpスクリプトで作るのは

それ以上に時間がかかってしまいました。

2008/10/02 11:53:47

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません