Czasem pisząc w PHP chcemy uzyskać PageRank danej strony. Trudno znaleźć dobre rozwiązanie, które działa. Zaprezentuję Ci Drogi Czytelniku mój sposób na ten problem. Skrypt będzie w pewnym stopniu zabezpieczony przed banem od Google, gdyż wyniki będą przechowywane w cache’u.
UWAGA! Na serwerach, które mają wyłączone odwoływanie się funkcji file_get_contents do serwerów zewnętrznych, poniższy skrypt nie będzie działał ( zazwyczaj są to serwery darmowe ).
PageRank – metoda nadawania indeksowanym stronom internetowym określonej wartości liczbowej, oznaczającej jej jakość.
Krótko i zwięźle.
Od siebie dodam, że w całości algorytm PageRank znają jedynie pracownicy Google i nie jest on znany przez żadnego “szaraka”.
W tym artykule postaram się wyjaśnić, jak szybko i prosto można napisać skrypt, który będzie pobierał PageRank strony, której adres będzie przekazywany poprzez zmienną GET strona. Wyniki będą przechowywane w specjalnie do tego przeznaczonym cache’u ( tak jakby okresowej pamięci wyników ) o ważności 24h. Wykorzystamy do tego dwie klasy, które są dostępne pod tym adresem: link. Niestety są one napisane dla PHP4, a dzisiaj wykorzystuje się PHP5. Niby nie jest to większy problem, bo każda wersja PHP jest zgodna wstecz, lecz stosowanie skryptów napisanych dla wersji 4. słynnego interpretera stron, jest niewskazane. Na szczęście kod obu klas nie jest długi, więc mogłem szybko przerobić je dla Was, aby były zgodne z najnowszym PHP. Obydwie znajdują się poniżej.
Plik google_pagerank.class.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | <?php /** * TechBrew.net's "popstats" is available from http://code.google.com/p/popstats/ * * This work is dual-licensed under the GNU Lesser General Public License * and the Creative Commons Attribution-Share Alike 3.0 License. * Copies or derivatives must retain both attribution and licensing statement. * * To view a copy of these licenses, visit: * http://creativecommons.org/licenses/by-sa/3.0/ * http://www.gnu.org/licenses/lgpl.html * * This software is provided AS-IS with no warranty whatsoever. */ require_once('cacher.class.php'); /** * Google PageRank Fetcher * * Requires an API key to access Technorati stats. * (Get one at http://technorati.com/developers/apikey.html) * * @author Mark Woodman, http://techbrew.net * Based on original code presumed to be in the public domain. * * $URL$ * $Rev$ * $Author$ * $Date$ * $Id$ */ class GooglePageRank { private $site; public $pagerank; /** * Constructor. * * @param url The site URL to check for PageRank * @param cacheTime (Optional) Length of time in seconds to cache results. */ public function __construct($site, $cacheTime=86400) { $this->site = $site; if(count($site)==0) die('Google needs a site URL to check pagerank.'); // Calculated variables $info = 'info:' . urldecode($site); $checksum = $this->checksum($this->strord($info)); $url = "http://www.google.com/search?client=navclient-auto&ch=6{$checksum}&features=Rank&q={$info}"; // Pull pagerank through cache $cacher = new Cacher('_google'); $result = $cacher->fetchContents($url, $cacheTime); // Parse results $this->raw = trim($result); preg_match('/Rank_[0-9]:[0-9]:(.*)/', $result, $r); if(!isset($r[1])) { trigger_error("Couldn't get Pagerank for {$site}. Got: [{$result}]", E_USER_NOTICE); error_log("\n" . date('r') . "Couldn't get Pagerank for {$site}", 3, 'error_log'); $this->pagerank = -1; } else { $this->pagerank =(isset($r[1])) ? $r[1] : 'Error'; } } /** * Converts number to int 32 * (Required for pagerank hash) */ private function to_int_32 (&$x) { $z = hexdec(80000000); $y = (int) $x; if($y ==- $z && $x <- $z){ $y = (int) ((-1) * $x); $y = (-1) * $y; } $x = $y; } /** * Fills in zeros on a number * (Required for pagerank hash) */ private function zero_fill ($a, $b) { $z = hexdec(80000000); if ($z & $a) { $a = ($a >> 1); $a &= (~$z); $a |= 0x40000000; $a = ($a >> ($b - 1)); } else { $a = ($a >> $b); } return $a; } /** * Pagerank hash prerequisites */ private function mix($a, $b, $c) { $a -= $b; $a -= $c; $this->to_int_32($a); $a = (int)($a ^ ($this->zero_fill($c,13))); $b -= $c; $b -= $a; $this->to_int_32($b); $b = (int)($b ^ ($a<<8)); $c -= $a; $c -= $b; $this->to_int_32($c); $c = (int)($c ^ ($this->zero_fill($b,13))); $a -= $b; $a -= $c; $this->to_int_32($a); $a = (int)($a ^ ($this->zero_fill($c,12))); $b -= $c; $b -= $a; $this->to_int_32($b); $b = (int)($b ^ ($a<<16)); $c -= $a; $c -= $b; $this->to_int_32($c); $c = (int)($c ^ ($this->zero_fill($b,5))); $a -= $b; $a -= $c; $this->to_int_32($a); $a = (int)($a ^ ($this->zero_fill($c,3))); $b -= $c; $b -= $a; $this->to_int_32($b); $b = (int)($b ^ ($a<<10)); $c -= $a; $c -= $b; $this->to_int_32($c); $c = (int)($c ^ ($this->zero_fill($b,15))); return array($a,$b,$c); } /** * Pagerank checksum hash emulator */ private function checksum ($url, $length = null, $init = 0xE6359A60) { if (is_null($length)) { $length = sizeof($url); } $a = $b = 0x9E3779B9; $c = $init; $k = 0; $len = $length; while($len >= 12) { $a += ($url[$k+0] + ($url[$k+1] << 8) + ($url[$k+2] << 16) + ($url[$k+3] << 24)); $b += ($url[$k+4] + ($url[$k+5] << 8) + ($url[$k+6] << 16) + ($url[$k+7] << 24)); $c += ($url[$k+8] + ($url[$k+9] << 8) + ($url[$k+10] << 16) + ($url[$k+11] << 24)); $mix = $this->mix($a,$b,$c); $a = $mix[0]; $b = $mix[1]; $c = $mix[2]; $k += 12; $len -= 12; } $c += $length; switch($len) { case 11: $c += ($url[$k + 10] << 24); case 10: $c += ($url[$k + 9] << 16); case 9: $c += ($url[$k + 8] << 8); case 8: $b += ($url[$k + 7] << 24); case 7: $b += ($url[$k + 6] << 16); case 6: $b += ($url[$k + 5] << 8); case 5: $b += ($url[$k + 4]); case 4: $a += ($url[$k + 3] << 24); case 3: $a += ($url[$k + 2] << 16); case 2: $a += ($url[$k + 1] << 8); case 1: $a += ($url[$k + 0]); } $mix = $this->mix($a, $b, $c); return $mix[2]; } /** * ASCII conversion of a string */ private function strord($string) { for($i = 0; $i < strlen($string); $i++) { $result[$i] = ord($string{$i}); } return $result; } /** * Number formatting for use with pagerank hash */ private function format_number ($number='', $divchar = ',', $divat = 3) { $decimals = ''; $formatted = ''; if (strstr($number, '.')) { $pieces = explode('.', $number); $number = $pieces[0]; $decimals = '.' . $pieces[1]; } else { $number = (string) $number; } if (strlen($number) <= $divat) return $number; $j = 0; for ($i = strlen($number) - 1; $i >= 0; $i--) { if ($j == $divat) { $formatted = $divchar . $formatted; $j = 0; } $formatted = $number[$i] . $formatted; $j++; } return $formatted . $decimals; } public function __destruct() { unset($this->site); unset($this->pagerank); } } ?> |
Plik cacher.class.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | <?php /** * TechBrew.net's "popstats" is available from http://code.google.com/p/popstats/ * * This work is dual-licensed under the GNU Lesser General Public License * and the Creative Commons Attribution-Share Alike 3.0 License. * Copies or derivatives must retain both attribution and licensing statement. * * To view a copy of these licenses, visit: * http://creativecommons.org/licenses/by-sa/3.0/ * http://www.gnu.org/licenses/lgpl.html * * This software is provided AS-IS with no warranty whatsoever. */ /** * Cacher utility to write the output of a remote URI request * to the local filesystem. * * @author Mark Woodman, http://techbrew.net * @version 15 April 2007 */ class Cacher { private $cachedir = CACHE_DIR; private $suffix; /** * Constructor. Requires CACHE_DIR directive to be set * prior to use. CACHE_DIR will contain cache files * which hold the contents of requested URLs. */ public function __construct($suffix='') { if(!(CACHE_DIR)) { die('CACHE_DIR not configured.'); } $this->suffix = $suffix; } /** * Fetch a URL. If it has already * been cached within the specified cacheTime, * the cached copy is returned. Otherwise a * fresh copy is retrieved and cached. * * If it can't write to the cache for some reason, the original URL * is returned. * * @param $url The URL to retrieve * @param $cacheTime The length of time to cache the requested URL. */ private function fetch($url, $cacheTime=86600) { // Determine cache file name $cacheFile = $this->cachedir . md5($url) . $this->suffix . '.cache' ; $refresh = true; if(@file_exists($cacheFile)) { $refresh = (time() - $cacheTime > @filemtime($cacheFile)) ; } @clearstatcache(); // Cache file if needed if($refresh) { try { $tries = 0; $errors = 0; $contents = false; while($tries<3) { $tries++; if(!$contents) { $contents = @file_get_contents($url); } if(!$contents) { $error = $error .(' GET_FAIL '); } else { $result = @file_put_contents($cacheFile, $contents); if(!$result) { $error = $error .(' PUT_FAIL '); } else { return $cacheFile; } } // TODO: Is this necessary? Is there a better way? usleep(10000); } error_log("\n" . date('r') . " - Failed to cache: {$url}", 3, 'error_log'); error_log("\n" . date('r') . " - Failure reasons: {$error}", 3, 'error_log'); return false; } catch(Exception $e) { error_log("\n" . date('r') . " - {$e}", 3, 'error_log'); error_log("\n" . date('r') . " - Cacher error: {$error}", 3, 'error_log'); return false; } } return $cacheFile; } /** * Fetch a URL and return contents as a string. If it has already * been cached within the specified cacheTime, * the cached copy is returned. Otherwise a * fresh copy is retrieved and cached. * * @param $url The URL to retrieve * @param $cacheTime The length of time to cache the requested URL. */ public function fetchContents($url, $cacheTime=86600) { $file = $this->fetch($url, $cacheTime); if(!$file) return false; return file_get_contents($file); } /** * Clear cache files for a url. */ private function clear($url) { // Determine cache file name $cacheFile = $this->cachedir . md5($url) . $this->suffix . '.cache' ; if(@file_exists($cacheFile)) { @unlink($cacheFile); } } public function __destruct() { unset($this->cachedir); unset($this->suffix); } } ?> |
UWAGA! Klasy te są autorstwa Mark’a Woodman’a i są one publikowane na licencji Creative Commons Attribution-Share Alike 3.0 License oraz GNU Lesser General Public License
Skoro już mamy kod klas, to warto by gdzieś zapisać.
Pierwszą umieszczamy w pliku pod nazwą google_pagerank.class.php, a drugą: cacher.class.php. Obydwa pliki umieszczamy w folderze includes.
Aby przygotować nasz katalog do działania z tymi klasami, musimy jeszcze utworzyć katalog cache ( oczywiście ma on znajdować się w głównym folderze ; musi zawierać prawa do zapisu ). Do tego tworzymy plik index.php i to nam wystarczy, aby napisać coś, co już będzie można nazwać prostym sprawdzaczem PageRank’u.
Przechodzimy do edycji pliku index.php. Pierw musimy do niego załadować klasę GooglePageRank. Pamiętając, że znajduje się ona w folderze includes, w pliku google_pagerank.class.php oraz korzystając z pseudofunkcji require_once, wykonujemy to:
require_once 'includes/google_pagerank.class.php';
O umieszczanie pliku z klasą Cacher nie musimy się martwić, gdyż plik, który przed chwilą połączyliśmy z index.php, sam go pobiera.
Klasa cache’ująca do działania potrzebuje zdefiniowanej stałej o nazwie CACHE_DIR, która będzie zawierała ścieżkę do katalogu, do którego będą zapisywane wyniki zapytań do serwerów Google’a. Robimy to za pomocą funkcji define() :
define('CACHE_DIR', './cache/');
Mamy już wszystko, co potrzebujemy do napisania najprostszego skryptu. Wystarczy utworzyć obiekt klasy GooglePageRank z parametrem adresu strony ( u nas to zmienna GET adres ) oraz wyświetlić zawartość zmiennej pagerank, nowoutworzonej klasy:
1 2 | $pagerank = new GooglePageRank($_GET['adres']); echo $pagerank->pagerank; |
I tutaj mała podpowiedź: gdy chcemy, aby cache odświeżał się częściej, bądź radziej niż 24 godziny, to przy tworzeniu obiektu musimy podać 2. parametr, którego wartość będzie liczbą całkowitą wyrażoną w sekundach. Np. by co 12h ( 43200 sekund ) pobierało od nowa PageRank dla danej strony, tworzymy tak obiekt:
$pagerank = new GooglePageRank($_GET['adres'],43200);
Jeżeli miałby ktoś problem z kodem pliku index.php, umieszczam jego całe ( straasznie długie
) źródło:
1 2 3 4 5 6 7 | <?php require_once 'includes/google_pagerank.class.php'; define('CACHE_DIR', './cache/'); $pagerank = new GooglePageRank($_GET['adres']); echo $pagerank->pagerank; ?> |
Jeżeli chcemy uruchomić nasz skrypt z wartością zmiennej GET adres równą http://webday.pl , to do przeglądarki wpisujemy:
http://adres-naszego-serwera/?adres=http://webday.pl
Przypominam, że serwer domyślnie odwołuje do pliku index.php, więc powyższy link pokaże to samo, co ten:
http://adres-naszego-serwera/index.php?adres=http://webday.pl
Działanie możemy również sprawdzić, umieszczając prosty formularz w pliku index.php:
1 2 3 4 | <form action="index.php" method="get">
<input type="text" name="adres"/>
<input type="submit" />
</form> |
Aby to zrobić, musimy minimalnie zmodyfikować nasz plik index.php, nadając mu taką wartość:
1 2 3 4 5 6 7 8 9 10 11 12 | <?php require_once 'includes/google_pagerank.class.php'; define('CACHE_DIR', './cache/'); if(isset($_GET['adres'])) { $pagerank = new GooglePageRank($_GET['adres']); echo $pagerank->pagerank; } echo '<form action="index.php" method="get"> <input type="text" name="adres"/> <input type="submit" /> </form>'; ?> |
Skrypt zwraca wartość -1 dla stron, które jeszcze nie posiadają PageRank ( taki przypadek można zauważyć tylko u bardzo młodych stronach, które jeszcze “nie przeżyły” odświeżenia PR ), a dla reszty zwróci wartość z zakresu 0-10.
Mankamentem może być to, że po 1000 pobraniach PageRank’u w ciągu dnia, nasze IP może zostać zablokowane, więc radzę rozsądnie korzystać z tego, jednak pamiętajmy, że PR odświeża się conajmniej co miesiąc, więc ważność cache’u możemy ustawić na jakieś 10 dni.