我正在尝试制作一个个人项目,该项目将使用刮刀从维基百科收集文本和数字数据,随后将所有这些数据移动到数据库,然后比较所有这些收集的值以进行可视化表示。
但是我在选择想要收集的值时遇到了一些困难,特别是对于 HTML 表格。我想选择内部包含数据且仅包含特定列的所有行。
例如我有一个这样的表:
Column1 column2 column3
rowdata1 rowdata1 rowdata1
rowdata2 rowdata2 rowdata2
我想让它看起来像这样:
Column1 column3
rowdata1 rowdata1
rowdata2 rowdata2
例如,没有第二列及其行。那么,有没有什么简单直接的解决方案呢?因为使用 Xpath 手动选择姓名和号码需要很长时间。下面是我当前代码的示例
const puppeteer = require('puppeteer');
async function scrapewiki(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const [el] = await page.$x('/html/body/div[3]/div[3]/div[5]/div[1]/table/tbody/tr[2]/td[1]/a');
const txt = await el.getProperty('textContent');
const country = await txt.jsonValue();
const [el2] = await page.$x('/html/body/div[3]/div[3]/div[5]/div[1]/table/tbody/tr[2]/td[3]');
const txt2 = await el2.getProperty('textContent');
const population = await txt2.jsonValue();
避免使用浏览器生成的 CSS 选择器和 XPath 。它们有时很方便,但几乎总是次优。查看表格,有一种更简洁的方法来识别您想要的数据:<table class="wikitable sortable">
。该表每行都有
<tr>
元素和
<td>
单元格。标准表格设置在这里。CSS 选择器
table.wikitable.sortable tr
(我假设它是页面上唯一的选择器)给出所有行,对于每一行,使用选择器
td
提取单元格,给出一个二维表格。
.slice(2)
对于撕掉标题很有用。其次,这是个人观点,但我几乎总是使用 CSS 选择器,除非我
have 使用 XPath 或者这是它们更干净的特殊情况。 XPath 语法比 CSS 选择器更糟糕。
const puppeteer = require("puppeteer"); // 15.4.0
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
const url = "https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population";
await page.goto(url, {waitUntil: "domcontentloaded"});
const tableSel = "table.wikitable.sortable tr";
const data = await page.$$eval(tableSel, els =>
els.slice(2).map(el =>
[...el.querySelectorAll("td")]
.map(e => e.textContent.trim())
)
);
const nameAndPop = data.map(e => [e[0], e[2]]);
console.table(nameAndPop.slice(0, 10));
console.log("total rows", data.length);
})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
最后,您不需要 Puppeteer 来完成此操作。最好使用 Wikipedia API,或者使用简单的轻量级 HTTP fetch
/
axios
请求与 Cheerio 等 HTML 解析库结合使用。您可以考虑将上述代码仅用于教育目的;这不是最好的做事方式。考虑在节点 18 上使用带有
fetch
的 Cheerio:
const cheerio = require("cheerio"); // 1.0.0-rc.12
fetch("https://en.wikipedia.org/wiki/List_of_countries_and_dependencies_by_population")
.then(res => res.text())
.then(text => {
const $ = cheerio.load(text);
const rows = [];
$("table.wikitable.sortable tr").slice(2).each(function (i, e) {
const row = [];
rows.push(row);
$(this).find("td").each(function (i, e) {
row.push($(this).text().trim());
});
});
const nameAndPop = rows.map(e => [e[0], e[2]]);
console.table(nameAndPop);
console.table(rows.length);
})
;
在我的慢速 Windows 10 上网本上,我可以在 3 秒内运行 Cheerio 脚本,而具有冷缓存的 Puppeteer 需要 34 秒(使用 Measure-Command
)。禁用 JS 并阻止图像和其他资源对于 Puppeteer 来说是个好主意,但我没有打扰;大部分开销是启动浏览器。
另请参阅:
披露:我是链接博客文章的作者。