Home PageКаталог Изменения НовыеКомментарии Пользователи Регистрация
CakePHP: Code/Components/GoogleSearch ...

Поиск Google

компонент для CakePHP, который парсит результаты поиска Google


Автор Владимир Лучанинов


Google — классная поисковая система, но почему то они закрыли доступ к поиску в нём через SOAP. Как говорила в далёком детстве школьная учительница английского “Shame on you”, ведь даже у Yandex есть Yandex.XML?. Мы же не спамеры, нам 1000 поисков в день с головой достаточно.


Но, пока можно искать через браузер, можно искать через Browser :)
Попробуем применить наш компонент в боевых условиях.


Что нужно от GoogleSearch (так затейливо назвал я этот компонент)


Помня о заветах Ильича Test-Driven development, напишем сначала тест. Пусть он ищет “google” и “yahoo”. Угадайте, какие сайты будут на первом месте? Вот их наличие и проверим.


<?php
class GoogleSearchTestController extends Controller {
    var 
$name 'GoogleSearchTest';
    var 
$uses null;
    var 
$components = array('Browser''GoogleSearch');
}

class 
GoogleSearchTest extends CakeTestCase {
    var 
$name 'GoogleSearch';
    var 
$controller null;
    var 
$component null;

    function 
setUp() {
        
$this->controller =& new GoogleSearchTestController();

        
restore_error_handler();
        @
$this->controller->_initComponents();
        
set_error_handler('simpleTestErrorHandler');

        
$this->controller->Browser->startup($this->controller);
        
$this->component $this->controller->GoogleSearch;
        
$this->component->startup($this->controller);
        
ClassRegistry::addObject('view', new View($this->controller));
    }



    function 
testSearch() {
        
$this->component->maxResults 10// no need to bother google a lot

        
$result $this->component->query('google');
        
$this->assertTrue($result['count']> 1000000);
        
$this->assertWithinMargin(count($result['sites']), $this->component->maxResults3);

        
$this->assertNotNull($result['sites'][0]);
        
$this->assertNotNull($result['sites'][0]['url']);
        
$this->assertNotNull($result['sites'][0]['title']);
        
$this->assertNotNull($result['sites'][0]['snippet']);

        
$this->assertPattern('/google\.com/i'$result['sites'][0]['url']);
        
$this->assertPattern('/google/i'$result['sites'][0]['title']);



        
$result $this->component->query('yahoo');
        
$this->assertTrue($result['count']> 1000000);
        
$this->assertWithinMargin(count($result['sites']), $this->component->maxResults3);

        
$this->assertPattern('/yahoo\.com/i'$result['sites'][0]['url']);
        
$this->assertPattern('/yahoo/i'$result['sites'][0]['title']);
    }
}
?>


Вот, кстати, и обнаружилась полезность тестов. Если компонент А использует компонент Б, то в контроллере, вызывающем компонент А, нужно обязательно указать компонент Б, даже если он сам по себе не используется. Я об этом постоянно забываю.

var $components = array('Browser', 'GoogleSearch');


Тест также показывает как правильно использовать компонент. Для поиска с помощью надо в контроллере написать

$this->GoogleSearch->query('test');


Можно перед этим его настроить

$this->GoogleSearch->maxResults = 100; // по умолчанию - 500
$this->GoogleSearch->lang = 'ru'; // по умолчанию - 'en'
$this->GoogleSearch->server = 'http://www.google.ru'; // без слеша в конце. по умолчанию 'http://www.google.com'


А вот и сам код.

<?
class GoogleSearchComponent {
    var 
$components = array('Browser');

    var 
$maxResults 500;
    var 
$lang 'en';
    var 
$server 'http://www.google.com';

    
/**
     * Main function. Searches $keyword in Google
     *
     * @param string $keyword Stuff you enter in the search box
     * @return array (sites=>a(url=>, title=>, snippet=>), count=>)
     */

    
function query($keyword) {
        
$limit 10;
        
$start 0;
        
$result = array('sites'=>array());

        while (
$start<$this->maxResults && !(isset($matches) && count($matches)<($limit-3))) {
            if (
$start>=100) {
                
$limit 100;
            } elseif (
$start>=40) {
                
$limit 20;
            }

            
$url $this->server.'/search?'.
                
'num='.$limit.
                
'&hl='.$this->lang.
                
'&client=firefox-a'.
                
'&rls=org.mozilla%3Aen-US%3Aofficial'.
                
'&as_qdr=all'.
                
'&q='.urlencode($keyword).
                
'&btnG=Search'.
                ((
$start>0) ? '&start='.$start.'&sa=N' '');


            
usleep(rand(1002000)); // be like humans for Google
            
$s $this->Browser->get($url);
            if (
strpos($s'<title>403 Forbidden</title>')!==false && preg_match('/^HTTP\/1.1 403 Forbidden/i'$this->Browser->header)) {
                die(
'<h3 style="color:red">Google reminds you that automatic Google parsing is prohibited. Don\'t be evil :)</h3>');
            }

            if (
$start==0) {
                
$result['count'] = r(','''$this->_find('|of (about )?<b>([\d,]+)</b> for <b>|i'$s2));
            }

            
preg_match_all('|<h2\s+class=.?r.?><a href="(.+)".+>(.+)</a></h2>.+<font size=-1>(.+)<br>|iU'$s$matchesPREG_SET_ORDER);
            foreach (
$matches as $match) {
                
$result['sites'][] = array(
                    
'url'      => $match[1],
                    
'title'  => strip_tags($match[2], '<b>'),
                    
'snippet'   => strip_tags($match[3], '<b>'),
                );
            }

            
$start += $limit;
        }

        return 
$result;
    }

    
/**
     * Alias for query
     *
     * @param string $keyword
     * @return array
     */
    
function search($keyword) {
        return 
$this->query($keyword);
    }

    
/**
     * Init
     *
     * @param AppController $controller
     */
    
function startup(&$controller) {

    }

    
/**
     * Looks for $pattern in $s and returns match no. $index
     *
     * @param string $pattern RegEx
     * @param string $s
     * @param int $index
     * @return string
     */
    
function _find($pattern$s$index=1) {
        
preg_match($pattern$s$out);
        return (isset(
$out[$index])) ? $out[$index] : '';
    }
}
?>


Disclaimer: Используйте этот код только в учебных целях. Google запрещает себя парсить роботами.


Кстати, респект программерам из Google. До того как я поставил случайную задержку между запросами, банили через 300 запросов.


 
Комментарии
Добавить комментарий:

Файлы [Скрыть файлы/форму]