Парсинг ajax сайтов (Casperjs)

31.08.2016 03:25

Существуют некоторое количество сайтов, которые не хотят отдавать нормальную страницу по прямому запросу. Будь то защита от копирования, использование сорт оф ReactJS либо, как в моём случае, SSO технология. Встречайте: http://www.orthopaedicsandtraumajournal.co.uk.

Наша задача - скравлить все статьи с сайта. Автоматический php парсер тут обломался и я полез смотреть причину. Для начала скачиваем wget'ом какую-нибудь страницу, например:

wget "http://www.orthopaedicsandtraumajournal.co.uk/issue/S1877-1327(16)X0004-8"

Открываем её и видим следующее:

<!-- hidden iFrame for each of the SSO URLs -->
<div class="hidden">
    
        <iframe src="//acw.secure.jbs.elsevierhealth.com/SSOCore/update?utt=7a73f03e93ed651d4875d32-edc8135c83377a70">Your browser doesn't support iFrames!</iframe>
    
        <iframe src="//acw.sciencedirect.com/SSOCore/update?utt=7a73f03e93ed651d4875d32-edc8135c83377a70">Your browser doesn't support iFrames!</iframe>
    
        <iframe src="//acw.scopus.com/SSOCore/update?utt=7a73f03e93ed651d4875d32-edc8135c83377a70">Your browser doesn't support iFrames!</iframe>
    
        <iframe src="//acw.sciverse.com/SSOCore/update?utt=7a73f03e93ed651d4875d32-edc8135c83377a70">Your browser doesn't support iFrames!</iframe>
    
        <iframe src="//acw.mendeley.com/SSOCore/update?utt=7a73f03e93ed651d4875d32-edc8135c83377a70">Your browser doesn't support iFrames!</iframe>
    
        <iframe src="//acw.elsevier.com/SSOCore/update?utt=7a73f03e93ed651d4875d32-edc8135c83377a70">Your browser doesn't support iFrames!</iframe>
</div>

<noscript>
    <a href="http://www.orthopaedicsandtraumajournal.co.uk/action/consumeSharedSessionAction?JSESSIONID=aaageCSP07pBp-JRGRwBv&MAID=EG3%2FXcO6rdQJcCVEIjGplg%3D%3D&SERVER=WZ6myaEXBLGvmNGtLlDx7g%3D%3D&ORIGIN=501907480&RD=RD">Redirect</a>
</noscript>

<!-- redirect to the product page after all iFrames are rendered -->
<script>
    setTimeout(redirectFun,2000);
    var iFramesList = document.getElementsByTagName("iframe");
    var renderedIFramesCount = 0;
    var numberOfIFrames = iFramesList.length;
    for (var i = 0; i < iFramesList.length; i++) {
        var iFrame = iFramesList[i];
        bindEvent(iFrame, 'load', function(){
            renderedIFramesCount = renderedIFramesCount + 1;
            if (renderedIFramesCount >= numberOfIFrames)
            {
                redirectFun();
            }
        });
    }
    var doRedirect = true;
    function redirectFun() {
        if (doRedirect)
            window.location.href = "http://www.orthopaedicsandtraumajournal.co.uk/action/consumeSharedSessionAction?JSESSIONID=aaageCSP07pBp-JRGRwBv&MAID=EG3%2FXcO6rdQJcCVEIjGplg%3D%3D&SERVER=WZ6myaEXBLGvmNGtLlDx7g%3D%3D&ORIGIN=501907480&RD=RD";
        doRedirect = false;
    }

    function bindEvent(el, eventName, eventHandler) {
        if (el.addEventListener){
            el.addEventListener(eventName, eventHandler, false);
        } else if (el.attachEvent){
            el.attachEvent(eventName, eventHandler);
        }
    }
</script>

Клиенту отдаётся страница со скрытыми фреймами. Затем скрипт ждёт загрузки фреймов и только потом загружает реальную страницу. Ок, мы живём в 21 веки и у нас есть CasperJS!

Для начала пишем скриптик для сбора урлов всех статей с этого сайта:

var fs = require('fs');
var casper = require('casper').create();
var baseUrl = 'http://www.orthopaedicsandtraumajournal.co.uk';
var resultFileName = 'links.txt';
var nextPageSelector = '.prevIssue > a';
var linksSelector = '.detail h3 > a';
var pageUrl = 'http://www.orthopaedicsandtraumajournal.co.uk/issue/S0268-0890(05)X0085-8';

casper.start();

casper.then(function(){
  casper.repeat(200, function(){
    casper.thenOpen(pageUrl, function(){
      console.log(this.getCurrentUrl());
      
      aList = this.evaluate(function(selector) {
        var al = [];
        jQuery(selector).each(function() {
          al.push(jQuery(this).attr('href'));
          });
        return al;
      }, linksSelector);
      
      pageUrl = this.evaluate(function(selector){
        return document.querySelector(selector).href;
      }, nextPageSelector);
  
      var file = fs.open(resultFileName, 'a');
      aList.map(function(item) {
        file.write(baseUrl + item + "
");
      });
      file.close();
    });
  });
});

casper.run();

var baseUrl = .. - это url самого сайта, обязательно без закрывающего слеша. Он нам нужен чтобы сформировать правильные url, так как на сайте все ссылки используют относительные пути.
var pageUrl = .. - Адрес первой страницы на которой расположены ссылки на статьи.
var resultFileName = .. - Файл, куда будут сохраняться спарсенные урлы.
var nextPageSelector = .. - css селектор для ссылки, которая ведёт на следующую страницу.
var linksSelector = .. - css селектор по которому выбираются ссылки непосредственно на статьи.

Запускаем его и на выходе получаем файл со списком всех нужных урлов. Тут мы использовали casper.repeat(200 ... это значит скрипт повторится 200 раз, меняйте его по собственному усмотрению. Мы вынуждены использовать такую неудобную конструкцию потому что в casperjs нет никакого аналога while цикла.

Теперь пишем следующий скриптик, он по очереди откроет каждый урл и сохранит страницу в указанную папку, а чтобы было всё ещё красивее, положим рядом .csv файл в котором покажем, какой .html файл по какому урл был скачен. Код:

var fs = require('fs');
var casper = require('casper').create();
var inputFile = 'links.txt';
var outputDir = 'data/';

casper.start(inputFile, function(){
  var links = this.fetchText('pre').split("
");
  var csvFile = fs.open(outputDir + 'urls.csv', 'a');
  csvFile.write("url,file
");
  csvFile.close();
  
  this.each(links, function(self, link){
    self.thenOpen(link, function() {
      console.log(this.getCurrentUrl());
      var d = new Date();
      var pageFileName = d.getTime() + '.html';
      var csvFile = fs.open(outputDir + 'urls.csv', 'a+');
      csvFile.write(this.getCurrentUrl() + "," + pageFileName + "
");
      csvFile.close();
      var pageFile = fs.open(outputDir + pageFileName, 'w');
      pageFile.write(this.getHTML());
      pageFile.close();
      
    });
  });
});

casper.run();

var inputFile = .. - Путь к файлу с ссылками (создан прошлым скриптом)
var outputDir = .. - Папка, куда всё это сохранять.

На этом всё, остаётся только подождать, пока скрипт сам, автоматически сделает всю работу.



Переезд на новый сервер

В апреле этого года вышла ubuntu 16.04LTS, которая "из коробки" поддерживает php7. А в свете глобальных изменений в ядре php стало очень заманчиво переехать на обновлённую систему. Кроме этого, давно хотел перейти с apache на ngnix и с mysql на mariadb.

Тестирование производительности OpenSource СУБД

Так получилось, что для текущего проекта требуется очень много и быстро считать SELECT COUNT. На этапе разработки не задумывались о том насколько быстро умеют это делать современные системы управления базами данных.


(0) Комментариев