Refactor price monitoring to work exclusively with ceneo.pl, removing unused selectors and browser automation. Update product comparison logic to match by name and include shop information in notifications.

This commit is contained in:
Norbert Maciaszek
2025-11-16 18:40:36 +01:00
parent 1f41be62f1
commit 72ba9aae20
2 changed files with 180 additions and 83 deletions

110
index.js
View File

@@ -1,22 +1,13 @@
const cheerio = require("cheerio"); const cheerio = require("cheerio");
const cron = require("node-cron"); const cron = require("node-cron");
const axios = require("axios"); const axios = require("axios");
const { chromium } = require("playwright");
const fs = require("fs"); const fs = require("fs");
const discordWebhook = const discordWebhook =
"https://discord.com/api/webhooks/1439286509390921749/t2Hb8XloF6zhDRYD1yh_QlkHHa9eHUyXvd9TxZRHwqR_b_OxxbnwDgsm4em8TwA9NQIa"; "https://discord.com/api/webhooks/1439286509390921749/t2Hb8XloF6zhDRYD1yh_QlkHHa9eHUyXvd9TxZRHwqR_b_OxxbnwDgsm4em8TwA9NQIa";
const priceSelectors = { // new version works with ceneo.pl only
miodowamydlarnia: ".projector_prices__price", const productsIds = ["182928416", "95706396", "90180325"];
greentouch: ".main-price",
amazon: "#corePrice_feature_div .a-price .a-offscreen",
soxo: "#projector_price_value span",
empik: '[data-ta-section="priceMainContainer"] [data-ta="price"]',
notino: "#pd-price",
};
const excludePage = ["allegro", "homla.com.pl", "home-you.com"];
function sendMessage(message) { function sendMessage(message) {
axios.post(discordWebhook, { axios.post(discordWebhook, {
@@ -36,7 +27,7 @@ async function compareAndSave(productsPrice) {
for (const product of productsPrice) { for (const product of productsPrice) {
const oldProduct = oldProductsPrice.find( const oldProduct = oldProductsPrice.find(
(oldProduct) => oldProduct.link === product.link (oldProduct) => oldProduct.name === product.name
); );
if (oldProduct && oldProduct.price !== product.price) { if (oldProduct && oldProduct.price !== product.price) {
@@ -45,6 +36,7 @@ async function compareAndSave(productsPrice) {
newPrice: product.price, newPrice: product.price,
oldPrice: oldProduct.price, oldPrice: oldProduct.price,
link: product.link, link: product.link,
shop: product.shop,
}); });
} }
} }
@@ -65,88 +57,40 @@ async function compareAndSave(productsPrice) {
); );
} }
async function getProducts() {
const products = await axios
.get(
"https://db.maciaszek.ovh/api/collections/gifts_items/records?fields=title,link"
)
.then((response) => response.data.items);
return products
.filter((product) => product.link !== "")
.filter(
(product) => !excludePage.some((page) => product.link.includes(page))
);
}
async function init() { async function init() {
const productsWithLinks = await getProducts(); const products = [];
const productsWithBrowser = [];
const productsPrice = [];
const selectors = Object.keys(priceSelectors);
console.log("Zaczynam sprawdzać ceny"); for (const productId of productsIds) {
for (const product of productsWithLinks) { const response = await axios.get(`https://www.ceneo.pl/${productId}`);
if (product.link === "") continue; const $ = cheerio.load(response.data);
try { const items = $(".product-offer__container").first();
const { data } = await axios.get(product.link, { for (const item of items) {
headers: { let name = $(item).data("productname");
"User-Agent": let price = $(item).data("price");
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36", let link = `https://www.ceneo.pl/${$(item).data("click-url")}`;
}, const shop = $(item).data("shopurl") || "ceneo.pl";
});
const $ = cheerio.load(data); if (!name || !price) {
const selector = selectors.find((selector) => name = $(item).find(".short-name__txt").text();
product.link.includes(selector) price = $(item).find(".price").text();
); link = `https://www.ceneo.pl/${productId}`;
const price = $(priceSelectors[selector]).text(); }
productsPrice.push({
name: product.title, if (!name || !price || !link) {
price: price, continue;
link: product.link, }
});
} catch { products.push({ name, price, link, shop });
productsWithBrowser.push(product);
} }
} }
if (productsWithBrowser.length > 0) { await compareAndSave(products);
const browser = await chromium.launch();
const context = await browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
});
const page = await context.newPage();
for (const product of productsWithBrowser) {
await page.goto(product.link);
const selector = selectors.find((selector) =>
product.link.includes(selector)
);
const element = await page.$(priceSelectors[selector]);
if (!element) continue;
const price = await element.textContent();
productsPrice.push({
name: product.title,
price: price,
link: product.link,
});
}
await browser.close();
}
await compareAndSave(productsPrice);
console.log("Sprawdzone! Aktualne ceny zapisane w productsPrice.json"); console.log("Sprawdzone! Aktualne ceny zapisane w productsPrice.json");
} }
sendMessage("Zaczynam monitoring cen"); sendMessage("Zaczynam monitoring cen");
const task = cron.schedule("0 */3 * * *", init, { const task = cron.schedule("0 6,9,12,14,16,18,21 * * *", init, {
timezone: "Europe/Warsaw", timezone: "Europe/Warsaw",
}); });

153
index.js.old Normal file
View File

@@ -0,0 +1,153 @@
const cheerio = require("cheerio");
const cron = require("node-cron");
const axios = require("axios");
const { chromium } = require("playwright");
const fs = require("fs");
const discordWebhook =
"https://discord.com/api/webhooks/1439286509390921749/t2Hb8XloF6zhDRYD1yh_QlkHHa9eHUyXvd9TxZRHwqR_b_OxxbnwDgsm4em8TwA9NQIa";
const priceSelectors = {
miodowamydlarnia: ".projector_prices__price",
greentouch: ".main-price",
amazon: "#corePrice_feature_div .a-price .a-offscreen",
soxo: "#projector_price_value span",
empik: '[data-ta-section="priceMainContainer"] [data-ta="price"]',
notino: "#pd-price",
};
const excludePage = ["allegro", "homla.com.pl", "home-you.com"];
function sendMessage(message) {
axios.post(discordWebhook, {
content: message,
});
}
async function compareAndSave(productsPrice) {
if (!fs.existsSync("productsPrice.json")) {
fs.writeFileSync("productsPrice.json", "[]");
}
const productsPriceJson = fs.readFileSync("productsPrice.json", "utf8");
const oldProductsPrice = JSON.parse(productsPriceJson);
const diffProducts = [];
for (const product of productsPrice) {
const oldProduct = oldProductsPrice.find(
(oldProduct) => oldProduct.link === product.link
);
if (oldProduct && oldProduct.price !== product.price) {
diffProducts.push({
name: product.name,
newPrice: product.price,
oldPrice: oldProduct.price,
link: product.link,
});
}
}
for (const product of diffProducts) {
sendMessage(
`Zmiana ceny **${product.name}**:\nCena: ${product.oldPrice} -> ${product.newPrice}\nLink: ${product.link}`
);
}
if (diffProducts.length === 0) {
sendMessage("Brak zmian w cenach");
}
fs.writeFileSync(
"productsPrice.json",
JSON.stringify(productsPrice, null, 2)
);
}
async function getProducts() {
const products = await axios
.get(
"https://db.maciaszek.ovh/api/collections/gifts_items/records?fields=title,link"
)
.then((response) => response.data.items);
return products
.filter((product) => product.link !== "")
.filter(
(product) => !excludePage.some((page) => product.link.includes(page))
);
}
async function init() {
const productsWithLinks = await getProducts();
const productsWithBrowser = [];
const productsPrice = [];
const selectors = Object.keys(priceSelectors);
console.log("Zaczynam sprawdzać ceny");
for (const product of productsWithLinks) {
if (product.link === "") continue;
try {
const { data } = await axios.get(product.link, {
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
},
});
const $ = cheerio.load(data);
const selector = selectors.find((selector) =>
product.link.includes(selector)
);
const price = $(priceSelectors[selector]).text();
productsPrice.push({
name: product.title,
price: price,
link: product.link,
});
} catch {
productsWithBrowser.push(product);
}
}
if (productsWithBrowser.length > 0) {
const browser = await chromium.launch();
const context = await browser.newContext({
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
});
const page = await context.newPage();
for (const product of productsWithBrowser) {
await page.goto(product.link);
const selector = selectors.find((selector) =>
product.link.includes(selector)
);
const element = await page.$(priceSelectors[selector]);
if (!element) continue;
const price = await element.textContent();
productsPrice.push({
name: product.title,
price: price,
link: product.link,
});
}
await browser.close();
}
await compareAndSave(productsPrice);
console.log("Sprawdzone! Aktualne ceny zapisane w productsPrice.json");
}
sendMessage("Zaczynam monitoring cen");
const task = cron.schedule("0 6,9,12,15,18,21 * * *", init, {
timezone: "Europe/Warsaw",
});
task.execute();