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.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>