tell why crawler stops
[phinde.git] / src / phinde / Crawler.php
1 <?php
2 namespace phinde;
3
4 class Crawler
5 {
6     protected $es;
7     protected $queue;
8
9     /**
10      * If the links only should be shown, not queued
11      */
12     protected $showLinksOnly = false;
13
14     static $supportedIndexTypes = array(
15         'application/atom+xml'  => '\\phinde\\LinkExtractor\\Atom',
16         'application/xhtml+xml' => '\\phinde\\LinkExtractor\\Html',
17         'text/html'             => '\\phinde\\LinkExtractor\\Html',
18     );
19
20     public function __construct()
21     {
22         $this->es = new Elasticsearch($GLOBALS['phinde']['elasticsearch']);
23         $this->queue = new Queue();
24     }
25
26     public function crawl($url)
27     {
28         $res = $this->fetch($url);
29         if ($res === false) {
30             return;
31         }
32
33         $linkInfos = $this->extractLinks($res);
34         $linkInfos = $this->filterLinks($linkInfos);
35         if ($this->showLinksOnly) {
36             $this->showLinks($linkInfos);
37         } else {
38             $this->enqueue($linkInfos);
39         }
40     }
41
42     protected function fetch($url)
43     {
44         $existingDoc = $this->es->get($url);
45
46         $req = new HttpRequest($url);
47         $req->setHeader(
48             'accept',
49             implode(',', array_keys(static::$supportedIndexTypes))
50         );
51         if ($existingDoc && isset($existingDoc->modate)) {
52             $nMoDate = strtotime($existingDoc->modate);
53             $req->setHeader('If-Modified-Since: ' . date('r', $nMoDate));
54         }
55
56         $res = $req->send();
57         if ($res->getStatus() === 304) {
58             //not modified since last time, so don't crawl again
59             $this->log('Not modified since last fetch');
60             return false;
61         } else if ($res->getStatus() !== 200) {
62             throw new \Exception(
63                 "Response code is not 200 but "
64                 . $res->getStatus() . ", stopping"
65             );
66         }
67         return $res;
68     }
69
70     protected function extractLinks(\HTTP_Request2_Response $res)
71     {
72         $mimetype = explode(';', $res->getHeader('content-type'))[0];
73         if (!isset(static::$supportedIndexTypes[$mimetype])) {
74             echo "MIME type not supported for indexing: $mimetype\n";
75             return array();
76         }
77
78         $class = static::$supportedIndexTypes[$mimetype];
79         $extractor = new $class();
80         return $extractor->extract($res);
81     }
82
83     protected function filterLinks($linkInfos)
84     {
85         $filteredLinkInfos = array();
86         foreach ($linkInfos as $linkInfo) {
87             $allowed = Helper::isUrlAllowed($linkInfo->url);
88             $crawl   = $allowed;
89             $index   = $GLOBALS['phinde']['indexNonAllowed'] || $allowed;
90
91             if ($crawl && count($GLOBALS['phinde']['crawlBlacklist'])) {
92                 foreach ($GLOBALS['phinde']['crawlBlacklist'] as $bl) {
93                     if (preg_match('#' . $bl . '#', $linkInfo->url)) {
94                         $crawl = false;
95                     }
96                 }
97             }
98
99             $linkInfo->known = $this->es->isKnown($linkInfo->url);
100             $linkInfo->crawl = $crawl;
101             $linkInfo->index = $index;
102             $filteredLinkInfos[] = $linkInfo;
103         }
104         return $filteredLinkInfos;
105     }
106
107     protected function enqueue($linkInfos)
108     {
109         foreach ($linkInfos as $linkInfo) {
110             if ($linkInfo->known) {
111                 continue;
112             }
113             if ($linkInfo->crawl || $linkInfo->index) {
114                 $this->es->markQueued($linkInfo->url);
115             }
116             if ($linkInfo->index) {
117                 $this->queue->addToIndex(
118                     $linkInfo->url, $linkInfo->title, $linkInfo->source
119                 );
120             }
121             if ($linkInfo->crawl) {
122                 $this->queue->addToCrawl($linkInfo->url);
123             }
124         }
125     }
126
127     protected function showLinks($linkInfos)
128     {
129         foreach ($linkInfos as $linkInfo) {
130             echo $linkInfo->url . "\n";
131             if ($linkInfo->title) {
132                 echo '   title: ' . $linkInfo->title . "\n";
133                 echo '  source: ' . $linkInfo->source . "\n";
134                 echo '   known: ' . intval($linkInfo->known)
135                     . ', crawl: ' . intval($linkInfo->crawl)
136                     . ', index: ' . intval($linkInfo->index) . "\n";
137             }
138         }
139     }
140
141     public function setShowLinksOnly($showLinksOnly)
142     {
143         $this->showLinksOnly = $showLinksOnly;
144     }
145
146     protected function log($msg)
147     {
148         echo $msg . "\n";
149     }
150 }
151 ?>