我试图从https://www.nba.com/stats/teams/isolation?PerMode=Totals&TypeGrouping=offensive中抓取一个表,但总是得到空数组。我想用类Crom_body__UYOcU抓取tbody的每个tr中的每个td
我使用nodejs和puppeteer,下面是我的代码
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.nba.com/stats/teams/isolation?PerMode=Totals&TypeGrouping=offensive');
const data = await page.evaluate(() => {
const rows = Array.from(document.querySelectorAll('tbody.Crom_body__UYOcU tr'));
return rows.map((row) => {
const tds = Array.from(row.querySelectorAll('td'));
return tds.map(td => td.textContent.trim());
});
});
console.log(data); // an array of arrays containing the text content of each cell in each row
await browser.close();
})();
谢谢你的帮助
2条答案
按热度按时间c3frrgcw1#
使用散列类名抓取HTML可能比使用站点自己的API脆弱得多。
您不需要puppeteer来获取该表中的数据-您甚至不需要抓取它:它是由从以下地址(我在dev tools network panel中找到的)获取的未经身份验证的数据填充的:
看起来服务器只需要适当的
Referer
header就可以获得带有JSON主体的响应,所以你可以简单地以这种方式请求数据:example.mjs
:在终端:
ttygqcqt2#
我看到两个问题:
1.如果您在headless模式下使用默认的用户代理,该网站将阻止您作为机器人。
1.表数据是在页面加载之后动态添加的,因此您需要使用类似
page.waitForSelector
的调用来等待它的到来。代码如下:
请注意,我使用的选择器看起来更稳定,并且最后没有随机数。如果只有一个表,普通的
"tr"
和"td"
可能更好。如果你想在你的行上添加标题,试着在
const data = ...
之后添加这个:要阻止不必要的请求以提高速度,您可以用途:
如果检查响应,可以看到包含表数据的API调用:
https://stats.nba.com/stats/synergyplaytypes?LeagueID=00&PerMode=Totals&PlayType=Isolation&PlayerOrTeam=T&SeasonType=Regular%20Season&SeasonYear=2022-23&TypeGrouping=offensive
您可以拦截该响应,而不是从DOM中抓取数据:
您可以选择使用上面相同的“zip”逻辑将标题Map到行。
现在,事实证明所有这些都只是为了指导目的。在这种情况下,API URL是不受保护的,我们可以简单地通过一个简单的HTTP请求来访问它,而不需要Puppeteer,正如this answer很好地说明的那样。
请参阅this tutorial,了解如何找到这样的未受保护的端点。请注意,并不总是可以访问它们,因此您通常需要使用本文中的策略。