Cheerio 选择的现有属性有时会返回 undefined使用 Puppeteer 获取 HTML

问题描述

我使用 Puppeteer 获取网站 HTML,然后使用 Cheerio 抓取数据。这是我的代码的一部分。它几乎每次都能正常工作,但有时我会从 companyAddress 和 companyIntro 中获取未定义。一开始,我以为可能是不同页面的差异造成的,但是即使我在不​​同的时间抓取同一个页面也会发生(大多数时候我得到了数据,但有时它是未定义的)。页面渲染成功,通过devtool确认属性及其值存在。我想知道背后的原因。会不会是Puppeteer fetch 的问题? Cheerio 代码是同步的,所以我不认为 Cheerio 是问题所在。我从来没有得到错误:无法获得未定义的 attr('profile'),所以这意味着有一个标题元素,但我得到错误:未定义的 substring()。这就是为什么我在它之前放了一个条件来检查。

const puppeteer = require('puppeteer')
const cheerio = require('cheerio')
const baseUrl = 'https://www.104.com.tw'

const sleep = (milisecond) => {
  return new Promise((resolve,reject) => setTimeout(resolve,milisecond))
}

const scrapeCompanyPage = async (dataList,page) => {
  for (let i = 0; i < dataList.length; i++) {
    await page.goto(dataList[i].companyUrl)
    const html = await page.content()
    const $ = cheerio.load(html)
    const header = $('div.header')
    //sometimes company data below is undefined,but header exists
    dataList[i].companyAddress = header.attr('address') ? header.attr('address') : null
    dataList[i].companyIntro = header.attr('profile') ? header.attr('profile').substring(0,50) : null 
    await sleep(1000)
  }
  return dataList
}

这部分代码抓取的网站是这样的:https://www.104.com.tw/company/1a2x6bk72b?jobsource=2018indexpoc 不同companyUrl内容不同,但结构相同。

下面是我要选择的 HTML 标签

<div data-v-690c5d70="" data-v-09405bf2="" class="header mb-4" productpictures="" custno="13000000010336" industrydesc="..." indcat="..." empno="30" capital="80" address="..." custlink="https://unnotech.com"profile="..." management="..." phone="..." fax="..." hrname="HR" lat="25.0755569" lon="121.5756586" news="" newslink="" linkmore="[object Object]" corpimage1="" corpimage3="" corplink2="" corplink1="" corplink3="" envpictures="[object Object],[object Object],[object Object]" historys="" addrnodesc="..." reporturl="//www.104.com.tw/question_admin/reaction.cfm? j=5070426e34463e6730323a632c2e365f2444a42252525256a47682e2987j48" postalcode=""
        >...</div>

解决方法

按Ctrl-U,你会看到主要内容的源代码是空的。该网站可能由 React、Vue 或其他 Javascript 库进行渲染。所以你需要等待元素出现。

但是如果我在 { "user1": 3,"user3": 2 } > Developer Tools > Network tab 中检查网站,重新加载页面,您将看到他们调用的 API 来获取这些元数据,例如地址、个人资料等。 .. 而且您可能不需要抓取 html。

,

那个链接上的那个有个人资料和地址属性,这样就不会在那里发生。

如果缺少该属性,您将获得未定义的信息,例如 $(div).attr('foo')

对于节点 14+,您可以使用可选的链接 ?操作员以避免这些问题:

dataList[i].companyIntro = header.attr('profile')?.substring(0,50)