在我使用 Nightmare 时在页面之间移动和抓取
Moving between pages and scraping as I go with Nightmare
有一个网站包含一个包含 25 个条目列表的页面,其中每个条目都是 link 到包含我需要的一些信息的页面。我想进入列表页面,然后:
1) 点击 link 进入第一个条目
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
(空白 space 之后的那个),而是试图通过直接进入 .click()
来继续旧的噩梦实例,但这也引发了错误。
我显然在 nightmare 的语法和语义方面需要一些帮助,但是除了 API 列表之外,在线文档并不多。有谁知道我该怎么做?
首先,像你拥有的那样调用 Nightmare - 分成两条链 - 可能 不会做你想做的事。 (This comment thread 是一本很好的入门读物,虽然很长。)内存服务,来自第二条链的操作将在第一条链之后立即排队,导致(可能)出现不良行为。你说你写的略有不同 - 我很想看看它,听起来它可能更接近一点。
其次,您试图在 .evaluate()
中提升 resultArr
,这是不可能的。传递给 .evaluate()
的函数在 Electron 内部被字符串化和重构——这意味着您将失去函数周围的环境上下文。 This example in nightmare-examples
深入一点,如果你好奇的话。
第三,也许这是一个打字错误或我的误解意图:您的 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');
});
希望这足以让您入门。
有一个网站包含一个包含 25 个条目列表的页面,其中每个条目都是 link 到包含我需要的一些信息的页面。我想进入列表页面,然后: 1) 点击 link 进入第一个条目 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
(空白 space 之后的那个),而是试图通过直接进入 .click()
来继续旧的噩梦实例,但这也引发了错误。
我显然在 nightmare 的语法和语义方面需要一些帮助,但是除了 API 列表之外,在线文档并不多。有谁知道我该怎么做?
首先,像你拥有的那样调用 Nightmare - 分成两条链 - 可能 不会做你想做的事。 (This comment thread 是一本很好的入门读物,虽然很长。)内存服务,来自第二条链的操作将在第一条链之后立即排队,导致(可能)出现不良行为。你说你写的略有不同 - 我很想看看它,听起来它可能更接近一点。
其次,您试图在 .evaluate()
中提升 resultArr
,这是不可能的。传递给 .evaluate()
的函数在 Electron 内部被字符串化和重构——这意味着您将失去函数周围的环境上下文。 This example in nightmare-examples
深入一点,如果你好奇的话。
第三,也许这是一个打字错误或我的误解意图:您的 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');
});
希望这足以让您入门。