Парсинг сайта при помощи PhantomJS + CasperJS
By Firepush |
В данной статье я расскажу о том, как при помощи скриптового браузера PhantomJS со связкой с CasperJS мне удалось собрать ссылки на RSS-ленты нужных сайтов. Начало истории можно узнать в предыдущей статье про алгоритм нахождения тем новостей.
О существовании подобных инструментов я узнал совсем недавно из свежей статьи на Хабре про SlimerJS. Последний отличается от Фантома другим браузерным движком и тем, что видно окно браузера. Изучив тему внимательней, я понял что CasperJS значительно облегчает написание кода. Но сделать так, чтобы Slimer заработал вместе с ним, у меня не получилось. Плюнув, я установил Фантом – все пошло без проблем.
Вся прелесть в том, что нам не нужно имитировать браузер, так как мы им и являемся! Вся возня с куками, прописыванием мета-информации и прочим, как при работе с curl – в прошлом. Некоторое время я поупражнялся с примерами из FAQ, пробежался вдоль по документации CasperJS и приступил.
Но немного отвлечемся. Напомню, что на руках у меня был список из более чем двух тысяч ссылок на самые разные ресурсы из Яндекс-Новостей. Задача: имея этот список, получить список ссылок на rss-ленты. От парсинга яндекс-подписок я отказался по причинам, указанным в прошлой статье. Удивительно, что мне не сразу пришла мысль, что помимо кривых яндекс-подписок, есть другие крутые RSS-читалки, учитывая что сам активно пользуюсь Feedler. А что, если они тоже предоставляют ссылки на rss-ленты?
В первую очередь посмотрел упомянутый Feedler. Отпадает – rss ссылок не дает. Стал гуглить и почти сразу наткнулся на digg.com. Минималистичный дизайн сразу приглянулся. Зарегистрировался, проверил – идеально! Помимо того, что определяет 90% всех ссылок, делает это почти всегда верно, причем, если находит несколько лент, показывает все по убыванию важности (тоже весьма точно). Но и это не все! Как оказалось, не нужно даже каждый раз при поиске вписывать ссылку в поисковую форму: искомое слово подставляется get-параметром:
voBhqPa
Просто подарок судьбы!
Итак, формализуем. Скрипт должен авторизоваться на сайте, потом по циклу генерировать урл в формате, как на скрине выше, открывать страницу и парсить ссылки на RSS. Ничего сложного.
Но, как всегда, возникли некоторые затруднения. Логин только через fb, twitter или g+. А еще иногда процесс парсинга непонятно почему прерывался. Поскольку окно браузера не отображается в Фантоме, я долго не мог понять в чем дело. На помощь пришла функция, позволявшая сделать скриншот браузера и сохранить в корень программы. Так я определил, что во всем виновато всплывающее окно, предлагающее похвалить Digg в соцсетях, и блокирующее все действия. Благо, это было легко исправить, указав, что, если на странице есть элемент с определенным css-селектором, нужно нажать на него (крестик этого окна) и продолжаем дальше.
Код программы привожу ниже:
//Login with google
var username = ‘**********’, password = ‘*****’;
var casper = require(‘casper’).create();
casper.start(‘http://digg.com/session/google?display=page&next=%2Freader’, function () {
this.fill(‘form#gaia_loginform’, {
‘Email’: username,
‘Passwd’: password
}, true); // submit
});
//check login status
casper.then(function() {
this.echo(this.getCurrentUrl());
this.capture(‘scr2.png’, {
top: 0,
left: 0,
width: 1920,
height: 1080
});
});
//huge array of urls
var urls = [
“newizv.ru”,
“finance.obozrevatel.com”,
“echomsk.spb.ru”,
“kurs.ru”,
//about 2000 urls here
“42.tut.by”,
“report.az”,
]
//main code
while(urls.length) {
var url = urls.shift();
var link = ‘http://digg.com/reader/search/’ + url;
casper.thenOpen(link, function() {
this.echo(this.getCurrentUrl()); //show in console current url
this.wait(2000); //waiting for result in page
});
//check if popup exists, if true – close it.
casper.then(function() {
if (this.exists(“div.btn-modal-close”)) {
this.click(“div.btn-modal-close”);
}
});
casper.then( function() {
this.echo(this.fetchText(‘div.discovery-feed-item-feed-url’));
var res = this.fetchText(‘div.discovery-feed-item-feed-url’) + ‘\n’;
var fs = require(‘fs’);
fs.write(“myfile.txt”, res, ‘a’); // a – append to file
}
);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Login with google
var username = ‘**********’, password = ‘*****’;
var casper = require(‘casper’).create();
casper.start(‘http://digg.com/session/google?display=page&next=%2Freader’, function () {
this.fill(‘form#gaia_loginform’, {
‘Email’: username,
‘Passwd’: password
}, true); // submit
});
//check login status
casper.then(function() {
this.echo(this.getCurrentUrl());
this.capture(‘scr2.png’, {
top: 0,
left: 0,
width: 1920,
height: 1080
});
});
//huge array of urls
var urls = [
“newizv.ru”,
“finance.obozrevatel.com”,
“echomsk.spb.ru”,
“kurs.ru”,
//about 2000 urls here
“42.tut.by”,
“report.az”,
]
//main code
while(urls.length) {
var url = urls.shift();
var link = ‘http://digg.com/reader/search/’ + url;
casper.thenOpen(link, function() {
this.echo(this.getCurrentUrl()); //show in console current url
this.wait(2000); //waiting for result in page
});
//check if popup exists, if true – close it.
casper.then(function() {
if (this.exists(“div.btn-modal-close”)) {
this.click(“div.btn-modal-close”);
}
});
casper.then( function() {
this.echo(this.fetchText(‘div.discovery-feed-item-feed-url’));
var res = this.fetchText(‘div.discovery-feed-item-feed-url’) + ‘\n’;
var fs = require(‘fs’);
fs.write(“myfile.txt”, res, ‘a’); // a – append to file
}
);
};
Результат на выходе получился не самым читаемым, так как fetchText не поддерживает разделителей. Впрочем, все ссылки все равно начинаются на http, поэтому с их последующим разделением проблем не было.