Начало
И вот пришла моя очередь покупать автомобиль. Как это делают ребята с работы я видел. Заходят на сайт и следят за предложениями, ну кто постарше покупает газету и просматривает объявления. Все это однообразно и отвлекаться на сиденье, исследование и нажатие по ссылкам не хотелось. Хотелось просто что бы кто то делал это за меня, таких людей не нашлось. Значит надо было заставить делать все это компьютер.
Постановка задачи
Как я видел решение данной проблемы написать парсер, написать скрипт рассылки. Парсер должен собирать данные объявлений с сайта( сайтом я выбрал «из рук в руки»), а рассылка должна отправлять мне на e-mail сообщения о новом лоте. Текст сообщения должен был содержать:
- ссылку на объявление
- короткий текст объявления
- цена автомобиля
- место продажи автомобиля
Парсер должен отрабатывать каждые N-минут. А после его отработки должны приходить сообщения. Результаты парсинга записываются в базу, а после отправки сообщения на почту, помечать каждое объявления как отправленное. Не хочу же я одно и тоже тысячу раз видеть.
Парсер
Это был самый трудный шаг. Помнится мне, давным-давно, я писал парсер для одноклассников. На PHP. Сначала мне пришлось разобраться в самой соц. сети и понять каким магическим образом оно работает. Потом надо было запомнить все эти сессии, кукисы и последовательность перехода по ссылкам. А превращение всех этих мыслей в код? О ужас. Как же хотелось что бы все происходило понятно. Как хотелось не задумываться о том, что давным давно умеет делать браузер. Как хотелось, что бы моя любимая Наташа наконец-то поняла и главное увидела результаты работы, а не белый текст на черном фоне командной строки.
Вот поэтому и хотелось просто управлять браузером, что бы было понятно и видно. И тут, на сцену выходит Selenium WebDriver. С помощью которого можно управлять браузером, умея только грамотно выбрать селекторы(css, XPath). Логика работы парсера становится прозрачной. Нажать на кнопку, подождать, ввести данные, нажать на кнопку и все. И никаких кукисов. Ура! И главное я буду видеть все живьем, а не в логах.
Подготовительные работы
И так нам надо установить:
- Java — для запуска Seleniuma.
- Selenium — собственно сам Selenium.
- Node.JS — все будет писать на js, поэтому без ноды никак.
- Mongo DB — база данных.
Далее в папку с проектом надо будет установить несколько модулей для Node
- wd — для работы с Seleium'ом.
- async — для уменьшения вложенности нашего кода.
- mongoose — для работы с базой.
- swig — для формирования html файлов, которые отправляются на почту
- emailjs — для отправки сообщений на почту
- cron — для запуска наших скриптов каждые N-минут(секунд)
Напомню что установка модулей выглядит так:
npm install "имя модуля"
Теперь запустим наш selenium
java -jar "Путь к файлу с selenium'ом"
И сервер базы данных
mongod --dbpath "Путь к месту хранения базы"
Пишем парсер
Источник выбрал как уже сказал — «Из рук в руки». Теперь последовательность действий:
Отчищаем все куки
browser.deleteAllCookies();
Выбираем регион
Вот в это поле вводим регион, который нас интересует. Регион как и все другие параметры описываем в объекте, который будет указан ниже. Ввод региона и нажатие можно описать следующим псевдокодом:
//находи инпут для ввода региона
browser.elementByCss(LOCATOR.cssPath)
.then(function(el){
//набираем регион
return el.type(OPTION.region);
})
.then(function(){
//меняем локатор на первый город в списке
LOCATOR.className = '';
LOCATOR.cssPath = '.b-searchRegion > ul:nth-child(1) > li:nth-child(1)';
return browser.elementByXPath('//span[contains(text(), "' + OPTION.region + '")]');
})
.then(function(el){
//нажимаем на найденный элемент
el.click();
});
Дальше выбираем раздел(мне нужен «Легковые автомобили»)
Описать можно просто нажатием на ссылку содержащую определенный текст
//ждем несколько секунд
browser.waitForVisibleByPartialLinkText(OPTION.category,OPTION.elWait)
.then(function(){
//возврат элемента содержащего текст
return browser.elementByPartialLinkText(OPTION.category);
})
.then(function(el){
//нажимаем на найденный элемент
el.click();
})
Теперь заключительная часть установки параметров для поиска, это нажатие на кнопку «больше параметров», ввести цену, год выпуска и остальные параметры. Все это можно посмотреть в видео.
Как видно все очень просто находим элемент, узнаем его уникальный локатор и нажимаем, вводим или оставляем его в покое.
Собственно, теперь необходимо собрать данные. Нажимаем на кнопку «Показать», и парсим данные. Получение текста выглядит очень просто
//получаем элемент содержащий текст
browser.elementByXPathOrNull(locationXPath)
.then(function(el){
if(el) {
//получаем текст
return el.text();
}
else{
cb('Нет такого элемента - ' + locationXPath);
}
})
Стоп-сигналом для сбора данных служит отсутствие вот такой вот синей стрелочки «вправо» на странице результатов:
После сбора данных записываем их базу. И закрываем браузер.
Кстати вот объект который описывает параметры для поиска автомобиля.
OPTION = {
region : 'Нижний Новгород',//регион поиска
category: 'Легковые автомобили',//категория поиска
price : {from : 0 , to : 1800000},//цена
cy : 'RUR',//валюта
releaseYear : {from : 2010, to : 2013},//год выпуска
mileage : {from : 0 , to : 99000 },//пробег
mark : ['BMW','ВАЗ', 'Audi','Hyundai'],//марка автомобиля
model : ['X1', 'X3', 'X5'],//модель автомобиля
carcass : ['седан', 'хэтчбек'],//кузов автомобиля
transmisson : ['автоматическая', 'механическая'],//трансмиссия
motor : ['бензин'],//тип двигателя
gear : ['задний','передний','постоянный полный','подключаемый полный'],//привод
photo : false,//с фото
video : false,//с видео
district : ['Заречный','Нагорный'],//jrheuf
area : ['Автозаводский', 'Канавинский', 'Ленинский'],//районы
metro : { lines : ['Автозаводская', 'Сормовская'], //линии метро
station : ['Горьковская м.', 'Пролетарская м.']//станции метро
},
source : ['любой'],//источник объявлений
submitted : ['вчера и сегодня'],//время подачи объявления
ajaxWaitMilisec : 2000,//время ожидания ajax мл.сек
elWait : 3000//время ожидания элемента мл.сек
},
Работа с базой
Структура базы следующая
MONGODBSCHEMA : {
title : String, //короткое описание объявления
link : {type : String , unique : true},//ссылка на объявление
price : String,//цена автомобиля
location : String,//место продажи автомобиля
phone : String,//номер телефона подавшего объявления*
text : String, //полное описание объявления*
images : Array,//ссылки на машину*
sms : {type : Boolean, default : false }, //отправилась ли мне смс с этим объявлением*
email : {type : Boolean, default : false }//отправилось ли мне сообщение на e-mail с этим объявлением
}
* — помечены поля которые планировались использоваться, но я решил отказаться от них. В дальнейшем могут понадобиться.
Рассылаем оповещения на почту
Это самые простой момент. Используем для этого модуль emailjs. Выбираем все документы из базы у которых поле «email» установлено в «false». Отправляем на на мой почтовый ящик. Изменяем свойство«email» на «true» у отправленных. Открываем приложение телефона которое отображает наши письма и изучаем подходящие.
Выполняем все каждые N-минут
Используем для этого модуль cron. Запускаем каждые 20 минут сначала парсер, а затем рассылку писем.
Вот и все
Теперь я слежу за продажей автомобилей тогда, когда у меня под боком телефон. И нету неприятного осадка от сломанного
А еще я хочу сказать огромное спасибо своей будущей жене Наташе, за то что она не против всех этих моих сумасшедших идей с домашним программирование, и за то что у нее такой бодрящий и милый смех.
Автор: thrtuk