【问题标题】:Continue on Null Value of Result (Nodejs, Puppeteer)继续结果的空值(Nodejs,Puppeteer)
【发布时间】:2021-07-31 20:52:02
【问题描述】:

我刚开始玩 Puppeteer (Headless Chrome) 和 Nodejs。我正在抓取一些测试站点,当所有值都存在时,一切正常,但如果缺少该值,我会收到如下错误:

Cannot read property 'src' of null(所以在下面的代码中,前两遍可能有所有值,但第三遍没有图片,所以它只是出错了)。

在我使用 if(!picture) continue; 之前,但我认为由于 for 循环,它现在无法正常工作。

任何帮助将不胜感激,谢谢!

for (let i = 1; i <= 3; i++) {
//...Getting to correct page and scraping it three times
  const result = await page.evaluate(() => {
      let title = document.querySelector('h1').innerText;
      let article = document.querySelector('.c-entry-content').innerText;
      let picture = document.querySelector('.c-picture img').src;

      if (!document.querySelector('.c-picture img').src) {
        let picture = 'No Link';     }  //throws error

      let source = "The Verge";
      let categories = "Tech";

      if (!picture)
                continue;  //throws error

      return {
        title,
        article,
        picture,
        source,
        categories
      }
    });
}

【问题讨论】:

  • Unrelated 如果您的变量没有改变使用 const 而不是 let

标签: javascript node.js puppeteer


【解决方案1】:
let picture = document.querySelector('.c-picture img').src;

if (!document.querySelector('.c-picture img').src) {
    let picture = 'No Link';     }  //throws error

如果没有图片,那么document.querySelector()返回null,它没有src属性。在尝试读取 src 属性之前,您需要检查您的查询是否找到了一个元素。

将 null-check 移到函数顶部还有一个额外的好处,即当您无论如何都打算退出时节省不必要的计算。

async function scrape3() {
  // ... 
  for (let i = 1; i <= 3; i++) {
  //...Getting to correct page and scraping it three times
    const result = await page.evaluate(() => {
        const pictureElement = document.querySelector('.c-picture img');

        if (!pictureElement) return null;

        const picture = pictureElement.src;
        const title = document.querySelector('h1').innerText;
        const article = document.querySelector('.c-entry-content').innerText;

        const source = "The Verge";
        const categories = "Tech";

        return {
          title,
          article,
          picture,
          source,
          categories
        }
    });

    if (!result) continue;

    // ... do stuff with result
  }

回答评论问题:“有没有办法跳过任何空白,然后返回其余部分?”

是的。在尝试从中读取属性之前,您只需要检查每个可能丢失的元素是否存在。在这种情况下,我们可以省略提前返回,因为您总是对所有结果感兴趣。

async function scrape3() {
  // ...
  for (let i = 1; i <= 3; i++) {
    const result = await page.evaluate(() => {
        const img = document.querySelector('.c-picture img');
        const h1 = document.querySelector('h1');
        const content = document.querySelector('.c-entry-content');

        const picture = img ? img.src : '';
        const title = h1 ? h1.innerText : '';
        const article = content ? content.innerText : '';
        const source = "The Verge";
        const categories = "Tech";

        return {
          title,
          article,
          picture,
          source,
          categories
        }
    });
    // ... 
  }
}

进一步的想法

由于我仍然在这个问题上,让我更进一步,并使用您可能感兴趣的一些更高级别的技术对其进行一些重构。不确定这是否正是您所追求的,但它应该给你一些关于编写更易于维护的代码的想法。

// Generic reusable helper to return an object property
// if object exists and has property, else a default value
// 
// This is a curried function accepting one argument at a
// time and capturing each parameter in a closure.
//
const maybeGetProp = default => key => object =>
  (object && object.hasOwnProperty(key)) ? object.key : default

// Pass in empty string as the default value
//
const getPropOrEmptyString = maybeGetProp('')

// Apply the second parameter, the property name, making 2
// slightly different functions which have a default value
// and a property name pre-loaded. Both functions only need
// an object passed in to return either the property if it
// exists or an empty string.
//
const maybeText = getPropOrEmptyString('innerText')
const maybeSrc = getPropOrEmptyString('src')

async function scrape3() {
  // ...

  // The _ parameter name is acknowledging that we expect a
  // an argument passed in but saying we plan to ignore it.
  //
  const evaluate = _ => page.evaluate(() => {

    // Attempt to retrieve the desired elements
    // 
    const img = document.querySelector('.c-picture img');
    const h1 = document.querySelector('h1')
    const content = document.querySelector('.c-entry-content')

    // Return the results, with empty string in
    // place of any missing properties.
    // 
    return {
      title: maybeText(h1),
      article: maybeText(article),
      picture: maybeSrc(img),
      source: 'The Verge',
      categories: 'Tech'
    }
  }))

  // Start with an empty array of length 3
  // 
  const evaluations = Array(3).fill()

    // Then map over that array ignoring the undefined
    // input and return a promise for a page evaluation
    //
    .map(evaluate)

  // All 3 scrapes are occuring concurrently. We'll
  // wait for all of them to finish.
  //
  const results = await Promise.all(evaluations)

  // Now we have an array of results, so we can 
  // continue using array methods to iterate over them
  // or otherwise manipulate or transform them
  // 
  results
    .filter(result => result.title && result.picture)
    .forEach(result => {
      //
      // Do something with each result
      // 
    })
}

【讨论】:

  • 感谢您的回复!已实施,但我得到SyntaxError: Illegal continue statement: no surrounding iteration statement
  • 哦,是的。那讲得通。我几乎不再使用for 循环(更喜欢纯高阶函数并使用map 之类的数组方法),所以我忘记了原始循环的一些特质。这里的问题是continue 在一个异步运行的函数内部,所以当尝试continue 时,基本上循环不在调用堆栈中。我更新了示例以将continue 拉出到循环的根块中。我们仍会提前退出该函数,但使用 return null 而不是 continue
  • 太棒了,我午饭后要试试这个!快速提问,所以如果没有 src,它不会返回任何东西吗?有没有办法跳过任何空白,然后返回其余部分?感谢一百万的帮助!
  • 是的,该示例提前退出并仅返回用于触发 continuenull,因为如果缺少 src,您的原始代码似乎试图退出。我添加了一个替换示例,它只为丢失的道具返回空字符串,另一个示例为您提供了一些更高级的想法。
  • 我尝试使您的解决方案适应我的脚本,其中我在 page.evaluate 中有一个 Array.from 结构。喜欢:Array.from(document.querySelectorAll('article.section-wrap section'), value =&gt; ({ nom: value.querySelector('h1.title').innerText.trim(), ... 有没有办法获取空值的特定字符串,我尝试了几种方法都没有成功。也许一些三元运算符语法可能是可能的,但我很困惑。
【解决方案2】:

Try-catch 对我有用:

try {
    if (await page.$eval('element')!==null) {
        const name = await page.$eval('element')
    }
}catch(error){
     name = ''
}

【讨论】:

  • 你好,你错过了page.$eval('element)' 标记
猜你喜欢
  • 2020-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-16
  • 2018-08-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多