Skip to content

Instantly share code, notes, and snippets.

@tatocaster
Created August 15, 2023 09:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tatocaster/59779b8af3ad089abefc53972806c750 to your computer and use it in GitHub Desktop.
Save tatocaster/59779b8af3ad089abefc53972806c750 to your computer and use it in GitHub Desktop.
get Nintendo EShop games and metacritics score
import { getGamesAmerica, GameUS, EshopError } from 'nintendo-switch-eshop';
import * as fs from 'fs';
import { HTMLElement, parse } from "node-html-parser";
import fetch from 'node-fetch';
const dest = './nintendo-games.json';
const allowedLettersInTitle = /[^a-zA-Z0-9 ]+/g;
const PAGE_SIZE = 25
interface NintendoGame {
title: string;
url : string;
nsuid: string;
esrb_rating:string;
date: string;
platform: string;
msrp: number;
genres: string;
meta_score: number;
}
function saveResponseToFile(response: NintendoGame[]) {
fs.writeFile(dest, JSON.stringify(response), (err) => {
if (err) {
console.error('Error writing JSON to file:', err);
} else {
console.log('JSON data has been written to the file successfully!');
}
});
}
async function readJsonFromFile(): Promise<NintendoGame[]> {
return new Promise((resolve, reject) => {
fs.readFile(dest, "utf8", (err, data) => {
if (err) {
reject(err);
} else {
try {
const jsonData = JSON.parse(data);
resolve(jsonData);
} catch (parseError) {
reject(parseError);
}
}
});
});
}
function getMetaScore(root: HTMLElement): number {
const extractMetaScore = (node: any): number => {
if (node.nodeType === 1 && node.rawTagName === "span") {
for (const attr of node.rawAttrs.split(" ")) {
const [key, val] = attr.split("=");
if (key === "itemprop" && val === "\"ratingValue\"") {
const metaScoreStr = node.rawText;
const metaScore = parseInt(metaScoreStr, 10);
if (isNaN(metaScore)) {
return 0;
}
return metaScore;
}
}
}
for (const child of node.childNodes) {
const score = extractMetaScore(child);
if (score !== 0) {
return score;
}
}
return 0;
};
return extractMetaScore(root);
}
async function fetchAndSaveDataInChunks(batchIndex: number, batchData: NintendoGame[]): Promise<void> {
for (const game of batchData) {
const response = await fetch(`https://www.metacritic.com/game/switch/${game.title.toLowerCase().replace(/ /g, "-")}`);
if (!response.ok) {
continue;
}
const html = await response.text();
const root = parse(html);
game.meta_score = getMetaScore(root);
}
// Save responses to a file
const filename = `responses_batch_${batchIndex}.json`;
fs.writeFileSync(filename, JSON.stringify(batchData));
console.log(`Batch ${batchIndex} completed and saved to ${filename}`);
}
async function fetchMetaCriticScores() {
const gameData = await readJsonFromFile();
console.log("JSON data size from file: ", gameData.length);
const totalEntries = gameData.length;
const totalBatches = Math.ceil(totalEntries / PAGE_SIZE);
for (let batchIndex = 158; batchIndex < totalBatches; batchIndex++) {
await fetchAndSaveDataInChunks(batchIndex, gameData.slice(batchIndex * PAGE_SIZE, (batchIndex+1) * PAGE_SIZE));
}
console.log("All batches completed.");
}
async function main(isFetchEnabled: boolean){
if(isFetchEnabled){
getGamesAmerica()
.then((result: GameUS[]) => {
console.log(result.length);
const newData : NintendoGame[] = result.map(game => ({
title: game.title.replace(allowedLettersInTitle, ""),
url : game.url,
nsuid: game.nsuid,
esrb_rating: game.esrbRating,
date: new Date(game.releaseDateDisplay).toLocaleDateString(),
platform: game.platform?.replace("Nintendo ", ''),
msrp: game.msrp,
genres: `[${game.genres?.map((item) => `'${item}'`).join()}]`,
meta_score: 0
}))
saveResponseToFile(newData);
})
.catch((error: EshopError) => {
console.error("Error occurred:", error);
});
} else{
fetchMetaCriticScores()
}
}
const programArguments = process.argv.slice(2)
const isFetchEnabled = programArguments.length > 0 && programArguments[0].toLowerCase() === "true";
main(isFetchEnabled)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment