Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 多个选择器上的木偶演员等待选择器_Javascript_Puppeteer_Screen Scraping - Fatal编程技术网

Javascript 多个选择器上的木偶演员等待选择器

Javascript 多个选择器上的木偶演员等待选择器,javascript,puppeteer,screen-scraping,Javascript,Puppeteer,Screen Scraping,我有一个傀儡控制一个网站,它有一个查找表单,可以返回一个结果,也可以返回一条“找不到记录”的消息。我怎么知道哪个被退回了? waitForSelector似乎一次只等待一个,而waitForNavigation似乎不起作用,因为它是使用Ajax返回的。 我正在尝试接球,但要想正确接球是很困难的,而且会让一切都慢下来 try { await page.waitForSelector(SELECTOR1,{timeout:1000}); } catch(err) { await

我有一个傀儡控制一个网站,它有一个查找表单,可以返回一个结果,也可以返回一条“找不到记录”的消息。我怎么知道哪个被退回了? waitForSelector似乎一次只等待一个,而waitForNavigation似乎不起作用,因为它是使用Ajax返回的。 我正在尝试接球,但要想正确接球是很困难的,而且会让一切都慢下来

try {
    await page.waitForSelector(SELECTOR1,{timeout:1000}); 
}
catch(err) { 
    await page.waitForSelector(SELECTOR2);
}
使任何元素都存在 您可以同时使用
querySelectorAll
waitFor
来解决此问题。使用带有逗号的所有选择器将返回与任何选择器匹配的所有节点

await page.waitFor(() => 
  document.querySelectorAll('Selector1, Selector2, Selector3').length
);

现在只返回
true
如果有某个元素,它不会返回哪个选择器匹配哪个元素

根据Abu Taher博士的建议,我得出以下结论:

// One of these SELECTORs should appear, we don't know which
await page.waitForFunction((sel) => { 
    return document.querySelectorAll(sel).length;
},{timeout:10000},SELECTOR1 + ", " + SELECTOR2); 

// Now see which one appeared:
try {
    await page.waitForSelector(SELECTOR1,{timeout:10});
}
catch(err) {
    //check for "not found" 
    let ErrMsg = await page.evaluate((sel) => {
        let element = document.querySelector(sel);
        return element? element.innerHTML: null;
    },SELECTOR2);
    if(ErrMsg){
        //SELECTOR2 found
    }else{
        //Neither found, try adjusting timeouts until you never get this...
    }
};
//SELECTOR1 found

如果木偶程序方法无法完成请求,它们可能会抛出错误。例如,如果选择器在给定时间段内与任何节点不匹配,page.waitForSelector(选择器[,options])可能会失败

对于某些类型的错误,Puppeter使用特定的错误类。这些类可通过require(“木偶演员/错误”)获得

支持的类列表:

处理超时错误的示例如下:

const {TimeoutError} = require('puppeteer/Errors');

// ...

try {
  await page.waitForSelector('.foo');
} catch (e) {
  if (e instanceof TimeoutError) {
    // Do something if this is a timeout.
  }
}

将上面的一些元素组合到一个helper方法中,我构建了一个命令,允许我创建多个可能的选择器结果,并处理第一个要解决的问题

/**
*@typedef{import('puppeter').ElementHandle}puppetereelementhandle
*@typedef{import('puppeter').Page}puppeterpage
*/
/**功能说明
@回调结果处理程序
@异步的
@param{puppetereerementhandle}元素匹配元素
@返回{Promise}可以返回任何内容,将被发送到HandlePossibleResults
*/
/**
*@typedef{Object}possibleoutcom
*@property{string}选择器触发此结果的选择器
*如果存在选择器,则将调用@property{OutcomeHandler}处理程序
*/
/**
*等待木偶演员页面上的多个选择器(结果),并在第一个出现时调用处理程序,
*结果处理程序应该按优先顺序排列,就像存在多个一样,只有第一个出现的处理程序
*将被调用。
*@param{puppeterpage}page puppeter页面对象
*@param{[possibleoutcom]}生成每个可能的选择器,以及要调用的处理程序。
*@returns{Promise}从结果处理程序返回结果
*/
异步函数处理可能的结果(第页,结果)
{
var outcomeSelectors=结果.map(结果=>{
返回结果选择器;
})。加入(‘,’);
返回页面。等待(结果选择器)
.然后(=>{
让等待项=[];
结果。forEach(结果=>{
让我们等待=第页。$(output.selector)
.然后(元素=>{
if(元素){
返回[结果,要素];
}
返回null;
});
等待。推(等待);
});
返回承诺。全部(可等待);
})
。然后(选中=>{
let found=null;
checked.forEach(check=>{
如果(!检查)返回;
如果(发现)返回;
让结果=检查[0];
让元素=检查[1];
设p=output.handler(元素);
发现=p;
});
发现退货;
});

}
我遇到了类似的问题,于是选择了这个简单的解决方案:

helpers.waitForAnySelector = (page, selectors) => new Promise((resolve, reject) => {
  let hasFound = false
  selectors.forEach(selector => {
    page.waitFor(selector)
      .then(() => {
        if (!hasFound) {
          hasFound = true
          resolve(selector)
        }
      })
      .catch((error) => {
        // console.log('Error while looking up selector ' + selector, error.message)
      })
  })
})
然后使用它:

const selector = await helpers.waitForAnySelector(page, [
  '#inputSmsCode', 
  '#buttonLogOut'
])

if (selector === '#inputSmsCode') {
  // We need to enter the 2FA sms code. 
} else if (selector === '#buttonLogOut') {
  // We successfully logged in
}

使用
Promise.race()
就像我在下面的代码片段中所做的那样,不要忘记
page.waitForSelector()中的
{visible:true}
选项

public async enterUsername(username:string) : Promise<void> {
    const un = await Promise.race([
        this.page.waitForSelector(selector_1, { timeout: 4000, visible: true })
        .catch(),
        this.page.waitForSelector(selector_2, { timeout: 4000, visible: true })
        .catch(),
    ]);

    await un.focus();
    await un.type(username);
}
公共异步enterUsername(用户名:字符串):承诺{ const un=等待承诺([ this.page.waitForSelector(选择器_1,{timeout:4000,visible:true}) .catch(), this.page.waitForSelector(选择器_2,{timeout:4000,visible:true}) .catch(), ]); 等待联合国的关注(); 等待联合国类型(用户名); }
进一步使用
Promise.race()
包装它,只需检查索引以了解更多逻辑:

//类型脚本
导出异步函数(承诺:承诺[]):承诺{
const indexedPromises:Array=promises.map((promise,index)=>newpromise((resolve)=>promise.then(()=>resolve(index)));
回报承诺。种族(indexedPromises);
}
//Javascript
导出异步函数(承诺){
const indexedPromises=promises.map((promise,index)=>newpromise((resolve)=>promise.then(()=>resolve(index)));
回报承诺。种族(indexedPromises);
}
用法:

const navOutcome=等待([
page.waitForSelector('SELECTOR1'),
page.waitForSelector('SELECTOR2')
]);
如果(导航输出===0){
//“选择器1”的逻辑
}else if(navigationOutcome==1){
//“选择器2”的逻辑
}

另一种简单的解决方案是从更CSS的角度来解决这个问题
waitForSelector
似乎遵循以下步骤。因此,本质上,您可以通过使用逗号来选择多个CSS元素

try {    
    await page.waitForSelector('.selector1, .selector2',{timeout:1000})
} catch (error) {
    // handle error
}

在Puppeter中,您可以简单地使用多个选择器,这些选择器由coma分隔,如下所示:

const foundElement = await page.waitForSelector('.class_1, .class_2');
返回的元素将是页面中找到的第一个元素的elementHandle

下一步,如果您想知道找到了哪个元素,您可以获得类名,如下所示:

const className = await page.evaluate(el => el.className, foundElement);
在您的情况下,类似于此的代码应该可以工作:

const foundElement = await page.waitForSelector([SELECTOR1,SELECTOR2].join(','));
const responseMsg = await page.evaluate(el => el.innerText, foundElement);
if (responseMsg == "No records found"){ // Your code here }

有趣的。。。所以,一旦我知道其中一个在那里,我就可以在很短的超时时间内使用try块了?是的。没错,这是一个绝妙的解决方案。事实上,如果promise.all(或allsetted)是“AND”逻辑的自然解决方案,那么promise.race应该被视为“or”的自然解决方案。