Как стать автором
Обновить

За баранкой пылесоса или умный Firefox

Время на прочтение5 мин
Количество просмотров4.2K
Браузер, который сам ходит по ссылкам, открывает/закрывает табы, парсит или сохраняет весь контент на файловую систему — интересно посмотреть на такое, не так ли? Лично мне интересно было создать что-то подобное. Никакой фантастики! В общем в очередной раз во мне проснулось нечто вроде программистской лени вдохновения и вместо того, чтобы писать обычный crawler (он же spider или по-простому парсер сайта) на PHP, Perl или Ruby, я начал разбираться, как это сделать на FireFox.

Подготовка


Для того, чтобы все это сделать мне нужна была JavaScript консоль, из которой я имел бы доступ к XUL DOM. Я написал очень простое расширение для FireFox, которое добавляет значок в нижний правый угол окна браузера

image

Кликнув по этому значку откроется окно в котором можно писать JavaScript код и управлять элементами браузера. Расширение можно скачать здесь.
Чтобы запустить скрипт жмем CTRL+Enter (фокус должен быть на элементе textbox).

Для тех кто в танке


Так как не все программисты знакомы с JavaScript объектами в самом FireFox-е, думаю немного теории будет полезно. По аналогии к HTML «передовыми игроками» являются window и document. Чтобы обратится к контенту (HTML DOM) нужно написать так
var links = window.content.document.getElementsByTagName('a');
// or just
var links = content.document.querySelectorAll('a');
alert(links.length)


* This source code was highlighted with Source Code Highlighter.


Еще очень важный момент — это как отловить момент загрузки HTML DOM, а также как работать с табами и браузером. Для обращения к элементу tabbrowser можно использовать глобальную переменную gBrowser. С помощью этого элемента можно манипулировать табами. Несколько примеров
// add new tab
var tab = gBrowser.addTab('http://habrahabr.ru');
gBrowser.selectedTab = tab;

// get browser element for tab
var browser = gBrowser.getBrowserForTab(tab);

// add listeners on page load
browser.addEventListener('DOMContentLoaded', function(event){
  // listener implementation
}, false);

// add listeners on HTML DOM loaded
browser.addEventListener('DOMContentLoaded', function(event){
  if (event.originalTarget instanceof HTMLDocument) {
    var document = event.originalTarget;
    // listener implementation
  }
}, false);

* This source code was highlighted with Source Code Highlighter.


Также думаю будет полезным научится сохранять веб страницу в файл
var tab = gBrowser.addTab('http://habrahabr.ru');
gBrowser.selectedTab = tab;
var browser = gBrowser.getBrowserForTab(tab);
browser.addEventListener('DOMContentLoaded', function(event){
  var document = event.originalTarget;
  if (document instanceof HTMLDocument && this.contentWindow.document == document) {
    var basename = document.location.href.replace(/\/+$/, ''),
       pos = basename.lastIndexOf('/');
    if (pos != -1) {
      basename = basename.substr(pos+1);
    }

    var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
    file.initWithPath('/tmp/'+basename);
    if (!file.exists()) {
      var chosen = new AutoChosen(file, makeFileURI(file));
      internalSave(document.location.href, document, null, null, document.contentType, false, null, chosen);
    }
  }
}, false)

* This source code was highlighted with Source Code Highlighter.


Дополнительное условие this.contentWindow.document == document фильтрует ситуации, когда на странице есть iframe элементы, а страницу сохранить нужно только раз и ту, которая нужна, а не из фрейма.
О функции internalSave (сохраняет файл) и классе AutoChosen (как я понял эмулирует выбор файла пользователем) можно почитать комментарии в исходном коде. Больше примеров работы с файлами здесь.

Дрессируем FireFox


Напишем скрипт, который научит FireFox входить под определенным пользователем на Хабр.
var Crawler = {
  habrahabr: function(username, password) {
    this.username = username;
    this.password = password;
  },
  onHTMLLoaded: function (browser, callback) {
    browser.addEventListener('DOMContentLoaded', function(event){
      var document = event.originalTarget;
      if (document instanceof HTMLDocument
        && this.contentWindow.document == document
      ) {
        this.removeEventListener('DOMContentLoaded', arguments.callee, false);
        callback.call(this, event, document);
      }
    }, false);
    return browser;
  }
}

Crawler.habrahabr.prototype = {
  url: 'http://habrahabr.ru/',
  openAndSignIn: function(inNewTab) {
    var tab = gBrowser.selectedTab, browser = null;
    if (inNewTab) {
      tab = gBrowser.addTab(this.url);
    } else {
      content.document.location = this.url;
    }
    browser = gBrowser.getBrowserForTab(tab);
    var that = this;
    Crawler.onHTMLLoaded(browser, function(event, document){
      document.location = document.querySelector('dl.panel-personal a').href;
      Crawler.onHTMLLoaded(this, function(event, document){
        var user  = document.getElementById('reg-f-username');
        user.value = that.username;
        document.getElementById('reg-f-password').value = that.password;
        user.form.querySelector('input[type="submit"]').click();
      });
    });
  }
};
var cr = new Crawler.habrahabr('serjoga', '***');
cr.openAndSignIn(true);


* This source code was highlighted with Source Code Highlighter.


В начале создаем объект Crawler, в который потом можно будет добавлять новые парсеры. Создаем конструктор для парсера habrahabr, в который передаем имя пользователя и пароль. Добавляем метод openAndSignIn в прототип объекта habrahabr. Этот метод умеет открывать Хабр и авторизироваться под заданным юзером.

P. S.: я часто автоматизирую воспроизведение тест-кейсов багов, очень забавно и не утомительно, в том случае когда нужно нажать кучу кнопок и ходить по 10 страницам еще и на 2 сайтах.
Теги:
Хабы:
Всего голосов 39: ↑31 и ↓8+23
Комментарии27

Публикации