2

PHP スクリプト (cURL を使用) を使用して、次のことを確認しています。

  • データベース内のリンクは正しい (つまり、HTTP ステータス 200 を返す)
  • リンクは実際にはリダイレクトされ、適切な/類似のページにリダイレクトされます (ページのコンテンツを使用)

この結果はログ ファイルに保存され、添付ファイルとしてメールで送信されます。

これはすべて問題なく機能していますが、非常に遅く、半分の時間でタイムアウトし、早期に中止されます。注目すべきは、チェックするリンクが約 16,000 あることです。

これをより速く実行する方法と、何が間違っているのか疑問に思っていましたか?

以下のコード:

function echoappend ($file,$tobewritten) {

        fwrite($file,$tobewritten);
        echo $tobewritten;
}

error_reporting(E_ALL);
ini_set('display_errors', '1');


$filename=date('YmdHis') . "linkcheck.htm";
echo $filename;
$file = fopen($filename,"w+");

try {
        $conn = new PDO('mysql:host=localhost;dbname=databasename',$un,$pw);
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        echo '<b>connected to db</b><br /><br />';

        $sitearray = array("medical.posterous","ebm.posterous","behavenet","guidance.nice","www.rch","emedicine","www.chw","www.rxlist","www.cks.nhs.uk");

        foreach ($sitearray as $key => $value) {    
            $site=$value;

            echoappend ($file, "<h1>" . $site . "</h1>");

            $q="SELECT * FROM link WHERE url LIKE :site";
            $stmt = $conn->prepare($q);
            $stmt->execute(array(':site' => 'http://' . $site . '%'));
            $result = $stmt->fetchAll();

            $totallinks = 0;
            $workinglinks = 0;

            foreach($result as $row)
            {

                $ch = curl_init();
                $originalurl = $row['url'];

                curl_setopt($ch, CURLOPT_URL, $originalurl);
                curl_setopt($ch, CURLOPT_HEADER, 1);
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                curl_setopt($ch, CURLOPT_NOBODY, true);
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);


                $output = curl_exec($ch);
                if ($output === FALSE) {
                    echo "cURL Error: " . curl_error($ch);
                }

                $urlinfo = curl_getinfo($ch);

                if ($urlinfo['http_code'] == 200)
                {
                    echoappend($file, $row['name'] . ": <b>working!</b><br />");
                    $workinglinks++;
                }
                else if ($urlinfo['http_code'] == 301 || 302)
                {
                    $redirectch = curl_init();                  
                    curl_setopt($redirectch, CURLOPT_URL, $originalurl);
                    curl_setopt($redirectch, CURLOPT_HEADER, 1);
                    curl_setopt($redirectch, CURLOPT_RETURNTRANSFER, 1);
                    curl_setopt($redirectch, CURLOPT_NOBODY, false);
                    curl_setopt($redirectch, CURLOPT_FOLLOWLOCATION, true);

                    $redirectoutput = curl_exec($redirectch);

                    $doc = new DOMDocument();
                    @$doc->loadHTML($redirectoutput);
                    $nodes = $doc->getElementsByTagName('title');

                    $title = $nodes->item(0)->nodeValue;

                    echoappend ($file, $row['name'] . ": <b>redirect ... </b>" . $title . " ... ");

                    if (strpos(strtolower($title),strtolower($row['name']))===false) {
                        echoappend ($file, "FAIL<br />");
                    }
                    else {
                        $header = curl_getinfo($redirectch);
                        echoappend ($file, $header['url']);
                        echoappend ($file, "SUCCESS<br />");
                    }

                    curl_close($redirectch);
                }
                else
                {
                    echoappend ($file, $row['name'] . ": <b>FAIL code</b>" . $urlinfo['http_code'] . "<br />");
                }

                curl_close($ch);

                $totallinks++;
            }
            echoappend ($file, '<br />');

            echoappend ($file, $site . ": " . $workinglinks . "/" . $totallinks . " links working. <br /><br />");


        }

        $conn = null;
        echo '<br /><b>connection closed</b><br /><br />';

    } catch(PDOException $e) {
            echo 'ERROR: ' . $e->getMessage();
    }
4

2 に答える 2

2

簡単な答えは、curl_multi_*メソッドを使用してリクエストを並列化することです。

遅い理由は、Webリクエストが比較的遅いためです。時々非常に遅い。curl_multi_ *関数を使用すると、複数のリクエストを同時に実行できます。

注意すべきことの1つは、一度に実行するリクエストの数を制限することです。つまり、一度に16,000のリクエストを実行しないでください。たぶん16から始めて、それがどうなるか見てみましょう。

次の例は、始めるのに役立ちます。

<?php

//
// Fetch a bunch of URLs in parallel. Returns an array of results indexed
// by URL.
//
function fetch_urls($urls, $curl_options = array()) {
  $curl_multi = curl_multi_init();
  $handles = array();

  $options = $curl_options + array(
    CURLOPT_HEADER         => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_NOBODY         => true,
    CURLOPT_FOLLOWLOCATION => true);

  foreach($urls as $url) {
    $handles[$url] = curl_init($url);
    curl_setopt_array($handles[$url], $options);
    curl_multi_add_handle($curl_multi, $handles[$url]);
  }

  $active = null;
  do {
    $status = curl_multi_exec($curl_multi, $active);
  } while ($status == CURLM_CALL_MULTI_PERFORM);

  while ($active && ($status == CURLM_OK)) {
    if (curl_multi_select($curl_multi) != -1) {
      do {
        $status = curl_multi_exec($curl_multi, $active);
      } while ($status == CURLM_CALL_MULTI_PERFORM);
    }
  }

  if ($status != CURLM_OK) {
    trigger_error("Curl multi read error $status\n", E_USER_WARNING);
  }

  $results = array();
  foreach($handles as $url => $handle) {
    $results[$url] = curl_getinfo($handle);
    curl_multi_remove_handle($curl_multi, $handle);
    curl_close($handle);    
  }
  curl_multi_close($curl_multi);

  return $results;
}

//
// The urls to test
//
$urls = array("http://google.com", "http://yahoo.com", "http://google.com/probably-bogus", "http://www.google.com.au");

//
// The number of URLs to test simultaneously
//
$request_limit = 2;

//
// Test URLs in batches
//
$redirected_urls = array();
for ($i = 0 ; $i < count($urls) ; $i += $request_limit) {
  $results = fetch_urls(array_slice($urls, $i, $request_limit));
  foreach($results as $url => $result) {
    if ($result['http_code'] == 200) {
      $status = "Worked!";
    } else {
      $status = "FAILED with {$result['http_code']}";
    }
    if ($result["redirect_count"] > 0) {
      array_push($redirected_urls, $url);
      echo "{$url}: ${status}\n";
    } else {
      echo "{$url}: redirected to {$result['url']} and {$status}\n";
    }
  }
}

//
// Handle redirected URLs
//
echo "Processing redirected URLs...\n";
for ($i = 0 ; $i < count($redirected_urls) ; $i += $request_limit) {
  $results = fetch_urls(array_slice($redirected_urls, $i, $request_limit), array(CURLOPT_FOLLOWLOCATION => false));
  foreach($results as $url => $result) {
    if ($result['http_code'] == 301) {
      echo "{$url} permanently redirected to {$result['url']}\n";
    } else if ($result['http_code'] == 302) {
      echo "{$url} termporarily redirected to {$result['url']}\n";
    } else {
      echo "{$url}: FAILED with {$result['http_code']}\n";
    }
  }
}

上記のコードは、URLのリストをバッチで処理します。2つのパスで機​​能します。最初のパスでは、各リクエストはリダイレクトに従うように構成され、各URLが最終的にリクエストの成功につながるのか失敗につながるのかを報告するだけです。

2番目のパスは、最初のパスで検出されたリダイレクトされたURLを処理し、リダイレクトが永続的なリダイレクト(新しいURLでデータベースを更新できることを意味します)であるか、一時的な(データベースを更新しないことを意味する)かを報告します。

ノート:

元のコードには、次の行がありますが、期待どおりに機能しません。

else if ($urlinfo['http_code'] == 301 || 302)

式は常にTRUEを返します。正しい式は次のとおりです。

else if ($urlinfo['http_code'] == 301 || $urlinfo['http_code'] == 302)
于 2012-11-23T05:49:14.163 に答える
0

また、置く

set_time_limit(0);

スクリプトの先頭で、30 秒に達したときに中断するのを停止します。

于 2012-11-23T05:53:52.220 に答える