diff --git a/README.md b/README.md index 1769e09..02c47ce 100644 --- a/README.md +++ b/README.md @@ -51,12 +51,12 @@ eventually, together with the current level of implementation ⛔️ = Not implemented ``` -| Source | Status | Comments | -| ---------------------------------------------------- | ------ | ---------------------------------------- | -| [iLMeteo](https://www.ilmeteo.it) | ✅ | | -| [3Bmeteo](https://www.3bmeteo.com/) | 🚧 | Precipitation might not work as intended | -| [OpenMeteo](https://open-meteo.com/) | ✅ | | -| [Meteo Aeronautica Militare](http://www.meteoam.it/) | ⛔️ | | +| Source | Status | Comments | +| ---------------------------------------------- | ------ | ----------------------------------------------- | +| [iLMeteo](https://www.ilmeteo.it) | ✅ | | +| [3Bmeteo](https://www.3bmeteo.com/) | 🚧 | Precipitation might not work as intended | +| [OpenMeteo](https://open-meteo.com/) | ✅ | | +| [Aeronautica Militare](http://www.meteoam.it/) | 🚧 | Week only has `[0..4]` instead of `[0..6]` days |
iLMeteo @@ -216,3 +216,35 @@ Format: ```
+ +
+Aeronautica Militare + +Format: + +```json +{ + "hourly": { + "temperature": { + "type": "number", + "unit": "°C" + }, + "precipitationProbability": { + "type": "number", + "unit": "%" + } + }, + "daily": { + "minimumTemperature": { + "type": "number", + "unit": "°C" + }, + "maximumTemperature": { + "type": "number", + "unit": "°C" + } + } +} +``` + +
diff --git a/index.js b/index.js index aa2ef49..afb3286 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ import puppeteer from 'puppeteer' import fetchILMeteo from './scrapers/iLMeteo.js' import fetch3Bmeteo from './scrapers/3Bmeteo.js' +import fetchAeronauticaMilitare from './scrapers/AeronauticaMilitare.js' import fetchOpenMeteo from './scrapers/OpenMeteo.js' const NIX_OPS = { @@ -18,10 +19,12 @@ const run = async () => { iLMeteo, treBmeteo, openMeteo, + aeronauticaMilitare, ] = await Promise.all([ fetchILMeteo(browser), fetch3Bmeteo(browser), fetchOpenMeteo(), + fetchAeronauticaMilitare(browser), ]) await browser.close() @@ -30,6 +33,7 @@ const run = async () => { iLMeteo, treBmeteo, openMeteo, + aeronauticaMilitare, } } diff --git a/scrapers/AeronauticaMilitare.js b/scrapers/AeronauticaMilitare.js new file mode 100644 index 0000000..4f31412 --- /dev/null +++ b/scrapers/AeronauticaMilitare.js @@ -0,0 +1,160 @@ +const FORMAT = { + hourly: { + temperature: { + type: 'number', + unit: '°C', + }, + precipitationProbability: { + type: 'number', + unit: '%', + }, + // weatherCode: { + // type: 'number', + // unit: 'Aeronautica Militare Weather Icons', + // }, + }, + daily: { + minimumTemperature: { + type: 'number', + unit: '°C', + }, + maximumTemperature: { + type: 'number', + unit: '°C', + }, + // weatherCode: { + // type: 'number', + // unit: 'Aeronautica Militare Weather Icons', + // }, + }, +} + +const range = (start, end) => { + /// returns [start .. (end-1)] + return [...Array(end - start).keys()].map(x => x + start) +} + +const parseTemp = el => { + // return el.textContent + return parseFloat(el.textContent) +} +const parsePrecProb = el => { + const text = el.textContent.trim() + return parseFloat(text) + return text + if (text === 'assenti') return 0 + if (text === 'deboli') return 0.05 + + try { + //TODO not 100% sure that this would work as it was never tested + } catch (error) { + console.log(error) + return text + } +} + +export default async browser => { + const page = await browser.newPage() + await page.goto('https://www.meteoam.it/it/meteo-citta/pisa') + + const meteogramInfoContainer = await page + .locator('.meteogram-info-container') + .waitHandle() + + const startTime = new Date().getHours() + 1 + const endTime = 24 + + const scrapeColumn = async i => { + const column = await meteogramInfoContainer.$( + `#weather_info_container_${i}`, + ) + + const tempDiv = await column.$('.weather-info-temperature') + const precProbDiv = await column.$('.weather-rain-probability') + + const temperature = await tempDiv.evaluate(parseTemp) + const precipitationProbability = + await precProbDiv.evaluate(parsePrecProb) + + return { temperature, precipitationProbability } + } + + const scrapeBlock = async i => { + const block = await meteogramInfoContainer.$( + `.swiper-slide[aria-label="${i + 1} / 5"]`, + ) + + const tempMaxDiv = await block.$('.meteogram-info-temp-max') + const tempMinDiv = await block.$('.meteogram-info-temp-min') + + const maximumTemperature = await tempMaxDiv.evaluate(parseTemp) + const minimumTemperature = await tempMinDiv.evaluate(parseTemp) + + return { minimumTemperature, maximumTemperature } + } + + const scrapeToday = async () => { + let today = {} + const indices = range(startTime, endTime) + const results = await Promise.all(indices.map(scrapeColumn)) + + for (let i = startTime; i < endTime; i++) { + today[i] = results[i - startTime] + } + + return today + } + + const scrapeTomorrow = async () => { + let tomorrow = {} + const indices = range(24 - startTime, 48 - startTime) + const results = await Promise.all(indices.map(scrapeColumn)) + + for (let i = 0; i < 24; i++) { + tomorrow[i] = results[i] + } + + return tomorrow + } + + const scrapeDayAfterTomorrow = async () => { + let dayAfterTomorrow = {} + const indices = range(48 - startTime, 72 - startTime) + const results = await Promise.all(indices.map(scrapeColumn)) + + for (let i = 0; i < 24; i++) { + dayAfterTomorrow[i] = results[i] + } + + return dayAfterTomorrow + } + + const scrapeWeek = async () => { + let week = {} + const indices = range(0, 5) + const results = await Promise.all(indices.map(scrapeBlock)) + + for (let i = 0; i < 5; i++) { + week[i] = results[i] + } + + return week + } + + const [today, tomorrow, dayAfterTomorrow, week] = await Promise.all([ + scrapeToday(), + scrapeTomorrow(), + scrapeDayAfterTomorrow(), + scrapeWeek(), + ]) + + await page.close() + + return { + format: FORMAT, + today, + tomorrow, + dayAfterTomorrow, + week, + } +}