1

外部 Web ページの特定の div のコンテンツを抽出したいのですが、div は次のようになります。

<dt>Win rate</dt><dd><div>50%</div></dd>

私の目標は「50%」です。私は実際にこのphpコードを使用してコンテンツを抽出しています:

function getvalue($parameter,$content){
    preg_match($parameter, $content, $match);
    return $match[1];
    };
$parameter = '#<dt>Score</dt><dd><div>(.*)</div></dd>#';
$content = file_get_contents('https://somewebpage.com');

すべてが正常に機能します。問題は、特に異なる $content で数回使用する必要がある場合、このメソッドに時間がかかりすぎることです。

同じ機能を達成するためのより良い(より高速、簡単など)方法があるかどうか知りたいですか?どうも!

4

3 に答える 3

3

DOMDocument::loadHTMLを使用して、指定されたノードに移動できます。

$content = file_get_contents('https://somewebpage.com');
$doc = new DOMDocument();
$doc->loadHTML($content);

目的のノードに到達するには、メソッドDOMDocument::getElementsByTagNameを使用できます。

$dds = $doc->getElementsByTagName('dd');
foreach($dds as $dd) {
  // process each <dd> element here, extract inner div and its inner html...
}

編集: DomDocument が遅いことについて @pebbl が指摘していることがわかります。確かにそうですが、preg_match を使用して HTML を解析すると問題が発生します。その場合は、イベント駆動型の SAX XML パーサーも検討することをお勧めします。ツリーを構築しないため、はるかに軽量で高速で、メモリの消費量が少なくなります。このようなパーサーについては、 XML_HTMLSaxを参照してください。

于 2012-09-16T18:04:07.133 に答える
2

基本的に、コードの速度を向上させるためにできることは主に 3 つあります。

外部ページの負荷を別の時間にオフロードします(つまり、cron を使用します) 。

Linuxベースのサーバーでは、何を提案すればよいかはわかりますが、Windowsを使用していると、同等のものが何であるかわかりませんが、Linux用のCronを使用すると、特定のスケジュール時間オフセットでスクリプトを起動できます-バックグラウンドで-そうではありませんブラウザを使用します。基本的には、 (データを更新する必要がある頻度に応じて)特定の時間オフセットで Web サイトのページを取得し、それらの Web ページをローカル システム上のファイルに書き込むことだけを目的とするスクリプトを作成することをお勧めします。

$listOfSites = array(
  'http://www.something.com/page.htm',
  'http://www.something-else.co.uk/index.php',
);

$dirToContainSites = getcwd() . '/sites';

foreach ( $listOfSites as $site ) {
  $content = file_get_contents( $site );

  /// i've just simply converted the URL into a filename here, there are
  /// better ways of handling this, but this at least keeps things simple.
  /// the following just converts any non letter or non number into an
  /// underscore... so, http___www_something_com_page_htm
  $file_name = preg_replace('/[^a-z0-9]/i','_', $site);

  file_put_contents( $dirToContainSites . '/' . $file_name, $content );
}

このスクリプトを作成したら、必要なだけ定期的に実行するようにサーバーを設定する必要があります。次に、統計を表示するフロントエンド スクリプトを変更して、ローカル ファイルから読み取ることができます。これにより、速度が大幅に向上します。

ディレクトリからファイルを読み取る方法については、次を参照してください。

http://uk.php.net/manual/en/function.dir.php

または、より簡単な方法(ただし、問題が発生する可能性があります)は、サイトの配列を再ステップし、上記の preg_replace を使用して URL をファイル名に変換し、フォルダー内のファイルの存在を確認することです。

統計の計算結果をキャッシュする

これは、かなり頻繁にアクセスしたい統計ページである可能性が非常に高いです(公開ページほど頻繁ではありませんが、それでも)。cron ベースのスクリプトが実行されるよりも頻繁に同じページにアクセスする場合、すべての計算を再度実行する理由はありません。基本的に、出力をキャッシュするために必要なことは、次のようなことを行うことだけです。

$cachedVersion = getcwd() . '/cached/stats.html';

/// check to see if there is a cached version of this page
if ( file_exists($cachedVersion) ) {
  /// if so, load it and echo it to the browser
  echo file_get_contents($cachedVersion);
}
else {
  /// start output buffering so we can catch what we send to the browser
  ob_start();

  /// DO YOUR STATS CALCULATION HERE AND ECHO IT TO THE BROWSER LIKE NORMAL

  /// end output buffering and grab the contents so we now have a string
  /// of the page we've just generated
  $content = ob_get_contents(); ob_end_clean();

  /// write the content to the cached file for next time
  file_put_contents($cachedVersion, $content);

  echo $content;
}

キャッシュを開始したら、いつキャッシュを削除またはクリアする必要があるかを認識する必要があります。そうしないと、統計出力が変化しません。この状況に関しては、キャッシュをクリアするのに最適なタイミングは、外部 Web ページを再度取得する時点です。したがって、この行を「cron」スクリプトの最後に追加する必要があります。

$cachedVersion = getcwd() . '/cached/stats.html';

unlink( $cachedVersion ); /// will delete the file

キャッシュ システムには他にも速度を向上させることができます(外部 Web ページの変更時間を記録し、更新されたときにのみ読み込むこともできます)が、説明を簡単にするように努めました。

この状況では HTML パーサーを使用しないでください

HTML ファイルをスキャンして 1 つの特定の一意の値を取得する場合、本格的な HTML パーサーや軽量の HTML パーサーを使用する必要はありません。RegExp を誤って使用することは、多くのスタートアップ プログラマーが陥りがちなことの 1 つであり、常に尋ねられる質問です。これにより、より多くの経験豊富なコーダーからの多くの自動ひざまずく反応が、次のロジックに自動的に準拠するようになりました。

if ( $askedAboutUsingRegExpForHTML ) {
  $automatically->orderTheSillyPersonToUse( $HTMLParser );
} else {
  $soundAdvice = $think->about( $theSituation );
  print $soundAdvice;
}

HTMLParsers は、マークアップ内のターゲットがそれほど一意でない場合、または一致するパターンが非常に脆弱なルールに依存しているため、余分なタグまたは文字が発生した瞬間に壊れる場合に使用する必要があります。速度を上げたい場合ではなく、コードの信頼性を高めるために使用する必要があります。すべての要素のツリーを構築しないパーサーでさえ、なんらかの形式の文字列検索または正規表現表記を使用するため、使用しているライブラリ コードが非常に最適化された方法でコンパイルされていない限り、適切にコーディングされたものには勝てません。 strpos/preg_match ロジック。

あなたが解析しようとしている HTML を見たことがないことを考えると、私はかなり外れている可能性がありますが、あなたのスニペットについて見たことから、strpos と preg_match の組み合わせを使用して値を見つけるのは非常に簡単なはずです。明らかに、HTML がより複雑で、ランダムに複数出現する可能性がある<dt>Win rate</dt><dd><div>50%</div></dd>場合は問題が発生しますが、それでも HTMLParser には同じ問題が発生します。

$offset = 0;

/// loop through the occurances of 'Win rate'
while ( ($p = stripos ($html, 'win rate', $offset)) !== FALSE ) {

  /// grab out a snippet of the surrounding HTML to speed up the RegExp
  $snippet = substr($html, $p, $p + 50 ); 

  /// I've extended your RegExp to try and account for 'white space' that could
  /// occur around the elements. The following wont take in to account any random
  /// attributes that may appear, so if you find some pages aren't working - echo
  /// out the $snippet var using something like "echo '<xmp>'.$snippet.'</xmp>';"
  /// and that should show you what is appearing that is breaking the RegExp.

  if ( preg_match('#^win\s+rate\s*</dt>\s*<dd>\s*<div>\s*([0-9]+%)\s*<#i', $snippet, $regs) ) {
    /// once you are here your % value will be in $regs[1];
    break; /// exit the while loop as we have found our 'Win rate'
  }

  /// reset our offset for the next loop
  $offset = $p;
}

知っておくべき落とし穴

上記のコメントで述べたように、PHP を初めて使用する場合、上記はかなり複雑に思えるかもしれません。あなたがやろうとしていることは、特に最適かつ高速に実行したい場合、非常に複雑です。ただし、私が提供したコードをすべて実行し、よくわからない/聞いたことのないビットを調査すると(php.net はあなたの友人です)、より良い方法を理解できるはずです。あなたがしていることを達成します。

ただし、先を推測すると、上記で直面する可能性のある問題のいくつかを次に示します。

  • ファイル権限エラー- ローカル オペレーティング システムとの間でファイルを読み書きできるようにするには、適切な権限が必要です。特定のディレクトリにファイルを書き込めない場合は、使用しているホストで書き込みが許可されていない可能性があります。このような場合は、フォルダへの書き込み権限を取得する方法について問い合わせることができます。それができない場合は、代わりにデータベースを使用するように上記のコードを簡単に変更できます。

  • コンテンツが表示されません。出力バッファリングを使用すると、echo コマンドと print コマンドがすべてブラウザに送信されず、代わりにメモリに保存されます。スクリプトが終了すると、PHP は保存されているすべてのコンテンツを自動的に出力するはずですが、ob_end_clean() などのコマンドを使用すると、実際には「バッファ」が消去されるため、すべてのコンテンツが消去されます。これは、何かをエコーし​​ていることがわかっているときに混乱を招く状況につながる可能性があります..しかし、それは表示されません。

(ミニ免責事項 :) 上記のすべてを手動で入力したので、PHP エラーがあることに気付くかもしれません。もしそうなら、それらは不可解です。ここに書き戻してください。StackOverflow が役に立ちます)

于 2012-09-17T08:49:52.647 に答える
1

使用しないようにする代わりにpreg_match、ドキュメントのコンテンツのサイズを縮小してみませんか? たとえば、 の前<bodyと後のすべてをダンプできます</body>。その後、preg_match検索するコンテンツが少なくなります。

また、これらのプロセスのそれぞれを疑似個別のスレッドとして実行して、一度に 1 つずつ発生しないようにすることもできます。

于 2012-09-16T18:10:34.267 に答える