Счётчик при парсинге в puppeteer
Есть функция, в которой перебирается массив ссылок:
const fs = require('fs')
const puppeteer = require('puppeteer')
let links = ["https://steamcommunity.com/inventory/76561198142264901/440/2?l=english&count=5000", "https://steamcommunity.com/inventory/76561198296942562/440/2?l=english&count=5000", "https://steamcommunity.com/inventory/76561198142264901/440/2?l=english&count=5000"];
async function browse() {
let browser = await puppeteer.launch({ headless: false, slowMo: 100, devtools: false });
for (let i = 0; i < links.length; i++) {
let link = links[i];
try {
let page = await browser.newPage();
await page.setViewport({ width: 1400, height: 900 });
await page.goto(link, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('body');
async function repeater() {
let html = await page.evaluate(async () =>{
let skinname = document.querySelector('pre').textContent;
skinname = JSON.parse(skinname)
let number = 0;
for (var i = 0; i < skinname.descriptions.length; i++) {
if ((skinname.descriptions[i].market_name === "The DethKapp")) {
for (var j = 0; j < skinname.descriptions[i].descriptions.length; j++) {
if (skinname.descriptions[i].descriptions[j].value === "Paint Color: Muskelmannbraun") {
++number;
}
}
}
}
})
}
setInterval(repeater, 1000);
}
catch (e) {
console.log(e);
}
}
}
browse();
как сделать так, чтобы счётчик считал совпадения на каждой странице, передавал их в определенную переменную и в итоге выводилась сумма всех совпадений, после чего счетчик бы обнулялся и цикл проходил снова?
Ответы (2 шт):
Если я правильно понимаю ситуацию, попробуйте так: возвращайте счётчик из функции, исполняемой в page.evaluate() и выводите его. Чтобы не путались страницы, можно выводить ещё какой-то идентификатор страницы при этом (например, номер адреса из массива или сам адрес).
async function repeater(number) {
const count = await page.evaluate(() => {
let skinname = document.querySelector('pre').textContent;
skinname = JSON.parse(skinname)
let number = 0;
for (var i = 0; i < skinname.descriptions.length; i++) {
if ((skinname.descriptions[i].market_name === "The DethKapp")) {
for (var j = 0; j < skinname.descriptions[i].descriptions.length; j++) {
if (skinname.descriptions[i].descriptions[j].value === "Paint Color: Muskelmannbraun") {
++number;
}
}
}
}
return number;
});
console.log(count);
}
setInterval(repeater, 1000);
Основная причина почему у вас не получается сделать требуемое - неприменение "Принципа единственной ответственности". Ваш код делает всё и сразу и поэтому с ним тяжело работать.
Предлагаю решать задачу упрощая код.
Первое - вынесем обработчик отдельной ссылки в отдельную функцию. Избавимся от поиска совпадений внутри контекста запущенного браузера. Сделаем так: браузер нашёл нужные данные и вернул их.
async function loadData(link) {
const page = await browser.newPage();
await page.setViewport({ width: 1400, height: 900 });
await page.goto(link, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('body');
const pageData = await page.evaluate(async () => {
const data = document.querySelector('pre').textContent;
try {
return JSON.parse(data)?.descriptions;
} catch (err) { /* */ }
});
await page.close();
return pageData;
}
Второе - функция, обрабатывающая данные. Для каждого адреса собрала данные из первой функции и посчитала количество совпадений.
async function findMatches() {
const items = await Promise.allSettled(
links.map(loadData)
);
const matchesCount = items.reduce(
(acc, x) => {
if (x.status === 'rejected') {
console.error(x.reason);
return acc;
}
(x.value || []).forEach((item) => {
if (isInterested(item)) {
acc += 1;
}
});
return acc;
},
0
);
return matchesCount;
}
Отдельно вынесем принятие решения - интересный или не интересный объект:
function isInterested(item) {
return item.market_name === 'The DethKapp'
&& item.descriptions.some(({ value }) => value === 'Paint Color: Muskelmannbraun');
}
Третье - всё написанное завернём в пускач и бесконечное повторение.
Et voila, всё вместе:
/* global document */
const { setTimeout } = require('timers/promises');
const puppeteer = require('puppeteer');
const links = [
'https://steamcommunity.com/inventory/76561198142264901/440/2?l=english&count=5000',
'https://steamcommunity.com/inventory/76561198296942562/440/2?l=english&count=5000',
'https://steamcommunity.com/inventory/76561198142264901/440/2?l=english&count=5000',
];
/** @type {puppeteer.Browser} */
let browser;
(async () => {
browser = await puppeteer.launch({
headless: true,
ignoreHTTPSErrors: true
});
do {
const matchesCount = await findMatches();
console.log(`[${new Date().toISOString()}] ${matchesCount}`);
await setTimeout(2000);
} while (1);
await browser.close();
})();
/**
* @returns {Promise<number>}
*/
async function findMatches() {
const items = await Promise.allSettled(
links.map(loadData)
);
const matchesCount = items.reduce(
(acc, x) => {
if (x.status === 'rejected') {
console.error(x.reason);
return acc;
}
(x.value || []).forEach((item) => {
if (isInterested(item)) {
acc += 1;
}
});
return acc;
},
0
);
return matchesCount;
}
/**
* @param {Record<string, any>} item
* @returns {boolean}
*/
function isInterested(item) {
return item.market_name === 'The DethKapp'
&& item.descriptions?.some(({ value }) => value === 'Paint Color: Muskelmannbraun');
}
/**
* @param {string} link
* @returns {Promise<Array<Record<string, any>>>}
*/
async function loadData(link) {
const page = await browser.newPage();
await page.setViewport({ width: 1400, height: 900 });
await page.goto(link, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('body');
const pageData = await page.evaluate(async () => {
const data = document.querySelector('pre').textContent;
try {
return JSON.parse(data)?.descriptions;
} catch (err) { /* */ }
});
await page.close();
return pageData;
}
В консоли:
~/git/stackoverflow-practice $ node src/puppeteer-steamcommunity/index.js
[2021-05-23T18:26:22.015Z] 2
[2021-05-23T18:26:29.736Z] 2
[2021-05-23T18:26:36.884Z] 2
[2021-05-23T18:26:44.980Z] 2
[2021-05-23T18:26:53.520Z] 2
[2021-05-23T18:27:00.185Z] 2
[2021-05-23T18:27:06.270Z] 2
[2021-05-23T18:27:13.507Z] 2
[2021-05-23T18:27:21.307Z] 2
[2021-05-23T18:27:28.601Z] 2
[2021-05-23T18:27:36.115Z] 2
[2021-05-23T18:27:44.886Z] 2
[2021-05-23T18:27:53.841Z] 2
[2021-05-23T18:28:01.060Z] 2
[2021-05-23T18:28:09.044Z] 2
nodejs v16.2.0, puppeteer v9.1.1