~tsep-dev/tsep/0.9-beta

« back to all changes in this revision

Viewing changes to branches/symfony/app/vendors/tsep_crawler.php

  • Committer: geoffreyfishing
  • Date: 2011-01-11 23:46:12 UTC
  • Revision ID: svn-v4:ae0de26e-ed09-4cbe-9a20-e40b4c60ac6c::125
Created a symfony branch for future migration to symfony

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
* HTTP Crawler for The Search Engine Project
 
4
 
5
* @author geoffreyfishing
 
6
*
 
7
* The following will be filled automatically by SubVersion!
 
8
* Do not change by hand!
 
9
*  $LastChangedDate: $
 
10
*  $LastChangedBy:  $
 
11
*  $LastChangedRevision: $
 
12
*
 
13
*/
 
14
 
 
15
/**
 
16
 * TSEPCrawler
 
17
 * Example usage:
 
18
 * $crawler = new TSEPCrawler('starturl', 'some_regx');
 
19
 * while ($result = $crawler->crawl())
 
20
 * {
 
21
 *  $content_of_page = $result->content;
 
22
 *  $url_of_page = $result->url;
 
23
 * }
 
24
 * 
 
25
 * @author Geoffrey
 
26
 *
 
27
 */
 
28
class TSEPCrawler {
 
29
        
 
30
        /**
 
31
         * regex
 
32
         * The regular expression that urls must match to be crawled
 
33
         * @var string
 
34
         */
 
35
        private $regex = '';
 
36
        
 
37
        /**
 
38
         * urls
 
39
         * URLS that are queued to be crawled
 
40
         * @var array
 
41
         */
 
42
        private $urls = array();
 
43
        
 
44
        /**
 
45
         * done
 
46
         * URLs that have already been crawled
 
47
         * @var array
 
48
         */
 
49
        private $done = array();
 
50
        
 
51
        /**
 
52
         * url
 
53
         * The current URL
 
54
         * @var string
 
55
         */
 
56
        private $url = '';
 
57
        
 
58
        /**
 
59
         * agent
 
60
         * The user agent to act as
 
61
         * @var string
 
62
         */
 
63
        private $agent = '';
 
64
        
 
65
        /**
 
66
         * Contructor
 
67
         * Initializes the class
 
68
         * @param string $start The start url
 
69
         * @param string $regex The Regular Expression that URLs must match to be crawled
 
70
         * @param string $elements The elements and their properties that contain the links
 
71
         */
 
72
        function __construct($start, $regex, $agent) {
 
73
                
 
74
                //Queue the start url to be crawled
 
75
                if ($start != null)
 
76
                        array_push($this->urls, $start);
 
77
                                        
 
78
                //Set the RegEx
 
79
                $this->regex = $regex;
 
80
                
 
81
                //Set the User Agent
 
82
                $this->agent = $agent;
 
83
                                        
 
84
                $this->cleanURLs();
 
85
                
 
86
                //Grab the robots.txt file
 
87
                $this->parseRobots($start);
 
88
                
 
89
        }
 
90
        
 
91
        /**
 
92
         * crawl
 
93
         * Advances the crawler to the next URL and returns the page contents
 
94
         * @return Page
 
95
         */
 
96
        function crawl() {
 
97
                
 
98
                
 
99
                //We will return false if there is nothing left to crawl
 
100
                if (empty($this->urls))
 
101
                        return false;
 
102
                
 
103
                $this->url = array_pop($this->urls);
 
104
                
 
105
                // Create the stream context
 
106
                $context = stream_context_create(array(
 
107
                    'http' => array(
 
108
                        'timeout' => 5      // Timeout in seconds
 
109
                    )
 
110
                ));
 
111
                
 
112
                // Fetch the URL's contents
 
113
                $contents = @file_get_contents($this->url, 0, $context);
 
114
                
 
115
                // Check for empties
 
116
                if (!empty($contents))  $this->parse($contents);
 
117
                        
 
118
                        
 
119
                /*
 
120
                 * If retreiving the contents failed, we don't need to do anything
 
121
                 * because the URL will simply be removed from the list. However,
 
122
                 * we need to add the URL to $this->done whatever the case is 
 
123
                 * because we don't want to call the same page twice (or more)
 
124
                 */
 
125
                        
 
126
                array_push($this->done, $this->url);
 
127
                
 
128
                //Now remove duplacate URLs, URLs that don't match the RegEx, and already crawled URLs
 
129
                $this->cleanUrls();
 
130
 
 
131
                $url = $this->url;
 
132
                
 
133
                $this->url = '';
 
134
                
 
135
                $type = $this->getType($contents);
 
136
                
 
137
                //And that is pretty much it
 
138
                return new Page($contents, $url, $type);
 
139
        }
 
140
        
 
141
        private function parseRobots ($url) {
 
142
                # parse url to retrieve host and path
 
143
            $parsed = parse_url($url);
 
144
            
 
145
            $useragent = $this->agent;
 
146
        
 
147
            $agents = array(preg_quote('*'));
 
148
            if($useragent) $agents[] = preg_quote($useragent);
 
149
            $agents = implode('|', $agents);
 
150
        
 
151
            # location of robots.txt file
 
152
            $robotstxt = @file("http://{$parsed['host']}/robots.txt");
 
153
            if(!$robotstxt) return true;
 
154
        
 
155
            $rules = array();
 
156
            $ruleapplies = false;
 
157
            foreach($robotstxt as $line) {
 
158
              # skip blank lines
 
159
              if(!$line = trim($line)) continue;
 
160
        
 
161
              # following rules only apply if User-agent matches $useragent or '*'
 
162
              if(preg_match('/User-agent: (.*)/i', $line, $match)) {
 
163
                $ruleapplies = preg_match("/($agents)/i", $match[1]);
 
164
              }
 
165
              if($ruleapplies && preg_match('/Disallow:(.*)/i', $line, $regs)) {
 
166
                # an empty rule implies full access - no further tests required
 
167
                if(!$regs[1]) return true;
 
168
                # add rules that apply to array for testing
 
169
                $rules[] = preg_quote(trim($regs[1]), '/');
 
170
              }
 
171
            }
 
172
        
 
173
            foreach($rules as $rule) {
 
174
              # Push the URL into the 'done' array
 
175
                                array_push($this->done, url_to_absolute("http://{$parsed['host']}/robots.txt", $rule));
 
176
            }
 
177
 
 
178
        }
 
179
        
 
180
        private function getType($contents) {
 
181
                
 
182
                if(preg_match('/<[^<>]+>/', $contents)) {
 
183
                        return 'text/html';
 
184
                }
 
185
                else {
 
186
                        return 'text/javascript';
 
187
                }
 
188
        }
 
189
        
 
190
        /**
 
191
         * parse
 
192
         * Parses an page and adds all the URLs it can find to $this->urls
 
193
         * @param string $contents The contents to parse
 
194
         */
 
195
        private function parse($contents) {
 
196
                                
 
197
                try {
 
198
                        $type = $this->getType($contents);
 
199
                        
 
200
                        switch ($type) {
 
201
                                case 'text/html':
 
202
                                        $this->parseHTML($contents);
 
203
                                        break;
 
204
                                case 'text/javascript':
 
205
                                        $this->parseJS($contents);
 
206
                                        break;
 
207
                                case 'text/css':
 
208
                                        $this->parseCSS($contents);
 
209
                                        break;
 
210
                                default: //Attempt to parse all three
 
211
                                        $this->parseHTML($contents);
 
212
                                        $this->parseCSS($contents);
 
213
                                        $this->parseJS($contents);
 
214
                                        break;
 
215
                        }
 
216
                        
 
217
                        return true; 
 
218
                }
 
219
                catch (Exception $ex) {
 
220
                        
 
221
                        return false;
 
222
                }
 
223
        }
 
224
 
 
225
        private function parseHTML($contents) {
 
226
                
 
227
                $dom = new DOMDocument();
 
228
                
 
229
                @$dom->recover = true;
 
230
                @$dom->loadHTML($contents);
 
231
                
 
232
                $simple = simplexml_import_dom($dom);
 
233
                
 
234
                unset($dom); //DomDocument is heavy and bloated
 
235
                
 
236
                        
 
237
                $links = $simple->xpath('/html/body//a');
 
238
                
 
239
                foreach ($links as $link){
 
240
 
 
241
                        array_push($this->urls, url_to_absolute($this->url, $link['href']));
 
242
                                
 
243
                }
 
244
                
 
245
        }
 
246
        
 
247
        
 
248
        //TODO: Implement Javascript parsing
 
249
        /**
 
250
         * parseJS
 
251
         * Parses links from JavaScript
 
252
         * @param string $contents The JavaScript to parse
 
253
         */
 
254
        private function parseJS ($contents) {
 
255
        
 
256
                return true;
 
257
        }
 
258
        
 
259
        //TODO: Implement CSS parsing
 
260
        /**
 
261
         * parseCSS
 
262
         * Parses links from CSS
 
263
         * @param string $contents The CSS to parse
 
264
         */
 
265
        private function  parseCSS ($contents) {
 
266
                return true;
 
267
        }
 
268
        
 
269
        /**
 
270
         * cleanURLs
 
271
         * Removes all URLs that are duplicates, have already been crawled, or do not match the RegEx
 
272
         */
 
273
        private function cleanURLs () {
 
274
        
 
275
                //Remove duplcates
 
276
                $this->urls = array_unique($this->urls);
 
277
                $this->done = array_unique($this->done);
 
278
                
 
279
                //Remove done urls              
 
280
                foreach ($this->urls as $key => $value)         
 
281
                        if(in_array($value, $this->done))
 
282
                                unset($this->urls[$key]);
 
283
                //TODO:Design a user friendly system of creating a regex
 
284
                //Check the RegEx
 
285
                //foreach ($this->urls as $key => $value)       
 
286
                //      if(!preg_match($this->regex, $value))
 
287
                //              unset($this->urls[$key]);
 
288
                
 
289
                foreach ($this->urls as $key => $value) {
 
290
                        //Check that the URL is on the same domain
 
291
                        $parsed = parse_url($value);
 
292
                        if ($this->regex != @$parsed['host']) 
 
293
                                unset($this->urls[$key]);
 
294
 
 
295
                        //Check that the URL is not mailto
 
296
                        if(preg_match('/mailto\:([^">]+)/', $value))
 
297
                                unset($this->urls[$key]);
 
298
                }
 
299
                
 
300
                //Reindex the arrays
 
301
                $this->done = array_values($this->done);
 
302
                $this->urls = array_values($this->urls);
 
303
        }
 
304
        
 
305
}
 
306
 
 
307
/**
 
308
 * Page
 
309
 * A page crawled by TSEPCrawler
 
310
 * @author Geoffrey
 
311
 *
 
312
 */
 
313
class Page {
 
314
        public $content;
 
315
        public $url;
 
316
        public $type;
 
317
        
 
318
        function __construct($content, $url, $type = 'text/html') {
 
319
                $this->content = $content;
 
320
                $this->url = $url;      
 
321
                $this->type = $type;
 
322
        }
 
323
}
 
 
b'\\ No newline at end of file'