/** * Timeout перед очередным запросом * @param string $url * @return bool */ protected function _timeout($url) { $domain = Uri::getHost($url); // пытаемся считать timeout для текущего домена из БД try { $timeout = $this->_Db->timeout($domain); } catch (\Exception $e) { $timeout = null; } try { if (!is_float($timeout)) { $timeouts = Robots\Instance::get($url, 'crawl-delay', $this->_config['agent']); $timeout = is_array($timeouts) && isset($timeouts[0]['value']) ? floatval($timeouts[0]['value']) : 0.0; // ставим timeout try { $timeout = $this->_Db->timeout($domain, $timeout); } catch (\Exception $e) { } } // получаем время последнего запроса try { $last = $this->_Db->request($domain); } catch (\Exception $e) { // если ошибка, то делаем вид, что только что сделали запрос $last = microtime(true); } $now = microtime(true); // переводим в микросекунды $diff = $last - $now + $timeout; // смотря что больше текущий момент или последний запрос (он может быть в будущем, т.к. мы резервируем время $next = max([$now, $last]); if ($diff > 0) { $next += $diff; } // небольшой хак, для того, что другие клиенты считали этот запрос выполенным, иначе может получить пачка // с одной задержкой $this->_Db->request($domain, $next); if ($diff > 0) { $sleep = ($next - $now) * 1000000; usleep($sleep); } return true; } catch (\Exception $e) { return false; } }
/** * Загружаем robots txt * * @param string $url домен или url для загрузки * @return string $content содержимое robots */ protected function _request($url, $agent) { Browser::pack(); Browser::configure(['agent' => $agent], ['direct' => true, 'exception' => true]); $content = Browser::get(Uri::getHost($url) . '/robots.txt'); Browser::unpack(); return $content; }
/** * Проверяем индексации url-а * * @param string $text сайт * @param string $region регион * @param array $additional массив дополнительных параметорв * @throws Exception * * @return array */ public function isIndexed($text, $region = null, $additional = []) { $query = is_array($text) ? $text['text'] : $text; $domain = is_array($text) && !empty($text['domain']) ? $text['domain'] : null; // формируем данные для поиска $params = []; if (Uri::isValid($query)) { $query = Uri::wwwLess(Uri::httpLess($query)); $params['text'] = 'url:(' . $query . ') | url:(www.' . $query . ')'; } else { $params['text'] = $query; } $params['region'] = $region; // домен if (!Uri::isValid($query) && !empty($domain)) { $params['text'] .= " site:{$domain}"; } $result = $this->search($params); if (!isset($result['total_pages'])) { throw new Exception('Не удалось проанализировать выдачу.'); } $domain = $domain ?: Uri::getHost($query); $validate = !empty($additional['validate']); $urls = array_filter($result['results'], function ($record) use($domain, $validate) { if (!empty($domain)) { if (!Uri::isSameHostIdna($record->url, $domain, false)) { return false; } } return $validate ? Checker::isValid($record->url, 'text/html') : true; }); return ['indexed' => !empty($urls) && count($urls) > 0, 'urls' => $urls]; }
/** * Устанавливаем url для следующего запроса * * @param string $url * @return bool */ public function url($url) { $this->_url = Uri::toIdn($url); // кодируем всё заранее в punycode во избежании путаницы return curl_setopt($this->_curl, CURLOPT_URL, $this->_url); }