【问题标题】:How can I dynamically inject functions to evaluate using Puppeteer?如何动态注入函数以使用 Puppeteer 进行评估?
【发布时间】:2018-06-20 19:16:59
【问题描述】:

我将Puppeteer 用于无头 Chrome。我希望评估页面内的一个函数,该函数使用其他函数的一部分,在其他地方动态定义。

下面的代码是一个最小的例子/证明。实际上functionToInject()otherFunctionToInject() 更复杂,需要页面DOM。

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(someURL);       

var functionToInject = function(){
    return 1+1;
}

var otherFunctionToInject = function(input){
    return 6
}

var data = await page.evaluate(function(functionToInject, otherFunctionToInject){
    console.log('woo I run inside a browser')
    return functionToInject() + otherFunctionToInject();
});

return data

当我运行代码时,我得到:

错误:评估失败:TypeError:functionToInject 不是函数

据我所知:functionToInject 没有被传递到页面的 JS 上下文中。但是如何将它传递到页面的 JS 上下文中呢?

【问题讨论】:

标签: javascript headless-browser puppeteer google-chrome-headless


【解决方案1】:

您可以使用addScriptTag向页面上下文添加功能:

const browser = await puppeteer.launch();
const page = await browser.newPage();

function functionToInject (){
    return 1+1;
}

function otherFunctionToInject(input){
    return 6
}

await page.addScriptTag({ content: `${functionToInject} ${otherFunctionToInject}`});

var data = await page.evaluate(function(){
    console.log('woo I run inside a browser')
    return functionToInject() + otherFunctionToInject();
});

console.log(data);

await browser.close();

这个例子是用字符串连接解决这个问题的一种肮脏的方式。在addScriptTag 方法中使用urlpath 会更干净。


或者使用exposeFunction(但现在函​​数被包装在Promise中):

const browser = await puppeteer.launch();
const page = await browser.newPage();

var functionToInject = function(){
    return 1+1;
}

var otherFunctionToInject = function(input){
    return 6
}

await page.exposeFunction('functionToInject', functionToInject);
await page.exposeFunction('otherFunctionToInject', otherFunctionToInject);

var data = await page.evaluate(async function(){
    console.log('woo I run inside a browser')
    return await functionToInject() + await otherFunctionToInject();
});

console.log(data);

await browser.close();

【讨论】:

  • 我不知道——我从不以这种方式使用它。我用exposeFunction 添加了另一个解决方案。
  • 我知道感谢是不必要的,但无论如何我只想说出来 - 这是一个非常全面的答案,包含示例和文档链接。
  • 另外,path 看起来也适合 addScriptTag - 无需自定义 URL 架构来保持注入的内容分开。
  • 很高兴知道使用第一种方法(addScriptTag),函数在浏览器中执行。使用第二种方法 (exposeFunction),该函数在 Node.js 中执行。如果你需要将 DOM 元素传递给函数,第一种方法是要走的路。
【解决方案2】:

工作示例accessible by link,在同一个 repo 中你可以看到被测试的组件。

it("click should return option value", async () => {
    const optionToReturn = "ClickedOption";

    const page = await newE2EPage();
    const mockCallBack = jest.fn();

    await page.setContent(
      `<list-option option='${optionToReturn}'></list-option>`
    );

    await page.exposeFunction("functionToInject", mockCallBack); // Inject function
    await page.$eval("list-option", (elm: any) => {
      elm.onOptionSelected = this.functionToInject;  // Assign function
    });
    await page.waitForChanges();

    const element = await page.find("list-option");
    await element.click();
    expect(mockCallBack.mock.calls.length).toEqual(1); // Check calls
    expect(mockCallBack.mock.calls[0][0]).toBe(optionToReturn); // Check argument
  });

【讨论】:

    【解决方案3】:

    您还可以使用page.exposeFunction(),这将使您的函数返回Promise(需要使用asyncawait)。发生这种情况是因为您的函数将不是 running inside your browser,而是在您的 nodejs 应用程序中,并且它的结果将在浏览器代码中来回发送。

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto(someURL);       
    
    var functionToInject = function(){
        return 1+1;
    }
    
    var otherFunctionToInject = function(input){
        return 6
    }
    
    await page.exposeFunction("functionToInject", functionToInject)
    await page.exposeFunction("otherFunctionToInject", otherFunctionToInject)
    
    var data = await page.evaluate(async function(){
        console.log('woo I run inside a browser')
        return await functionToInject() + await otherFunctionToInject();
    });
    
    return data
    

    相关问题:

    1. exposeFunction() does not work after goto()
    2. exposed function queryseldtcor not working in puppeteer
    3. How to use evaluateOnNewDocument and exposeFunction?
    4. exposeFunction remains in memory?
    5. Puppeteer: pass variable in .evaluate()
    6. Puppeteer evaluate function
    7. allow to pass a parameterized funciton as a string to page.evaluate
    8. Functions bound with page.exposeFunction() produce unhandled promise rejections
    9. How to pass a function in Puppeteers .evaluate() method?
    10. Why can't I access 'window' in an exposeFunction() function with Puppeteer?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 2021-09-25
      • 1970-01-01
      • 2018-07-05
      相关资源
      最近更新 更多