instamancer
Version:
Scrape the Instagram API with Puppeteer
520 lines • 35.5 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const aws = __importStar(require("aws-sdk"));
const fs = __importStar(require("fs"));
const readline = __importStar(require("readline"));
const winston = __importStar(require("winston"));
const path = __importStar(require("path"));
const uuid_1 = require("uuid");
const plugins = __importStar(require("../plugins"));
const api_1 = require("./api/api");
const getPool_1 = require("./getpool/getPool");
const depotUpload = __importStar(require("./http/depot"));
const download_1 = require("./http/download");
const s3Upload = __importStar(require("./http/s3"));
const getLogger = (args) => {
const transports = [];
if (args["logging"] !== "none") {
transports.push(new winston.transports.File({
filename: args["logfile"],
level: args["logging"],
silent: args["logging"] === "none",
}));
}
return winston.createLogger({
level: args["logging"],
silent: args["logging"] === "none",
transports,
});
};
function getOptions(args, logger) {
const options = {
enableGrafting: args["graft"],
executablePath: args["browser"],
fullAPI: args["full"],
headless: !args["visible"],
logger,
plugins: [],
sameBrowser: args["sameBrowser"],
silent: args["quiet"],
sleepTime: args["sleep"],
strict: args["strict"],
total: args["count"],
};
for (const pluginName of args["plugin"]) {
if (plugins.plugins[pluginName]) {
options.plugins.push(new plugins.plugins[pluginName]());
}
else {
throw new Error("Couldn't find plugin " + pluginName);
}
}
return options;
}
/**
* Build argument parser
*/
function buildParser(args, callback) {
/* tslint:disable:no-unused-expression */
require("yargs")(args)
.usage("Usage: $0 <command> [options]")
.command("hashtag [id]", "Scrape a hashtag", {}, async (handleArgs) => {
await spawn(handleArgs);
callback();
})
.command("user [id]", "Scrape a users posts", {}, async (handleArgs) => {
await spawn(handleArgs);
callback();
})
.command("post [ids]", "Scrape a comma-separated list of posts", {}, async (handleArgs) => {
await spawn(handleArgs);
callback();
})
.command("search [query]", "Perform a search of users, tags and places", {}, async (handleArgs) => {
const logger = getLogger(handleArgs);
const options = getOptions(handleArgs, logger);
if (!handleArgs["query"]) {
throw new Error("query required");
}
const search = api_1.createApi("search", handleArgs["query"], options);
const result = await search.get();
process.stdout.write("\n");
process.stdout.write(JSON.stringify(result, null, 2));
process.stdout.write("\n");
callback();
})
.command("batch [batchfile]", "Read newline-separated arguments from a file", {}, () => {
// A list of functions which create new Promises that are
// resolved by buildParser when the spawn commands are
// finished
// See https://stackoverflow.com/a/45951080/7435520
const functions = [];
// Read the list of commands from file
readline
.createInterface({
crlfDelay: Infinity,
input: fs.createReadStream(args[1]),
})
.on("line",
// For each line, create a new function which
// creates a new promise to be resolved by
// buildParser
(line) => {
if (line.length > 0 && line.charAt(0) !== "#") {
functions.push(() => new Promise((res) => buildParser(line, res)));
}
})
.on("close",
// When all lines have been read, synchronously
// execute the commands by waiting for their
// promises to be resolved
async () => {
for (const f of functions) {
await f();
}
process.exit();
});
})
/* tslint:disable:object-literal-sort-keys */
.options({
count: {
alias: "c",
number: true,
default: 0,
describe: "Number of posts to download (0 for all)",
group: "Configuration",
},
full: {
alias: ["f"],
boolean: true,
default: false,
describe: "Retrieve full post data",
group: "Configuration",
},
sleep: {
alias: ["s"],
number: true,
default: 2,
describe: "Seconds to sleep between interactions",
group: "Configuration",
},
graft: {
alias: "g",
boolean: true,
default: true,
describe: "Enable grafting",
group: "Configuration",
},
browser: {
alias: ["b"],
string: true,
default: undefined,
describe: "Browser path. Defaults to the puppeteer version",
group: "Configuration",
},
sameBrowser: {
boolean: true,
default: false,
describe: "Use a single browser when grafting",
group: "Configuration",
},
download: {
alias: "d",
boolean: true,
default: false,
describe: "Save images from posts",
group: "Download",
},
downdir: {
default: "downloads/[endpoint]/[id]",
describe: "Download path",
group: "Download",
},
video: {
alias: "v",
boolean: true,
default: false,
describe: "Download videos (requires full)",
implies: "full",
group: "Download",
},
sync: {
boolean: true,
default: false,
describe: "Force download between requests",
group: "Download",
},
threads: {
alias: "k",
number: true,
default: 4,
describe: "Parallel download / depot threads",
group: "Download",
},
waitDownload: {
alias: "w",
boolean: true,
default: false,
describe: "Download media after scraping",
group: "Download",
},
bucket: {
string: true,
default: undefined,
describe: "Upload files to an AWS S3 bucket",
group: "Upload",
},
depot: {
string: true,
default: undefined,
describe: "Upload files to a URL with a PUT request (depot)",
group: "Upload",
},
file: {
alias: ["o"],
string: true,
default: "[id]",
describe: "Output filename. '-' for stdout",
group: "Output",
},
type: {
alias: ["t"],
default: "json",
describe: "Filetype",
choices: ["csv", "json", "both"],
group: "Output",
},
mediaPath: {
alias: ["m"],
boolean: true,
default: false,
describe: "Add filepaths to _mediaPath",
group: "Output",
},
visible: {
boolean: true,
default: false,
describe: "Show browser on the screen",
group: "Display",
},
quiet: {
alias: ["q"],
boolean: true,
default: false,
describe: "Disable progress output",
group: "Display",
},
logging: {
alias: ["l"],
default: "none",
choices: ["none", "error", "info", "debug"],
group: "Logging",
},
logfile: {
string: true,
default: "instamancer.log",
describe: "Log file name",
group: "Logging",
},
strict: {
boolean: true,
default: false,
describe: "Throw an error on response type mismatch",
group: "Validation",
},
plugin: {
alias: ["p"],
array: true,
default: [],
describe: "Use a plugin from the plugins directory",
group: "Plugins",
},
})
.demandCommand()
.example("$0 hashtag instagood -fvd", "Download all the available posts, and their media from #instagood")
.example("$0 user arianagrande --type=csv --logging=info --visible", "Download Ariana Grande's posts to a CSV file with a non-headless browser, and log all events")
.epilog("Source code available at https://github.com/ScriptSmith/instamancer")
.strict().argv;
/* tslint:enable:no-unused-expression */
}
/**
* Spawn an instance of the API
* @param args
*/
async function spawn(args) {
// Initiate logger
const logger = getLogger(args);
// Check id
if (!(args["id"] || args["ids"])) {
throw new Error("Id required");
}
// Pick endpoint
let ids;
if (args["_"][0] === "post") {
ids = args["ids"].split(",");
args["id"] = ids.length === 1 ? ids[0] : "posts";
args["full"] = true;
}
else {
ids = args["id"];
}
// Define options
const options = getOptions(args, logger);
// Replace downdir
const downdir = args["downdir"]
.replace("[id]", args["id"])
.replace("[endpoint]", args["_"]);
// Replace depot url
let depotUrl = args["depot"];
if (depotUrl && depotUrl.includes("[uuid]")) {
depotUrl = depotUrl.replace("[uuid]", uuid_1.v4());
if (!args["quiet"]) {
process.stdout.write(depotUrl + "\n");
}
}
// Get s3 bucket
const s3Bucket = args["bucket"];
// Check if outputting to stdout
const printOutput = args["file"] === "-";
// Connect to object storage
let downloadUpload;
let toCSVFunc = download_1.toCSV;
let toJSONFunc = download_1.toJSON;
if (depotUrl) {
// Depot
const depotConfig = {
directory: downdir,
url: depotUrl,
logger,
};
downloadUpload = depotUpload.depot.bind(depotConfig);
toCSVFunc = depotUpload.toCSV.bind(depotConfig);
toJSONFunc = depotUpload.toJSON.bind(depotConfig);
}
else if (s3Bucket) {
// s3
const s3Config = {
bucket: s3Bucket,
directory: downdir,
s3: new aws.S3(),
logger,
};
downloadUpload = s3Upload.s3.bind(s3Config);
toCSVFunc = s3Upload.toCSV.bind(s3Config);
toJSONFunc = s3Upload.toJSON.bind(s3Config);
}
else {
// Download
downloadUpload = download_1.download.bind({
directory: downdir,
logger,
});
}
// Start API
logger.info("Starting API at " + Date.now());
const obj = api_1.createApi(args["_"][0], ids, options);
await obj.start();
// Start download pool
const getPool = new getPool_1.GetPool(args["threads"], downloadUpload);
// Pick between synchronous and parallel downloads
const downloadFunction = args["sync"]
? downloadUpload
: getPool.add.bind(getPool);
// Add pause callback
function handleKeypress(str, key) {
if (key.name === "space") {
obj.pause();
}
else if (key.name === "c" && key.ctrl) {
process.stdout.write("\n");
process.kill(process.pid, "SIGINT");
}
}
process.stdin.on("keypress", handleKeypress);
// Array of urls and filenames
let downloadMedia = [];
// Download posts
const posts = [];
for await (const post of obj.generator()) {
// Add _mediaPath key
if (args["mediaPath"]) {
post["_mediaPath"] = [];
}
// Identify download urls
if (args["download"] && ("node" in post || "shortcode_media" in post)) {
// Check the scraping level
if (args["full"]) {
// Check if album
const postObject = post;
const children = postObject.shortcode_media.edge_sidecar_to_children;
if (children !== undefined) {
for (const child of children.edges) {
const shortcode = child.node.shortcode;
// Check if video
let mediaUrl;
let mediaType;
if (child.node.is_video && args["video"]) {
mediaUrl = child.node.video_url;
mediaType = FILETYPES.VIDEO;
}
else {
mediaUrl = child.node.display_resources.pop().src;
mediaType = FILETYPES.IMAGE;
}
saveMediaMetadata(post, args, downloadMedia, downdir, mediaUrl, shortcode, mediaType);
}
}
else {
const shortcode = postObject.shortcode_media.shortcode;
// Check if video
let mediaUrl;
let mediaType;
if (postObject.shortcode_media.is_video && args["video"]) {
mediaUrl = postObject.shortcode_media.video_url;
mediaType = FILETYPES.VIDEO;
}
else {
mediaUrl = postObject.shortcode_media.display_resources.pop()
.src;
mediaType = FILETYPES.IMAGE;
}
saveMediaMetadata(post, args, downloadMedia, downdir, mediaUrl, shortcode, mediaType);
}
}
else {
const postObject = post;
saveMediaMetadata(post, args, downloadMedia, downdir, postObject.node.thumbnail_src, postObject.node.shortcode, FILETYPES.IMAGE);
}
}
// Save post
posts.push(post);
// Output if required
if (printOutput) {
process.stdout.write(JSON.stringify(post, null, 2) + "\n");
}
// Download the identified media
if (!args["waitDownload"]) {
for (const asset of downloadMedia) {
await downloadFunction(...asset);
}
downloadMedia = [];
}
}
// Download remaining media
for (const asset of downloadMedia) {
await downloadFunction(...asset);
}
// Close download pool
await new Promise((resolve) => {
getPool.close(resolve);
});
await Promise.all(getPool.promises);
// Replace filename
const filename = args["file"]
.replace("[id]", args["id"])
.replace("[endpoint]", args["_"]);
// Save file
if (!printOutput) {
if (args["type"] !== "json") {
let saveFile = filename;
if (args["type"] === "both" || args["file"] === "[id]") {
saveFile += ".csv";
}
await toCSVFunc(posts, saveFile);
}
if (args["type"] !== "csv") {
let saveFile = filename;
if (args["type"] === "both" || args["file"] === "[id]") {
saveFile += ".json";
}
await toJSONFunc(posts, saveFile);
}
}
// Remove pause callback
process.stdin.removeAllListeners("keypress");
// Close logger
logger.close();
}
function saveMediaMetadata(post, args, downloadMedia, downDir, url, shortcode, fileType) {
if (args["mediaPath"]) {
let uri = path.join(downDir, shortcode + "." + fileType);
uri = args["swift"] ? "swift://" + uri : uri;
post["_mediaPath"].push(uri);
}
downloadMedia.push([url, shortcode, fileType]);
}
// Catch key presses
readline.emitKeypressEvents(process.stdin);
if ("setRawMode" in process.stdin) {
process.stdin.setRawMode(true);
}
// Parse args
buildParser(process.argv.slice(2), () => {
process.exit(0);
});
var FILETYPES;
(function (FILETYPES) {
FILETYPES["VIDEO"] = "mp4";
FILETYPES["IMAGE"] = "jpg";
})(FILETYPES || (FILETYPES = {}));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFQSw2Q0FBK0I7QUFDL0IsdUNBQXlCO0FBQ3pCLG1EQUFxQztBQUNyQyxpREFBbUM7QUFFbkMsMkNBQTZCO0FBQzdCLCtCQUFnQztBQUNoQyxvREFBc0M7QUFDdEMsbUNBQThDO0FBRTlDLCtDQUEwQztBQUMxQywwREFBNEM7QUFDNUMsOENBQXdEO0FBQ3hELG9EQUFzQztBQUV0QyxNQUFNLFNBQVMsR0FBRyxDQUFDLElBQUksRUFBRSxFQUFFO0lBQ3ZCLE1BQU0sVUFBVSxHQUFHLEVBQUUsQ0FBQztJQUN0QixJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxNQUFNLEVBQUU7UUFDNUIsVUFBVSxDQUFDLElBQUksQ0FDWCxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDO1lBQ3hCLFFBQVEsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ3pCLEtBQUssRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO1lBQ3RCLE1BQU0sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssTUFBTTtTQUNyQyxDQUFDLENBQ0wsQ0FBQztLQUNMO0lBQ0QsT0FBTyxPQUFPLENBQUMsWUFBWSxDQUFDO1FBQ3hCLEtBQUssRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO1FBQ3RCLE1BQU0sRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssTUFBTTtRQUNsQyxVQUFVO0tBQ2IsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDO0FBRUYsU0FBUyxVQUFVLENBQUMsSUFBSSxFQUFFLE1BQU07SUFDNUIsTUFBTSxPQUFPLEdBQWE7UUFDdEIsY0FBYyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDN0IsY0FBYyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDL0IsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFN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