Перемещение между страницами и скрапинг, когда я работаю с Nightmare

Есть веб-сайт, содержащий страницу со списком из 25 записей, где каждая запись является ссылкой на страницу, содержащую некоторую информацию, которая мне нужна. Я хочу перейти на страницу со списком, а затем: 1) щелкнуть ссылку на первую запись 2) получить весь HTML-код 3) вернуться на страницу со списком (для этого есть кнопка) 4) повторить для каждого другого списка

Я также хотел бы сделать это максимально эффективно, что, как мне сказали, означает использование обещаний. Вот мой набросок кода, который не работает:

var Nightmare = require('nightmare');
var nightmare = Nightmare({ openDevTools: true, show: true })
var Xray = require('x-ray');
var x = Xray();
var resultArr = [];

nightmare
.goto(hidTestURL)
.wait(2500)
.click('input[name="propertySearchOptions:advanced"]') //start navigating to listing page
.wait(2500)
.type('input[name="propertySearchOptions:streetName"]', 'Main')
.wait(2500)
.select('select[name="propertySearchOptions:recordsPerPage"]', '25')
.wait(2500)
.click('input[name="propertySearchOptions:search"]') //at listing page
.wait(2500)
.then(function(){
  nightmare
  .click('a[href^="Property.aspx?prop_id=228645"]') //first entry
  .evaluate(function(){ //retrieve info
    var resultArr = [];
    resultArr.push(document.querySelector('html').innerHTML);
  })
})

nightmare
.click('a[id="propertyHeading_searchResults"]') //return to listing page
.evaluate(function(){
  return resultArr.push(document.querySelector('html').innerHTML); retrieve listing page info to show that it returned.
})
.then(function (resultArr) {
  console.log('resultArr', resultArr);
  x(resultArr[1], 'body@html') //output listing page html
    .write('results.json');
})

Это доходит до страницы со списком, а затем не продолжается дальше. Я также попробовал тот же код, но с return nightmare для каждого использования nightmare, кроме первого. Я видел несколько примеров, в которых использовалось return, но когда я это делал, код выдавал ошибку.

Я также попытался не включать третий nightmare (тот, что после пробела) и вместо этого попытался продолжить старый экземпляр кошмара, перейдя прямо к .click(), но это также вызвало ошибку.

Мне явно нужна помощь с синтаксисом и семантикой кошмара, но в Интернете не так много документации, кроме списка API. Кто-нибудь знает, как я могу сделать эту работу?


person Phylth    schedule 04.08.2016    source источник


Ответы (1)


Во-первых, вызов Nightmare в том виде, как он у вас есть — разбитый на две цепочки — вероятно не приведет к тому, что вы хотите. (Эта ветка комментариев является хорошим, хотя и длинным, учебником.) Память обслуживания, действия из второй цепочки будут поставлены в очередь сразу после первой, что приведет (вероятно) к нежелательному поведению. Вы сказали, что написали это немного по-другому - мне было бы любопытно увидеть это, звучит так, как будто это могло быть немного ближе.

Во-вторых, вы пытаетесь поднять resultArr в .evaluate(), что невозможно. Функция, переданная .evaluate(), преобразуется в строку и воссоздается внутри Electron — это означает, что вы потеряете окружающий контекст вокруг функции. Этот пример в nightmare-examples углубляется, если вам интересно.

В-третьих, и, возможно, это опечатка или мое недоразумение: ваш селектор href использует оператор start-with (^=), это преднамеренно? Должно ли это быть концом с ($=)?

В-четвертых, зацикливание асинхронных операций сложно. У меня складывается впечатление, что это тоже может быть камнем преткновения?

Имея все это в виду, давайте взглянем на изменение исходного сценария. По общему признанию, не проверено, так как у меня нет доступа к вашему тестовому URL-адресу, так что это немного от бедра:

var Nightmare = require('nightmare');
var nightmare = Nightmare({ openDevTools: true, show: true })
var Xray = require('x-ray');
var x = Xray();

nightmare
.goto(hidTestURL)
.wait(2500)
.click('input[name="propertySearchOptions:advanced"]') //start navigating to listing page
.wait(2500)
.type('input[name="propertySearchOptions:streetName"]', 'Main')
.wait(2500)
.select('select[name="propertySearchOptions:recordsPerPage"]', '25')
.wait(2500)
.click('input[name="propertySearchOptions:search"]') //at listing page
.wait(2500)
.evaluate(function(){
  //using `Array.from` as the DOMList is not an array, but an array-like, sort of like `arguments`
  //planning on using `Array.map()` in a moment
  return Array.from(
    //give me all of the elements where the href contains 'Property.aspx'
    document.querySelectorAll('a[href*="Property.aspx"]'))
    //pull the target hrefs for those anchors
    .map(a => a.href);
})
.then(function(hrefs){
  //here, there are two options:
  //  1. you could navigate to each link, get the information you need, then navigate back, or
  //  2. you could navigate straight to each link and get the information you need.
  //I'm going to go with #1 as that's how it was in your original script.

  //here, we're going to use the vanilla JS way of executing a series of promises in a sequence.
  //for every href in hrefs,
  return hrefs.reduce(function(accumulator, href){
    //return the accumulated promise results, followed by...
    return accumulator.then(function(results){
      return nightmare
        //click on the href
        .click('a[href="'+href+'"]')
        //get the html
        .evaluate(function(){
          return document.querySelector('html').innerHTML;
        })
        //add the result to the results
        .then(function(html){
          results.push(html);
          return results;
        })
        .then(function(results){
          //click on the search result link to go back to the search result page
          return nightmare
            .click('a[id="propertyHeading_searchResults"]')
            .then(function() {
              //make sure the results are returned
              return results;
            });
        })
    });
  }, Promise.resolve([])) //kick off the reduce with a promise that resolves an empty array
})
.then(function (resultArr) {
  //if I haven't made a mistake above with the `Array.reduce`, `resultArr` should now contain all of your links' results
  console.log('resultArr', resultArr);
  x(resultArr[1], 'body@html') //output listing page html
    .write('results.json');
});

Надеюсь, этого достаточно, чтобы вы начали.

person Ross    schedule 06.08.2016