“Распарсить сайт” — словосочетание, которое повергало меня в уныние всего полгода назад. В моей голове сразу же проносились знакомые проблемы с настройкой фантома, или возней с селениумом. Мысли о возможной необходимости подменять useragent, пагинации и других действиях во время парсинга заставляли откладывать эту задачу в долгий ящик…

Но всё изменилось, когда я встретил Гуся. Мир парсинга заиграл новыми красками. Под катом я хочу показать несколько простых примеров, которые могут помочь распарсить непростые сайты.

Кстати, написав парсер, Гусь решил снять фильм про это, пока что вы можете насладиться трейлером:





Как натравить Гуся на сайт


Всем известно, Гусь любит пощипать — травку, бабушку, гусынь… и конечно же сайты. Чтобы Гусь отложил свои дела и пощипал сайт для вас, необходимо просто указать ему дорогу.

На данный момент Гусь умеет парсить:

  • внутри nodejs, используя PhantomJS;
  • прямо в браузере (идеально если вы пишете плагин для браузера)
  • используя селениум

Каждый из способов имеет свои плюшки и недостатки. Например, Phantom может работать на сервере, но в нем не очень удобно дебажить, запуск Гуся в браузере требует внешнего инструмента, который садит Гуся на сайт. Селениум является довольно универсальным решением, но на данный момент Гусь только учится его использовать.

Итак, чтобы запустить Гуся на сайт, прежде всего надо выбрать и создать среду обитания. Возможные среды:

  • PhantomEnvironment
  • BrowserEnvironment
  • SeleniumEnvironment


В данной статье я буду рассматривать PhantomEnvironment, как наиболее развитый на данный момент.

import {
   PhantomEnvironment,
   Parser
} from 'goose-parser';

const env = new PhantomEnvironment({
   url: 'http://www.gooseplanet.ru/'
});
const parser = new Parser({env});


Среда определяет точку входа Гуся — УРЛ начальной страницы сайта.

До парсинга


Зачастую, до того как мы начнем парсить нам необходимо выполнить какие-либо действия на странице. Например, поиск на гусином сайте знакомств. Гусь поищет за вас — только попросите.

const actions = [
   {
       type: 'type',                // тип действия - печатать
       text: 'гусыня',              // Гусь будет искать гусынь, чтоб неплохо потоптаться
       scope: '.field[name=search]' // селектор елемента
   },
   {
       type: 'click',
       scope: 'button[type=submit]',
       waitForPage: true            // если знаем что результаты откроются на новой странице
   }
];


Декларативность — наше всё


Гусь описателен. Он прост и немногословен.

Пора бы и пощипать


Итак, теперь мы знаем, как найти гусынь. Пора стрельнуть у них адресок. Допустим верстка результатов поиска выглядит вот так:
<ul class="goose-babes">
   <li class="goose-babe">
       <img src="https://habrastorage.org/getpro/habr/post_images/3df/cbd/088/3dfcbd088e8f5b4ba060a73f8d5e3788.jpg" alt="Кряша" class="photo">
       <span class="name">Кряша</span>
       <div>
           <address>Гусятник №5</address>
       </div>
   </li>
   <li class="goose-babe">
       <img src="https://habrastorage.org/getpro/habr/post_images/a48/283/87b/a4828387bbe8658749cc7a42d53ddcd9.jpg" alt="Гатильда" class="photo">
       <span class="name">Гатильда</span>
       <div>
           <address>Хлев</address>
       </div>
   </li>
</ul>


Сориентируем Гуся, задав правила для расположения данных в этой верстке:
const rules = {
   scope: '.goose-babe',
   collection: [[
       {
           name: 'name',
           scope: '.name'
       },
       {
           name: 'address',
           scope: 'address'
       }
   ]]
};


Запускаем Гуся!!


parser.parse({ actions, rules }).then(console.log);


И получаем результат:
[
 {
   "name": "Кряша",
   "address": "Гусятник №5"
 },
 {
   "name": "Гатильда",
   "address": "Хлев"
 }
]


И это только начало


Конечно же данная статья лишь знакомит читателей с Гусем, а не описывает все его возможности. Подробно о том, что умеет Гусь можно прочитать в документации. Но все же давайте перечислим некоторые приемы, которые Гусь приберег для непростых ситуаций:

  • Гусь не боится пагинации — он может скролить страницы или прокликивать ссылки для получения нового контента. Вы можете даже научить Гуся кастомной пагинации;
  • Гусь умеет ходить между страницами, важно и грациозно;
  • Гусь может выполнять необходимые действия с клавиатурой и мышью для получения конкретного кусочка информации — у него очень шустрые лапки;
  • Гусь может преобразовать полученные результаты;
  • Гусь — умное, интеллигентное животное, вы можете рассказать ему новые фишки для парсинга и он начнет их применять — API для Гусь-парсера очень легко расширяется;
  • Гусь — животное общинное, за счет своей любви к nodejs, легко может жить на гусиной ферме и парсить миллионы сайтов единовременно;


Поставьте Гусю звезду — и он захватит мир ради вас github.com/redco/goose-parser
Поделиться с друзьями
-->

Комментарии (11)


  1. WST
    07.06.2016 08:24
    +4

    Надеюсь, это не разработка Алексея Бабушкина?


    1. GreyCat
      07.06.2016 08:55
      +4

      Пока и из всей статьи, и из поверхностного чтения документации абсолютно непонятно, чем это лучше сотни имеющихся альтернатив. Более-менее понятно, чем хуже: хотя бы необходимостью изучать очередной новый API, механизм описания правил и т.д. Интересно, есть какие-то преимущества и killer features, ради чего нужно бросить Scrapy, Grab, pjscrape, EDF, harvest/jarvest, upton, или даже какую-то когорту более высокоуровневых инструментов, где не надо вручную писать правила?


      1. ChALkeRx
        07.06.2016 10:38
        +4

        Исходя из текста статьи, я так понял, что гусями.


        Но в данный момент это претензия не к самой библиотеке, а скорее к методу подачи материала в статье. Код ещё не смотрел.


        1. GreyCat
          07.06.2016 13:45

          Я чуть-чуть посмотрел, пока чего-то суперинтересного не обнаружил. Масштабирования нет примерно никакого: ни слова о том, чтобы подключать какое-то внешнее хранилище URLов, индексы, очереди и т.д. Выживаемость, перезапускаемость и восстанавливаемость процесса тоже на первый взгляд примерно никакая: даже если очень захотелось и начал парсить пару миллионов страниц на одной машине и оно упало где-то в конце — начинаем все с самого начала. Автоматизации парсинга толком никакой не просматривается: для того, чтобы локализировать нужный кусок текста на странице нужно написать вручную некое декларативное описание координат вытаскиваемого. Никаких инструментов, чтобы генерировать такие штуки хотя бы визуально, тупо ткнув в браузере по элементу, вроде бы нет. Поддержки каких-то продвинутых методов стабилизации скачивания (поддержания собственного списка проксей, абьюзинг TOR ;), или хотя бы зацепления за ту же Crawlera) нет — есть максимум куцая ротация заранее заданного списка проксей.


          Вопрос, что же там есть уникального, чего нет нигде, остается.


  1. jonic
    07.06.2016 11:49
    +6

    Планируется цикл статей — этаких how-to в таком же стиле? :)


    1. maZahaca
      08.06.2016 10:58

      да :)


  1. Amareis
    07.06.2016 12:16

    parser.parse({ actions, rules }).then(console.log);


    Вот это разве не бросит исключение? Там вроде надо делать console.log.bind(console).


    1. ChALkeRx
      07.06.2016 12:48

      В браузере — бросит, в Node.js методы console прибайндены к console.


  1. IvanPanfilov
    07.06.2016 13:18
    +2

    {
    type: 'type', // тип действия — печатать
    text: 'гусыня', // Гусь будет искать гусынь, чтоб неплохо потоптаться
    scope: '.field[name=search]' // селектор елемента
    },
    {
    type: 'click',
    scope: 'button[type=submit]',
    waitForPage: true // если знаем что результаты откроются на новой странице
    }

    const rules = {
    scope: '.goose-babe'

    зачем изобретать новый язык?
    и чем это лучше querySelector?


    1. asavin
      08.06.2016 20:38

      Это не новый язык, это json, в котором селекторы Sizzle + какие-то действия, которые нужно совершить.


  1. Demogor
    13.06.2016 08:09

    Сама статья любопытна, стиль написания позабавил, спасибо.
    Что до личного опыта — из всех инструментов для базового парсинга, которые пробовал, пока больше всего понравился NW.js/Electron. Из коробки можно подключить тот же jQuery(а можно и ванильной js'кой) и строить DOM-дерево со всеми последующими плюшками.
    За счет контекста ноды легко контачится с Tor'ом, чуть сложнее — с Fiddler(что позволяет делать волшебные штуки вроде распределения списка прокси по каждому запросу либо изменений ответа сервера), из коробки умеет прокси/подмену заголовков(что тоже иногда весьма полезно) и прочие радости жизни. Отличная поддержка ES6 из коробки, все такое. Ну и визуальное отображение результатов/отладки никто не отменял, конечно же(удобно, когда приходится ковырять пакован обфусцированного js).