【问题标题】:Moving between pages and scraping as I go with Nightmare使用 Nightmare 在页面之间移动和抓取
【发布时间】:2016-08-04 15:52:01
【问题描述】:

有一个网站包含一个包含 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');
})

这会到达列表页面,然后不再继续。我也尝试了相同的代码,但除了第一个之外,每次使用 nightmare 时都使用 return nightmare。我见过一些使用return 的例子,但是当我这样做时,代码抛出了一个错误。

我还尝试不包括第三个nightmare(空格后面的那个),而是尝试通过直接转到.click() 来继续旧的噩梦实例,但这也引发了错误。

我显然需要一些关于噩梦的语法和语义方面的帮助,但是除了 API 列表之外,在线文档并不多。有谁知道我怎样才能做到这一点?

【问题讨论】:

    标签: javascript html web-scraping promise nightmare


    【解决方案1】:

    首先,像你一样调用 Nightmare - 分成两条链 - 可能不会做你想做的事。 (This comment thread 是一个很好的 - 尽管很长 - 入门。)内存服务,来自第二个链的动作将在第一个链之后立即排队,导致(可能)不良行为。你说你的写法略有不同——我很想看看,听起来可能更接近一点。

    其次,您试图将resultArr 提升到.evaluate(),这是不可能的。传递给 .evaluate() 的函数在 Electron 内部被字符串化和重构——这意味着你将失去函数周围的环境上下文。如果你好奇的话,nightmare-examples 中的This example 会更深入一点。

    第三,也许这是一个错字或我误解的意图:您的href 选择器使用了starts-with (^=) 运算符,这是故意的吗?这应该是结尾($=)吗?

    第四,looping over asynchronous operations is tricky。我觉得这也可能是一个绊脚石?

    考虑到所有这些,让我们来看看修改您的原始脚本。诚然未经测试,因为我无权访问您的测试 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');
    });
    

    希望这足以让您入门。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-27
      • 2013-04-04
      • 1970-01-01
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多