こちらの質問の続きです
http://q.hatena.ne.jp/1378940673
ソース1の場合はうまく動くのですがソース2の場合はうまく動きません
どこがダメなのでしょうか?
ソース1
<html><body> <?php $s = ''; $b = strip_tags( @file_get_contents( __FILE__ ), '<tr><td>'); if ( preg_match_all("#<tr[^<]+?(.+?)</tr#is", $b, $trs , PREG_SET_ORDER) ) { $a = array(); foreach( $trs as $tr ) { if ( preg_match_all("#<td[^<]+?(.+?)</td#is", $tr[1], $tds , PREG_SET_ORDER) ) { $a[] = '<option value="' . $tds[0][1] . '">' . $tds[0][1] . '回</option>'; } } $a = array_unique( $a, SORT_REGULAR ); $a[] = preg_replace( '#([^>]+?)>(.+)#', '$1 selected="selected">$2', array_pop( $a ) ); $s = '<select name="kai1">' . "\n"; $s .= join( $a, "\n" ) . "\n"; $s .= '</select>' . "\n"; } echo ( $s ); ?> <TABLE> <tr> <td>1</td> </tr> <tr> <td>1</td> </tr> <tr> <td>2</td> </tr> <tr> <td>3</td> </tr> </TABLE></body></html>
<html><body> <?php $s = ''; $b = strip_tags( @file_get_contents( __FILE__ ), '<tr><td>'); if ( preg_match_all("#<tr[^<]+?(.+?)</tr#is", $b, $trs , PREG_SET_ORDER) ) { $a = array(); foreach( $trs as $tr ) { if ( preg_match_all("#<td[^<]+?(.+?)</td#is", $tr[1], $tds , PREG_SET_ORDER) ) { $a[] = '<option value="' . $tds[0][1] . '">' . $tds[0][1] . '回</option>'; } } $a = array_unique( $a, SORT_REGULAR ); $a[] = preg_replace( '#([^>]+?)>(.+)#', '$1 selected="selected">$2', array_pop( $a ) ); $s = '<select name="kai1">' . "\n"; $s .= join( $a, "\n" ) . "\n"; $s .= '</select>' . "\n"; } echo ( $s ); ?> <TABLE> <tr> <td><input type="checkbox" class="hikakubox"/></td> <td>1</td> </tr> <tr> <td><input type="checkbox" class="hikakubox"/></td> <td>1</td> </tr> <tr> <td><input type="checkbox" class="hikakubox"/></td> <td>2</td> </tr> <tr> <td><input type="checkbox" class="hikakubox"/></td> <td>3</td> </tr> </TABLE></body></html>
2ヶ所にある preg_match_all の正規表現改変(単純化しました)および、
2列目からの抜きだしへの改変($tds[0][1] →$tds[1][1])を行いました
<?php $s = ''; $b = strip_tags( @file_get_contents( __FILE__ ), '<tr><td>'); if ( preg_match_all("#<tr.*?>(.*?)</tr.*?>#is", $b, $trs , PREG_SET_ORDER) ) { $a = array(); foreach( $trs as $tr ) { if ( preg_match_all("#<td.*?>(.*?)</td.*?>#is", $tr[1], $tds , PREG_SET_ORDER) ) { $a[] = '<option value="' . $tds[1][1] . '">' . $tds[1][1] . '回</option>'; } } $a = array_unique( $a, SORT_REGULAR ); $a[] = preg_replace( '#([^>]+?)>(.+)#', '$1 selected="selected">$2', array_pop( $a ) ); $s = '<select name="kai1">' . "\n"; $s .= join( $a, "\n" ) . "\n"; $s .= '</select>' . "\n"; } echo ( $s ); ?>
正規表現の部分をいじりました。
<?php ... $a = array(); foreach( $trs as $tr ) { if ( preg_match_all("#<td[^>]*>(\d+)</td>#is", $tr[1], $tds , PREG_SET_ORDER) ) {
ideone.com で確認したのがこちら。
http://ideone.com/UPi8ri
スクレイピングするなら正規表現を使うよりもsimple_html_domのようなDOMを使った方が簡単なように思います。
今回はインストール無しで使えるDOMDocumentを使って実装してみます。
function select_html($name, $opt, $select=NULL) { $o = array('<select name="'.$name.'">'); foreach($opt as $k=>$v) { $s = ($v == $select) ? ' selected' : ''; $o[] = ' <option value="'.$k.'"'.$s.'>'.$v.'</option>'; } $o[] = '</select>'; return join($o, "\n"); }
selectのhtml文を生成します。$optは配列で連想配列を想定していますが単に添字でもOKです。
値が$selectと同じだとselectedを付加します。キーと値でoptionを生成しているので主旨とずれていますが
' <option value="'.$v.'"'.$s.'>'.$v.'</option>';
とすることで希望するコードを生成します。
function file_get_dom($file) { $doc = new DOMDocument(); $doc->loadHTMLFile($file); return $doc; }
$file からDOMを生成して返します。htmlファイルの中に漢字などがあると文字化けを起すようなのでエンコードを指定する必要があるようなのですがnew DOMDocument('1.0','UTF-8')や$doc->encoding = 'UTF-8'
のようにしても解決しませんでした。htmlファイルの中にmetaで
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
とすることで解決できました。
function dom_get_table($doc, $n=0) { $table = array(); $tbl = $doc->getElementsByTagName('table'); foreach ($tbl->item($n)->getElementsByTagName('tr') as $r=>$tr) { // foreach ($tr->childNodes as $c=>$td) // if ($td->nodeName == 'td') $table["$c"]["$r"] = $td->nodeValue; foreach ($tr->childNodes as $td) if ($td->nodeName == 'td') $table["$r"][] = $td->nodeValue; } return $table; }
$docのtableから二次元の配列にして返します。htmlの中にはtableが複数ある場合があると思いますので$nで何番目かを指定します。
本来はidなどで指定できたほうが無駄が無いと思いますが。
配列は行要素の中に列の配列が入っています。そのため後でtransposeで行と列を入れ替えています。
foreach ($tr->childNodes as $c=>$td) if ($td->nodeName == 'td') $table["$c"]["$r"] = $td->nodeValue;
なら列要素の中に行の配列を入れることができるので後のtransposeをする必要がなくなります。
function transpose($org) { $trans = array(); foreach ($org as $n=>$r) foreach ($r as $m=>$c) $trans["$m"]["$n"] = $c; return $trans; }
二次元配列の行と列を入れ替えます。
$opt = transpose(dom_get_table(file_get_dom("hoge.html"))); $opt = array_unique($opt[1]); echo select_html('問題番号', array_values($opt), end($opt)); //$opt = array_unique(array_column(dom_get_table(file_get_dom("hoge.html")),1)); //echo select_html('問題番号', array_values($opt), end($opt));
hoge.htmlをfile_get_domでDOMに格納します。
dom_get_tableでtableから二次元配列を生成します。
transposeで行と列を入れ替えます。
ソース1の場合は0列目なのでarray_uniqueするのは$opt[0]となります。
ソース2の場合は1列目なのでarray_uniqueするのは$opt[1]となります。
array_uniqueで重複があった場合、添字が飛ぶのでarray_valuesで付け替えています。
がselect_htmlで$kを使わない、或いは添字が飛んでもいいのであればする必要はありません。
PHP5.5からarray_columnが使えるようになり、二次元の配列から列を取り出すことができるのでtransposeする必要がありません。
それ以前でもhttp://www.php.net/manual/ja/function.array-column.phpからソースをコピペしても使えます。
$opt = array_unique(array_column(dom_get_table(file_get_dom("hoge.html")),1)); echo select_html('問題番号', array_values($opt), end($opt));
ソース1の場合は0列目なのでarray_column(dom_get_table(file_get_dom("hoge.html")),0)
ソース2の場合は1列目なのでarray_column(dom_get_table(file_get_dom("hoge.html")),1)
endは$optの配列ポインタを最後の要素に設定してしまうので以降に$optを参照する場合はresetする必要があります。
全体像をお教えして作っていただくのは申し訳ないと思い
2013/09/14 12:00:41勉強のために自分で少しづつ作っていって
つまずいた時はこちらで質問させていただいております
回答を強制しているわけではないので、気にしなくて良いと思います。
2013/09/14 12:30:24小出しにすると、場当たり的な回答しかできなくなってしまいますので、
結果遠回りになり、手間が増えているだけの気がしているのです…。
ご質問の開始点から、すでに10日以上が過ぎ、質問数も20になろうかと思いますが、
このままでは倍の期間、倍の質問数になっても終わらないような気がしてなりません。
もちろん、それがご希望なら無理にとは申せませんし、
こちらとしましても、きっちりした回答にまとめるのに数日かかるかもしれませんので、
これまた無理にとは申せませんが、もう少しまとまった形でご質問していただくほうが、
たぶん、すっきりするものと思います。
週明けくらいまでは暇にしてる予定なので、この返信欄でお答えいただいても結構ですし、
お時間かかるようでしたら、受付中の質問の解決を優先していただいても結構です。
いずれも、あわてて質問を終了することなく、ゆっくりとお考えください。