Created
May 11, 2022 23:09
-
-
Save wes-goulet/20f117d3358610d14328c47f6772cfb5 to your computer and use it in GitHub Desktop.
Node script to fetch instagram posts for a given user and save the files to a folder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
// @ts-check | |
import fetch from "node-fetch"; | |
import fs from "fs"; | |
import { IgApiClient } from "instagram-private-api"; | |
/** | |
* @typedef InstaPost | |
* @prop {string} thumbnailUrl | |
* @prop {string} shortcode | |
* @prop {string} link | |
* @prop {string | null} uploadDate ISO string Date | |
* | |
* @typedef InstaFeed | |
* @prop {InstaPost[]} posts | |
* @prop {string} lastFetched ISO string date | |
* | |
* @typedef {Array<{node: {shortcode: string, display_url: string, thumbnail_src: string, taken_at_timestamp: number}}>} RapiApiInstagarmResult | |
* @typedef {Array<{code: string, image_versions2: {candidates: [{url: string}]}, taken_at: number}>} NodeApiInstagarmResult | |
*/ | |
const RAPID_API_KEY = process.env.INSTA_RAPID_API_KEY; | |
const INSTA_USER = process.env.INSTA_FETCHER_USERNAME; | |
const INSTA_USER_ID = process.env.INSTA_FETCHER_USERID; | |
const INSTA_PWD = process.env.INSTA_FETCHER_PWD; | |
// @ts-ignore | |
const scriptPath = import.meta.url; | |
const igFolderName = "ig-recent"; | |
const feedFileDir = new URL(`../public/_data/${igFolderName}`, scriptPath) | |
.pathname; | |
const feedFilePath = `${feedFileDir}/feed.json`; | |
/** | |
* | |
* @param fileName {string} name of file (including extension). Ex: "picA.png" | |
* @returns {string} | |
*/ | |
function buildPicturePath(fileName) { | |
return new URL(`../public/static/${igFolderName}/${fileName}`, scriptPath) | |
.pathname; | |
} | |
/** | |
* @param imageUrl {string} | |
* @param filePath {string} Full file path | |
*/ | |
async function downloadImage(imageUrl, filePath) { | |
const res = await fetch(imageUrl); | |
res.body.pipe(fs.createWriteStream(filePath)); | |
} | |
async function fetchFeedv2() { | |
try { | |
const ig = new IgApiClient(); | |
ig.state.generateDevice(INSTA_USER); | |
await ig.account.login(INSTA_USER, INSTA_PWD); | |
const userFeed = ig.feed.user(INSTA_USER_ID); | |
const feedItems = await userFeed.items(); | |
/** @type {Promise<InstaPost | undefined>[]} */ | |
const postPromises = feedItems.map((apiResponse, index) => { | |
const { code, taken_at, image_versions2, carousel_media } = apiResponse; | |
const pictureSrc = | |
image_versions2?.candidates?.at(0)?.url || | |
carousel_media?.at(0)?.image_versions2?.candidates?.at(0)?.url; | |
if (!pictureSrc) { | |
return Promise.resolve(undefined); | |
} | |
const fileName = `${index}.png`; | |
const picPath = buildPicturePath(fileName); | |
return downloadImage(pictureSrc, picPath).then(() => { | |
return { | |
shortcode: code, | |
link: `https://www.instagram.com/p/${code}`, | |
thumbnailUrl: `/static/ig-recent/${fileName}`, | |
uploadDate: taken_at ? new Date(taken_at).toISOString() : null, | |
}; | |
}); | |
}); | |
const posts = await Promise.all(postPromises); | |
/** @type {InstaFeed} */ | |
const feed = { | |
posts: posts.filter(Boolean), | |
lastFetched: new Date().toISOString(), | |
}; | |
fs.writeFileSync(feedFilePath, JSON.stringify(feed)); | |
} catch (err) { | |
console.error(err); | |
} | |
} | |
async function fetchFeed() { | |
try { | |
const response = await fetch( | |
`https://instagram40.p.rapidapi.com/account-feed?username=${INSTA_USER}`, | |
{ | |
method: "GET", | |
headers: { | |
"x-rapidapi-host": "instagram40.p.rapidapi.com", | |
"x-rapidapi-key": RAPID_API_KEY, | |
}, | |
} | |
); | |
console.log(response); | |
const parsed = /** @type {RapiApiInstagarmResult} */ ( | |
await response.json() | |
); | |
/** @type {Promise<InstaPost | undefined>[]} */ | |
const postPromises = /** @type {RapiApiInstagarmResult} */ (parsed).map( | |
(apiResponse, index) => { | |
const { | |
node: { thumbnail_src, taken_at_timestamp, shortcode, display_url }, | |
} = apiResponse; | |
const pictureSrc = thumbnail_src || display_url; | |
if (!pictureSrc) { | |
return Promise.resolve(undefined); | |
} | |
const fileName = `${index}.png`; | |
const picPath = buildPicturePath(fileName); | |
return downloadImage(pictureSrc, picPath).then(() => { | |
return { | |
shortcode, | |
link: `https://www.instagram.com/p/${shortcode}`, | |
thumbnailUrl: `/static/ig-recent/${fileName}`, | |
uploadDate: taken_at_timestamp | |
? new Date(taken_at_timestamp).toISOString() | |
: null, | |
}; | |
}); | |
} | |
); | |
const posts = await Promise.all(postPromises); | |
/** @type {InstaFeed} */ | |
const feed = { | |
posts: posts.filter(Boolean), | |
lastFetched: new Date().toISOString(), | |
}; | |
fs.writeFileSync(feedFilePath, JSON.stringify(feed)); | |
} catch (err) { | |
console.error(err); | |
} | |
} | |
// ensure directories exist before running | |
fs.mkdirSync(feedFileDir, { recursive: true }); | |
fs.mkdirSync(buildPicturePath(""), { recursive: true }); | |
// @ts-ignore | |
await fetchFeedv2(); | |
// await fetchFeed(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment