Google Search Position Checker
I was recently asked if there was a simple way to check the position a particular page comes in a google search result for a given set of keywords – and without doing a manual search for each keyword and counting down from the top! Luckily Google’s AJAX API along with a bit of PHP can be used for this, so I wrote a small class to do the job.
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 | <?php class GPositionFinder { private $_keyword; private $_page; private $_offset = 0; private $_apiUrl = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&hl=en&rsz=large&q=%s"; private $_referer = "http://www.karlrixon.co.uk"; private $_results; private $_position = 0; private $_output; private $_outputMethod = 'text'; public function __construct($keyword=null, $page=null) { $this->setKeyword($keyword); $this->setPage($page); } public function setReferer($value) { if ($value) { $this->_referer = $value; return true; } return false; } public function setKeyword($value) { if ($value) { $this->_keyword = urlencode($value); return true; } return false; } public function setPage($value) { if ($value) { $this->_page = basename($value); return true; } return false; } public function setOutputMethod($value) { switch ($value) { case 'json': $this->_outputMethod = 'json'; break; case 'text': $this->_outputMethod = 'text'; break; default: return false; } return true; } public function getPosition() { return $this->_position; } public function getOutput() { return $this->_output; } public function go() { if (!$this->_setApiUrl()) { trigger_error('You must set a valid keyword before calling go().', E_USER_ERROR); } if (!$this->_page) { trigger_error('You must set a valid page to check before calling go().', E_USER_ERROR); } while(!$this->_position && $this->_offset < 40) { $this->_getNextResultSet(); if (empty($this->_results) || !is_object($this->_results)) { break; } $this->_checkPosition(); } $output = null; switch ($this->_outputMethod) { case 'json': $output = json_encode(array( 'position' => $this->_position, 'keyword' => urldecode($this->_keyword), 'page' => $this->_page )); break; case 'text': $output = "position={$this->_position}&keyword=" . rawurldecode($this->_keyword) . "&page={$this->_page}"; break; } return $this->_output = $output; } private function _setApiUrl($url=null) { if (!$this->_keyword) { return false; } $this->_apiUrl = sprintf($this->_apiUrl, $this->_keyword); return true; } private function _getNextResultSet() { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->_apiUrl . "&start={$this->_offset}"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_REFERER, $this->_referer); $body = curl_exec($ch); curl_close($ch); $this->_results = json_decode($body); } private function _checkPosition() { foreach ($this->_results->responseData->results as $key => $result) { if (preg_match("#^{$this->_referer}.*{$this->_page}$#", $result->unescapedUrl)) { $this->_position = $this->_offset + 1; return; } else { $this->_offset++; } } } } ?> |
Usage
Usage is pretty simple. You either need to edit the default value of the $_referer property, or else you can use the setReferer() method. It is important to correctly set the referrer to your own site. Not only does Google require this, the class also uses this value to differentiate between your about.php and someone else’s about.php when examining results.
1 2 3 | $posFinder = new GPositionFinder('my keyword phrase', 'mypage.htm'); $posFinder->setReferer('http://www.mysite.com'); echo $posFinder->go(); |
As you can see from the previous example, keyword and page can be passed in to the constructor when an instance of the class is created, but they can also be set later using the setKeyword() and setPage() methods:
1 2 3 4 | $posFinder = new GPositionFinder; $posFinder->setKeyword('my keyword phrase'); $posFinder->setPage('mypage.htm'); echo $posFinder->go(); |
If you are going to use this method though, be sure to set both a keyword and page before calling go() or else you will get a fatal error.
The class can output results in a few different ways. By default it will output a string deleimited in the same way as a URL query string, containing the position of the page, the keyword used and the page used. These last two components may be useful in AJAX implementations as they save you having to keep track of these values on the client side. Here’s an example of the default output:
1 2 3 | $posFinder = new GPositionFinder('keyword', 'mypage.htm'); echo $posFinder->go(); // outputs position=1&keyword=keyword&page=mypage.htm |
Another option is to have the output returned as a JSON encoded string:
1 2 3 4 | $posFinder = new GPositionFinder('keyword', 'mypage.htm'); $posFinder->setOutputMethod('json'); echo $posFinder->go(); // outputs {"position":1,"keyword":"keyword","page":"mypage.htm"} |
As you can see the go() method returns the output itself, but if you can also access the output at any time via the getOutput() method.
1 2 3 4 | $posFinder = new GPositionFinder('keyword', 'mypage.htm'); $posFinder->go(); // some other code echo $posFinder->getOutput(); |
You may also want to access the position you achieved directly without using an output string:
1 2 3 4 | $posFinder = new GPositionFinder('keyword', 'mypage.htm'); $posFinder->go(); echo $posFinder->getPosition(); // outputs 1 (hopefully!) |
Limitations
The class will only examine the top 40 results. I believe Google limits the number of results to 64, and for my own purposes, anything not in the top 40 results (the top 4 pages of a default google search) is not really worth worrying about anyway. You can of course extend this, but be sure to keep it less than the Google imposed limit or else the class may break (I haven’t tried it).
Another thing to bear in mind is that this class will only search google.com. This is due to the nature of the API, and as far as I know, nothing can be done to search local Google sites. If anyone knows differently please let me know as google.co.uk results would be useful to me!
Another limitation to consider is that this class will only work in PHP >= 5.2.0. This is due to the need for the json_decode function as the API returns results in JSON format. It could be rewritten to work on earlier versions of PHP4 using the PEAR Services_JSON package, and defining the following functions at the top of the class file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | include("JSON.php"); if( !function_exists('json_encode') ) { function json_encode($data) { $json = new Services_JSON(); return $json->encode($data); } } if( !function_exists('json_decode') ) { function json_decode($data) { $json = new Services_JSON(); return $json->decode($data); } } |
This will allow the code to function, and if the server is ever upgraded the PEAR package will automatically become redundant in favour of the native PHP versions of json_encode and json_decode.
Finally bear in mind this class was written to solve a specific need I had. It may not be ideal for you, and it may be lacking features you like. If you make any improvements to it (or find any bugs!) please let me know.