Парсинг выдачи Google. Практика.

Очень часто перед веб-мастерам встает задача парсинга выдачи поисковиков.

Это может быть нужно для того, чтобы:

  • автоматизировать процесс обработки какой-то статистической информации;
  • посчитать количество ссылающихся на ваш сайт ресурсов, определить их качество и прочие параметры;
  • найти урлы сайтов, которые натырили у вас контент (хотя тут лучше использовать что-то наподобии CopyScape, однако я заметил, что он иногда выдает бред);
  • определить частоту запросов.

И это лишь малый диапазон задач, в которых вам может понадобиться парсинг выдачи поисковиков. А точнее это то, что мне пришло в голову :)

А ведь еще у нас есть такие ребята как серые и черные оптимизаторы, которые при помощи поисковика ищут спамилки. Но это вообще отдельный разговор, который ничего общего с нашими благими целями не имеет :)

В данной статье я хотел бы рассказать как проще всего из выдачи Google доставать такие параметры как:

  • время генерации ответа (может понадобиться для статистики и расчета общего времени парсинга);
  • количество страниц, соответствующих вашему запросу (частота);
  • ну и собственно ссылки на конечные ресурсы.

Реализовывать механизм получения выдачи вы можете на чем угодно: curl, сокеты, fopen. Особой разницы для нас между ними нет. Предположим, что получать содержимое выдачи вы уже умеете (а если не умеете, то читать дальше вам просто незачем).

И вот тут начинаются вопросы.

1. Как достать из выдачи время генерации и количество страниц на данный запрос?

Я делаю это таким кодом (выдрано из тучи моих классов и гигантского ядра парсера, так что если найдете какую-то ошибку - напишите в комментах):

$blue_line_pcre = "(Результаты|Результати|Results)(.*)";
$parse_time_pcre= '\<b\>([\d\,\.]+)\<\/b\>\s[^\d]+$';
$parse_num_pcre	= '\<b\>([\d\,]+)\<\/b\>[^\d]+';
 
if(preg_match("/$blue_line_pcre/Ui",$Result,$matches)) {
    unset($matches[0]);
    if(!empty($matches[1])) {
        $BlueLine=preg_replace("/\&nbsp\;/",'',$matches[2]);
    } else {
        $BlueLine='';
    }
}
 
$find=$parse_num_pcre.$parse_time_pcre;
if(preg_match("/$find/Ui",$BlueLine,$matches)) {
    unset($matches[0]);
    $query_results_num=$matches[1];
    $query_time=$matches[2];
}

То есть, предполагается, что в переменной $Result хранится html код выдачи (и очено важно к этому коду не забыть применить

$Result=preg_replace("/[\n\r\t]/", '', $Result);

, чтобы потом не ломать голову почему половина регулярок не работают).
После выполнения моего скрипта в переменной $BlueLine будет html код синей полоски Гугла, в которой будет написано, что-то на подобии “Результаты 1 - 10 из примерно 87 300 000 для вакансии. (0,04 секунд)”.

В переменной query_results_num будет количество сайтов, подходящих под ваш запрос, а в переменной query_time - время генерации выдачи.

Из кода выше вы могли заметить, что регулярное выражение для нахождения кода “синей линии” имеет следующий непонятный вид:

(Результаты|Результати|Results)(.*)

Это сделано исключительно для того, чтобы с равной точностью парсить выдачу как google.com, так и google.ru и google.com.ua.

Заметка: результаты выдачи по одному и тому же запросу на разных локализованных версиях Google отличаются! Хоть иногда эту разницу тяжело обнаружить, но все же она есть. Можете на досуге поиграться с каким-то ВЧ запросом. Расскажете о результатах своих наблюдений.

Также некоторые вопросы могут вызывать регулярные выражения для нахождения времени генерации и количества страниц:

$parse_time_pcre = '\<b\>([\d\,\.]+)\<\/b\>\s[^\d]+$';
$parse_num_pcre	= '\<b\>([\d\,]+)\<\/b\>[^\d]+';

Для чего я использую вот такие конструкции [\d\,\.]? Исключительно для совместимости. Делаю я это потому, что разделителем целой и дробной части в нашей стране является “.”, а в США - “,”. В регулярном выражении для количества страниц вы также можете видеть символ “\,” - это используется для того, чтобы правильно находить количество страниц, так как американцы разделяют тысячи этим знаком.

2. Как достать ссылки из выдачи?

Эта даже проще чем выдирать параметры. Я делаю это при помощи следующего кода:

$find = $parse_links_pcre = "<a\shref=\"([^\"]+)\".*sclass=l";
if(preg_match_all("/$find/Ui",$Result,$matches)) {
    unset($matches[0]);
    $links=$matches[1];
}

В массиве $links будут все ссылки, найденный на текущей странице. Недостатки у такой унификации тоже есть: в результирующем массиве вы получите все ссылки “Translate” и этот метод не очень-то и быстро работает из-за использования в регулярном выражении “.*”. Но это место всегда можно подрихтовать под ваши конкретные требования. В оригинальном виде я тоже не особо часто использую данное регулярное выражение. Но есть также и плюсы у этого кода: он не загребает ссылки с оплаченных объявлений, а они, как вы знаете, вылазят на каждой странице поиска и могут очень сильно нарушить ваши статистические исследования или сбор ссылок.

Спасибо, что дочитали до конца. Рад буду выслушать ваши предложения и дополнения.

14 комментариев

  1. ParserPro » Архивы сайта » Выдача Google:

    […] Контакты « Парсинг выдачи Google. Практика. […]

  2. Mendel:

    будь проще и к тебе потянутся люди :)
    как взять контент это какраз один из сложнейших этапов… брать то надо красиво :)
    простейший пример с прокси и максимальной скоростью работы:

    function mybot($url,$proxy=FALSE,$user_agent="Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)")
    {
    // получим контент
    $ch = curl_init();    // initialize curl handle
    if($proxyFALSE) curl_setopt($ch, CURLOPT_PROXY, $proxy);
    curl_setopt($ch, CURLOPT_URL, $url); // set url to post to
    curl_setopt($ch, CURLOPT_FAILONERROR, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
    $document = curl_exec($ch);
    curl_close($ch);
    return $document;
    }

    а по теме, так мудрено слишком…
    проще например так:

    // ****************************************************************
    // посмотрим сколько страниц в индексе гугла
    // ****************************************************************
    $ccc=mybot('http://www.google.com.ua/search?q=site:'.$domain);
    $ccc=str_replace('&nbsp;','',$ccc);
    preg_match_all('@<b>([0-9]*)</b>@si',$ccc,$ccc);
    $data['g_index']=$ccc[1][2];

    фсё….можно проще конечно, но лень :)

  3. admin:

    Ну я и так старался не очень мудрить :) Сори, если уж слишком закрутил.

    Я код “синей линии” достаю отдельно только потому что он иногда может понадобиться для анализа.

    А вот по поводу вашего кода могу сказать, что вот это:

    preg_match_all('@<b>([0-9]*)</b>@si',$ccc,$ccc);

    не найдет на google.com нужного нам числа. А в трудовых буднях работника парсинга приходится и его выдачу “анализировать”.

    Я же в свою очередь пытался сделать универсальный инструмент. Может и не получилось. Но я буду стараться :)

  4. Mendel:

    исправил на google.com - parse.com.ua - скрипт выдал 10 страниц… :)
    не забывай что   мы уже удалили :)
    зачем тащить всю синюю линию в упор не понимаю - 1-10 неинформативно, что я запрашивал я и так знаю, а сколько времени у гоши ушло так это ему надо считать а не мне :) ну да ладно, если тебе нужно…

  5. Mendel:

    блин, во второй строчке жесткий пробел был схаван. обведи его [html] плиз…

  6. admin:

    Этот вордпресс меня уже порядком поднапряг :)
    Хавает коды как хочет :)

    Но ваша регулярка все равно не универсальная получилась и даже очень не универсальная!
    Она вытащит только числа до 999 включительно, потому что потом google.com будет отдавать страницы в виде “1,000″, что уже не подходит под ваш шаблон регулярного выражения.

    Можете проверить на каком-то ВЧ запросе, а не на “parse.com.ua” :)

  7. Mendel:

    http://www.google.com/search?q=site:domenforum.net
    как и com.ua отдает с пробелами, которые уже удалены :)
    но если у вас такой запрос что гоша почемуто отдает с запятой то либо добавте после девятки запятую либо в прелидущей щамене замените пробел на запятую… ладно, не будем спорить о стиле. в ваших задачах удобнее ваш код, в моих быстреее мой :)

  8. admin:

    Ок.
    Но и по той ссылке, что вы дали он мне отдал число с запятой :)

  9. hamster:

    О, классно. Очень полезно. Искал что-то подобное, но не думал, что найду тут.. Сам пока осваиваю программинг - еще зеленый.)

  10. admin:

    Всегда рад помочь!

  11. [YS.PRO]:

    $Result=preg_replace(”/[\n\r\t]/”, ”, $Result);

    Ох не нравится мне это, зачем комп мучать?
    $result = str_replace(array(”\n”, “\r”, “\t”), ”, $result);

  12. admin:

    Я не знаю, но как я наблюдал так нагрузка не такая уж и громадная :)))
    Ну по крайней мере мой двухядерный Атлон с легкостью справляется :))))))

  13. ParserPro » Архивы сайта » Тест. Кто быстрее: preg_replace() или str_replace()?:

    […] к моему посту “Парсинг выдачи Google. Практика.” [YS.PRO] оставил коммент следующего содержания: […]

  14. Виктор:

    Спасибо было полезно, узнал много нового

Оставить комментарий