所以我试图在Twitter上抓取一个页面来获取推文:

我想获取元素;单独获取文本、图像、视频,但我不断获取空数组

//Scraper.js
const puppeteer = require('puppeteer');
const fs = require('fs');

async function scrapeTwitter() {
  try {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://twitter.com/coindesk');
    await page.waitForLoadState('networkidle2');


    const html = await page.content();
    const $ = cheerio.load(html);
    const tweets = $('[data-testid="tweet"]');
    
    const posts = [];
    tweets.each(function () {
      const text = $(this).find('.tweet-text').text().trim();
      const image = $(this).find('.tweet-image').attr('src');
      const video = $(this).find('.tweet-video').attr('src');
      posts.push({ text, image, video });
    });
    
      
    await browser.close();
    
    return posts;
  } catch (error) {
    console.error('Error scraping Twitter:', error);
    return [];
  } 
}

module.exports = scrapeTwitter;

推荐答案

I wouldn't use Cheerio with Puppeteer. Puppeteer已经使用实时页面,因此将整个内容序列化并转储到静态HTML解析器中是没有意义的.如果您想抓取更多推文,您需要向下滚动,然后重新快照整个页面,以保持Cheerio与动态网站同步.

还有,avoid spurious waitForLoadState calls.goto已经有一个{waitFor: "networkidle2"}选项,所以我会使用它,而不是在它之后再加上第二个选项,这可能会导致奇怪的问题.

除此之外,您的 Select 器不会为我返回任何内容.也许可以try 类似以下操作:

const puppeteer = require("puppeteer"); // ^22.6.0

const url = "<Your URL>";

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  const ua =
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36";
  await page.setUserAgent(ua);
  await page.setRequestInterception(true);
  const blockedResources = ["stylesheet", "font"];
  page.on("request", req => {
    if (blockedResources.includes(req.resourceType())) {
      req.abort();
    } else {
      req.continue();
    }
  });
  await page.goto(url, {waitUntil: "domcontentloaded"});
  const tweetSel = '[data-testid="tweet"]';
  await page.waitForSelector(tweetSel);
  const data = [];

  for (let i = 0; i < 50 && data.length < 20; i++) {
    const preLen = await page.$$eval(
      tweetSel,
      els => els.length
    );
    await page.keyboard.press("PageDown");

    try {
      await page.waitForFunction(
        `document.querySelectorAll('${tweetSel}').length > ${preLen}`,
        {timeout: 2_000}
      );
    } catch (err) {
      // ...
    }

    const chunk = await page.$$eval(tweetSel, els =>
      els.map(el => ({
        text: el
          .querySelector('[data-testid="tweetText"]')
          .textContent.trim(),
        photo: el
          .querySelector('[data-testid="tweetPhoto"] img')
          ?.getAttribute("src"),
      }))
    );

    for (const e of chunk) {
      if (data.every(f => f.text !== e.text)) {
        data.push(e);
      }
    }
  }

  console.log(data);
  console.log(data.length);
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

拦截API响应可能比touch 多姆更可靠;这里还有很大的改进空间,而且时间在某种程度上是随意的猜测.

披露:我是链接博客文章的作者.

Node.js相关问答推荐

Windows上使用ES6+的OpenAPI规范的Express服务器不接受嵌套路由'

为什么在导出的函数中调用node-sqlite3中的数据库方法时不起作用?

如何在MEVN堆栈中结合创建和更新表单流程?

如何从基于JSON的HTML/SCSS模板生成PDF?

mongodb首先自连接,然后根据某些条件与另一个表连接

如何在Node.js的telegraf.js命令中添加参数?

有没有办法判断 UUID 是否是使用 node.js 中的特定命名空间生成的?

$not 的聚合版本是什么?

在 MacOS Ventura 上使用 NVM 安装 node ?

看起来这段代码try GET 请求发送的值变为空白

NestJS TypeORM 可选查询不起作用

bash:npm:找不到命令?

如何在 Node.js 的 console.log() 中创建换行符

使用 Node.js 在内存中缓冲整个文件

如何将`yarn.lock`与`package.json`同步?

JavaScript 异步编程:promise 与生成器

如何在 MongoDB 中查询引用的对象?

Node.js, require.main === 模块

Express.js:没有这样的文件或目录

什么时候应该将函数存储到变量中?