This commit is contained in:
@@ -10,10 +10,17 @@ const symbols = {
|
||||
BPIDEST2040: {
|
||||
url: "https://www.bancobpi.pt/particulares/poupar-investir/ppr/bpi-destino-ppr-2040",
|
||||
name: "BPI Destino PPR 2040",
|
||||
type: "bpi",
|
||||
},
|
||||
BPIDEST2050: {
|
||||
url: "https://www.bancobpi.pt/particulares/poupar-investir/ppr/bpi-destino-ppr-2050",
|
||||
name: "BPI Destino PPR 2050",
|
||||
type: "bpi",
|
||||
},
|
||||
IE00BK5BQT80: {
|
||||
url: "https://www.deco.proteste.pt/investe/investimentos/fundos/vanguard-ftse-all-world-ucits-etf-usd-acc",
|
||||
name: "Vanguard FTSE All-World UCITS ETF USD Acc",
|
||||
type: "deco",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -38,53 +45,99 @@ async function getLatestPrice(symbol) {
|
||||
// Visit the page and wait until network connections are completed
|
||||
await page.goto(config.url, { waitUntil: "networkidle2" });
|
||||
|
||||
// Interact with the DOM to retrieve the price and date
|
||||
const [price, date] = await page.evaluate(() => {
|
||||
const spanTags = document.getElementsByTagName("span");
|
||||
const priceSearchText = "ÚLTIMA COTAÇÃO:";
|
||||
const dateSearchText = "DATA COTAÇÃO:";
|
||||
let priceElementFound;
|
||||
let dateElementFound;
|
||||
|
||||
for (let i = 0; i < spanTags.length; i++) {
|
||||
if (spanTags[i].textContent.trim() == priceSearchText) {
|
||||
priceElementFound = spanTags[i];
|
||||
if (priceElementFound && dateElementFound) break;
|
||||
}
|
||||
if (spanTags[i].textContent.trim() == dateSearchText) {
|
||||
dateElementFound = spanTags[i];
|
||||
if (priceElementFound && dateElementFound) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priceElementFound || !dateElementFound) {
|
||||
throw new Error("Could not find price or date elements");
|
||||
}
|
||||
|
||||
return [
|
||||
priceElementFound.nextSibling.innerHTML,
|
||||
dateElementFound.nextSibling.innerHTML,
|
||||
];
|
||||
});
|
||||
|
||||
const marketPrice = parseFloat(
|
||||
price.replace("€", "").trim().replace(",", "."),
|
||||
);
|
||||
|
||||
return {
|
||||
symbol,
|
||||
name: config.name,
|
||||
currency: "EUR",
|
||||
price: marketPrice,
|
||||
date: date.trim(),
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
switch (config.type) {
|
||||
case "bpi":
|
||||
return await scrapeBPI(page, symbol, config);
|
||||
case "deco":
|
||||
return await scrapeDeco(page, symbol, config);
|
||||
default:
|
||||
throw new Error(`Unsupported type: ${config.type}`);
|
||||
}
|
||||
} finally {
|
||||
// Always close the browser
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function scrapeDeco(page, symbol, config) {
|
||||
// Interact with the DOM to retrieve the price and date
|
||||
const [price, date] = await page.evaluate(() => {
|
||||
const priceElement = document.querySelector(
|
||||
".product-points-data__current-value",
|
||||
);
|
||||
const dateElement = document.querySelector(
|
||||
".product-points-data__current-value + span",
|
||||
);
|
||||
|
||||
if (!priceElement || !dateElement) {
|
||||
throw new Error("Could not find price or date elements");
|
||||
}
|
||||
|
||||
const price = priceElement.textContent
|
||||
.replace("€", "")
|
||||
.replace("EUR", "")
|
||||
.replace(",", ".")
|
||||
.trim();
|
||||
const date = dateElement.textContent.trim();
|
||||
|
||||
return [price, date];
|
||||
});
|
||||
|
||||
const marketPrice = parseFloat(price);
|
||||
return {
|
||||
symbol,
|
||||
name: config.name,
|
||||
currency: "EUR",
|
||||
price: marketPrice,
|
||||
date,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function scrapeBPI(page, symbol, config) {
|
||||
// Interact with the DOM to retrieve the price and date
|
||||
const [price, date] = await page.evaluate(() => {
|
||||
const spanTags = document.getElementsByTagName("span");
|
||||
const priceSearchText = "ÚLTIMA COTAÇÃO:";
|
||||
const dateSearchText = "DATA COTAÇÃO:";
|
||||
let priceElementFound;
|
||||
let dateElementFound;
|
||||
|
||||
for (let i = 0; i < spanTags.length; i++) {
|
||||
if (spanTags[i].textContent.trim() == priceSearchText) {
|
||||
priceElementFound = spanTags[i];
|
||||
if (priceElementFound && dateElementFound) break;
|
||||
}
|
||||
if (spanTags[i].textContent.trim() == dateSearchText) {
|
||||
dateElementFound = spanTags[i];
|
||||
if (priceElementFound && dateElementFound) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priceElementFound || !dateElementFound) {
|
||||
throw new Error("Could not find price or date elements");
|
||||
}
|
||||
|
||||
return [
|
||||
priceElementFound.nextSibling.innerHTML,
|
||||
dateElementFound.nextSibling.innerHTML,
|
||||
];
|
||||
});
|
||||
|
||||
const marketPrice = parseFloat(
|
||||
price.replace("€", "").trim().replace(",", "."),
|
||||
);
|
||||
|
||||
return {
|
||||
symbol,
|
||||
name: config.name,
|
||||
currency: "EUR",
|
||||
price: marketPrice,
|
||||
date: date.trim(),
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// API endpoint to get latest price for a symbol
|
||||
app.get("/price/:symbol", async (req, res) => {
|
||||
const { symbol } = req.params;
|
||||
@@ -92,7 +145,7 @@ app.get("/price/:symbol", async (req, res) => {
|
||||
try {
|
||||
console.log(`Fetching price for symbol: ${symbol}`);
|
||||
const priceData = await getLatestPrice(symbol);
|
||||
console.log(`Success: ${symbol} - €${priceData.price}`);
|
||||
console.log(`Success: ${symbol} - ${priceData.price}`);
|
||||
res.json(priceData);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching price for ${symbol}:`, error.message);
|
||||
|
||||
Reference in New Issue
Block a user