Skip to content

Instantly share code, notes, and snippets.

@wes-goulet
Created May 11, 2022 23:09
Show Gist options
  • Save wes-goulet/20f117d3358610d14328c47f6772cfb5 to your computer and use it in GitHub Desktop.
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
#!/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