Fetch Instagram posts data by pasting this code in browser console.
(() => {
/* ============================================================
INSTAGRAM PUBLIC PROFILE RESEARCH COLLECTOR
WITH PREVIOUS CSV CONTINUATION
============================================================ */
if (window.igResearchCollectorV3Continue) {
console.log("Instagram collector is already loaded.");
return;
}
/* ============================================================
SETTINGS
SAME STYLE AS ORIGINAL V3
============================================================ */
const SETTINGS = {
// Delay after profile scrolling
scrollDelay: 1800,
// Stop after this many scroll rounds with no new posts
noNewScrollLimit: 7,
// Safety limit
maxScrollRounds: 400,
// Wait after loading individual post
postLoadDelay: 2500,
// Delay between posts
betweenPostsDelay: 2000,
// 0 = process every discovered post
maxPosts: 0
};
/* ============================================================
CSV COLUMNS
SAME DETAILED EXPORT STRUCTURE
============================================================ */
const COLUMNS = [
"row_number",
"profile_username",
"profile_name",
"profile_url",
"profile_description",
"post_shortcode",
"post_url",
"post_type",
"posted_date",
"posted_datetime",
"displayed_date",
"post_caption",
"caption_word_count",
"caption_character_count",
"caption_sentence_count",
"image_alt_text",
"representative_image_url",
"hashtags",
"hashtag_count",
"mentions",
"mention_count",
"visible_likes",
"visible_comments",
"visible_views",
"location",
"raw_visible_post_text",
"raw_meta_description",
"collection_status",
"collection_error",
"collected_at"
];
/* ============================================================
STATE
============================================================ */
const postUrls =
new Map();
/*
* Only NEW records collected during this browser session
* are stored here.
*
* Previous CSV remains as one raw text string.
*/
const results =
[];
/*
* URLs already contained in previous CSV
* plus URLs collected during this session.
*/
const processedUrls =
new Set();
let previousCsvText =
"";
let previousCsvFileName =
"";
let previousRowCount =
0;
let nextRowNumber =
1;
let running =
false;
let stopRequested =
false;
let processingWindow =
null;
/* ============================================================
BASIC HELPERS
============================================================ */
const sleep = ms =>
new Promise(
resolve =>
setTimeout(
resolve,
ms
)
);
const cleanText = (value = "") =>
String(value)
.replace(
/\u00a0/g,
" "
)
.replace(
/\s+/g,
" "
)
.trim();
const unique = values =>
[
...new Set(
values.filter(Boolean)
)
];
function getProfileUsername() {
return location.pathname
.split("/")
.filter(Boolean)[0] || "";
}
function getShortcode(url) {
try {
const parts =
new URL(
url,
location.origin
)
.pathname
.split("/")
.filter(Boolean);
if (
parts[0] === "p" ||
parts[0] === "reel" ||
parts[0] === "reels"
) {
return (
parts[1] ||
""
);
}
const index =
parts.findIndex(
part =>
part === "p" ||
part === "reel" ||
part === "reels"
);
if (
index >= 0 &&
parts[index + 1]
) {
return (
parts[
index + 1
]
);
}
} catch (e) {}
return "";
}
function getMeta(
doc,
property
) {
return (
doc.querySelector(
`meta[property="${property}"]`
)?.content ||
""
);
}
/* ============================================================
FLOATING PANEL STATUS
============================================================ */
function setStatus(message) {
console.log(
"[Instagram Collector]",
message
);
const element =
document.getElementById(
"ig-v3c-status"
);
if (element) {
element.textContent =
message;
}
}
function updateCounters() {
const found =
document.getElementById(
"ig-v3c-found"
);
const processed =
document.getElementById(
"ig-v3c-processed"
);
const imported =
document.getElementById(
"ig-v3c-imported"
);
if (found) {
found.textContent =
`Posts found: ${postUrls.size}`;
}
if (processed) {
processed.textContent =
`Total records: ${
previousRowCount +
results.length
} | New this run: ${
results.length
}`;
}
if (imported) {
imported.textContent =
previousCsvText
? `Previous CSV: ${previousRowCount} rows loaded`
: "Previous CSV: not loaded";
}
}
/* ============================================================
PROFILE INFORMATION
============================================================ */
function getMainProfileInfo() {
const username =
getProfileUsername();
return {
profile_username:
username,
profile_url:
username
? `${location.origin}/${username}/`
: "",
profile_name:
cleanText(
document.querySelector(
"header h2"
)?.innerText ||
document.querySelector(
"header h1"
)?.innerText ||
""
),
profile_description:
cleanText(
getMeta(
document,
"og:description"
)
),
profile_image_url:
getMeta(
document,
"og:image"
)
};
}
const PROFILE =
getMainProfileInfo();
/* ============================================================
CSV PARSER
Handles:
commas
quotes
escaped quotes
multiline caption fields
============================================================ */
function parseCSV(text) {
const rows =
[];
let row =
[];
let field =
"";
let insideQuotes =
false;
for (
let i = 0;
i < text.length;
i++
) {
const char =
text[i];
if (
insideQuotes
) {
if (
char === '"'
) {
if (
text[i + 1] === '"'
) {
field +=
'"';
i++;
} else {
insideQuotes =
false;
}
} else {
field +=
char;
}
continue;
}
if (
char === '"'
) {
insideQuotes =
true;
continue;
}
if (
char === ","
) {
row.push(
field
);
field =
"";
continue;
}
if (
char === "\n"
) {
row.push(
field
);
rows.push(
row
);
row =
[];
field =
"";
continue;
}
if (
char === "\r"
) {
continue;
}
field +=
char;
}
/*
* Final field / row.
*/
if (
field.length ||
row.length
) {
row.push(
field
);
rows.push(
row
);
}
return rows;
}
/* ============================================================
LOAD PREVIOUS CSV
============================================================ */
function loadPreviousCSV() {
if (
running
) {
alert(
"Stop the collector before loading a previous CSV."
);
return;
}
const input =
document.createElement(
"input"
);
input.type =
"file";
input.accept =
".csv,text/csv";
input.style.display =
"none";
input.onchange =
async () => {
const file =
input.files?.[0];
input.remove();
if (!file) {
return;
}
try {
setStatus(
"Reading previous CSV..."
);
const text =
await file.text();
const cleanCsv =
text.replace(
/^\uFEFF/,
""
);
const rows =
parseCSV(
cleanCsv
);
if (
rows.length <
2
) {
throw new Error(
"CSV does not contain post records."
);
}
const headers =
rows[0]
.map(
value =>
cleanText(
value
)
);
/*
* Validate that this is the same
* detailed-post-data CSV format.
*/
for (
let index = 0;
index <
COLUMNS.length;
index++
) {
if (
headers[index] !==
COLUMNS[index]
) {
throw new Error(
`CSV column mismatch at column ${index + 1}. Expected "${COLUMNS[index]}", found "${headers[index] || ""}".`
);
}
}
const postUrlIndex =
headers.indexOf(
"post_url"
);
const rowNumberIndex =
headers.indexOf(
"row_number"
);
if (
postUrlIndex < 0 ||
rowNumberIndex < 0
) {
throw new Error(
"CSV is missing post_url or row_number."
);
}
/*
* Loading a previous CSV starts
* continuation mode fresh.
*/
postUrls.clear();
processedUrls.clear();
results.length =
0;
let imported =
0;
let highestRow =
0;
for (
let index = 1;
index < rows.length;
index++
) {
const row =
rows[index];
/*
* Ignore completely blank final row.
*/
if (
!row.some(
value =>
String(
value ?? ""
).trim()
)
) {
continue;
}
const postUrl =
cleanText(
row[
postUrlIndex
] || ""
);
const rowNumber =
Number(
row[
rowNumberIndex
]
);
if (
postUrl
) {
processedUrls.add(
postUrl
);
}
if (
Number.isFinite(
rowNumber
) &&
rowNumber >
highestRow
) {
highestRow =
rowNumber;
}
imported++;
}
previousCsvText =
cleanCsv
.replace(
/(?:\r?\n)+$/,
""
);
previousCsvFileName =
file.name;
previousRowCount =
imported;
nextRowNumber =
highestRow > 0
? highestRow + 1
: imported + 1;
updateCounters();
setStatus(
`Previous CSV loaded: ${imported} rows. New records will start at row ${nextRowNumber}.`
);
console.log(
`[Instagram Collector] Loaded ${imported} existing rows from ${file.name}.`
);
console.log(
`[Instagram Collector] ${processedUrls.size} existing post URLs will be skipped.`
);
} catch (error) {
console.error(
error
);
alert(
`Could not load CSV:\n\n${
error.message ||
error
}`
);
setStatus(
"Previous CSV could not be loaded."
);
}
};
document.body.appendChild(
input
);
input.click();
}
/* ============================================================
FIND CURRENTLY LOADED POST URLS
ORIGINAL STYLE
============================================================ */
function collectPostUrls() {
let added =
0;
const anchors =
document.querySelectorAll(
'a[href*="/p/"], a[href*="/reel/"]'
);
anchors.forEach(
anchor => {
try {
const url =
new URL(
anchor.href,
location.origin
);
const path =
url.pathname;
if (
!path.includes(
"/p/"
) &&
!path.includes(
"/reel/"
)
) {
return;
}
const cleanUrl =
`${url.origin}${path}`;
if (
!postUrls.has(
cleanUrl
)
) {
postUrls.set(
cleanUrl,
true
);
added++;
}
} catch (e) {}
}
);
updateCounters();
return added;
}
/* ============================================================
AUTO SCROLL PROFILE
ORIGINAL LOGIC
============================================================ */
async function scanProfile() {
setStatus(
"Scanning profile..."
);
collectPostUrls();
let previousCount =
postUrls.size;
let noNewRounds =
0;
for (
let round = 1;
round <=
SETTINGS.maxScrollRounds;
round++
) {
if (
stopRequested
) {
break;
}
window.scrollBy({
top:
Math.floor(
window.innerHeight *
0.85
),
left:
0,
behavior:
"smooth"
});
await sleep(
SETTINGS.scrollDelay
);
collectPostUrls();
const currentCount =
postUrls.size;
if (
currentCount ===
previousCount
) {
noNewRounds++;
} else {
noNewRounds =
0;
previousCount =
currentCount;
}
setStatus(
`Scanning profile: ${currentCount} unique posts found`
);
if (
noNewRounds >=
SETTINGS.noNewScrollLimit
) {
break;
}
if (
SETTINGS.maxPosts >
0 &&
currentCount >=
SETTINGS.maxPosts
) {
break;
}
}
setStatus(
`Profile scan complete: ${postUrls.size} posts found`
);
}
/* ============================================================
WAIT FOR POST PAGE
ORIGINAL V3 LOGIC
============================================================ */
async function waitForPost(win) {
const started =
Date.now();
const timeout =
20000;
while (
Date.now() -
started <
timeout
) {
if (
stopRequested
) {
return false;
}
if (
win.closed
) {
return false;
}
try {
const pathname =
win.location.pathname ||
"";
const bodyText =
cleanText(
win.document.body
?.innerText ||
""
);
if (
pathname.includes(
"/challenge/"
) ||
pathname.includes(
"/accounts/login/"
)
) {
throw new Error(
"Instagram requested login or verification."
);
}
if (
/temporarily blocked|try again later|confirm it's you/i
.test(
bodyText
)
) {
throw new Error(
"Instagram temporarily interrupted the session."
);
}
if (
win.document
.querySelector(
"article"
)
) {
return true;
}
} catch (error) {
if (
/Instagram/i.test(
error.message ||
""
)
) {
throw error;
}
}
await sleep(
500
);
}
return false;
}
/* ============================================================
CAPTION
ORIGINAL EXTRACTION LOGIC
============================================================ */
function extractCaption(
doc,
article
) {
const headings =
[
...article
.querySelectorAll(
"h1"
)
]
.map(
element =>
cleanText(
element.innerText
)
)
.filter(Boolean);
if (
headings.length
) {
return headings.sort(
(
a,
b
) =>
b.length -
a.length
)[0];
}
const description =
cleanText(
getMeta(
doc,
"og:description"
)
);
if (
!description
) {
return "";
}
const quoted =
description.match(
/:\s*["“]([\s\S]*?)["”]\s*$/
);
if (
quoted?.[1]
) {
return cleanText(
quoted[1]
);
}
const colon =
description.indexOf(
":"
);
if (
colon !== -1
) {
return cleanText(
description.substring(
colon + 1
)
)
.replace(
/^["“]|["”]$/g,
""
);
}
return "";
}
/* ============================================================
IMAGE ALT TEXT
============================================================ */
function extractImageAltText(
article
) {
const images =
[
...article
.querySelectorAll(
"img"
)
]
.filter(
img => {
try {
const rect =
img
.getBoundingClientRect();
return (
rect.width >=
180 &&
rect.height >=
180
);
} catch {
return false;
}
}
);
const altTexts =
unique(
images
.map(
img =>
cleanText(
img.alt ||
""
)
)
.filter(Boolean)
);
return altTexts.join(
" | "
);
}
/* ============================================================
REPRESENTATIVE IMAGE
============================================================ */
function extractRepresentativeImage(
doc,
article
) {
const ogImage =
getMeta(
doc,
"og:image"
);
if (
ogImage
) {
return ogImage;
}
const images =
[
...article
.querySelectorAll(
"img"
)
]
.filter(
img => {
try {
const rect =
img
.getBoundingClientRect();
return (
rect.width >=
200 &&
rect.height >=
200
);
} catch {
return false;
}
}
);
return (
images[0]?.currentSrc ||
images[0]?.src ||
""
);
}
/* ============================================================
DATE
============================================================ */
function extractDate(
article
) {
const time =
article.querySelector(
"time[datetime]"
);
const datetime =
time?.getAttribute(
"datetime"
) ||
"";
return {
posted_datetime:
datetime,
posted_date:
datetime
? datetime.substring(
0,
10
)
: "",
displayed_date:
cleanText(
time?.innerText ||
""
)
};
}
/* ============================================================
LOCATION
============================================================ */
function extractLocation(
article
) {
const location =
article.querySelector(
'a[href*="/explore/locations/"]'
);
return cleanText(
location?.innerText ||
""
);
}
/* ============================================================
ENGAGEMENT
============================================================ */
function extractEngagement(
doc,
article
) {
const metaDescription =
cleanText(
getMeta(
doc,
"og:description"
)
);
const visibleText =
cleanText(
article.innerText ||
""
);
let likes =
"";
let comments =
"";
let views =
"";
const likesMatch =
metaDescription.match(
/([\d.,]+(?:\s?[KMB])?)\s+likes?/i
);
if (
likesMatch
) {
likes =
likesMatch[1];
}
const commentsMatch =
metaDescription.match(
/([\d.,]+(?:\s?[KMB])?)\s+comments?/i
);
if (
commentsMatch
) {
comments =
commentsMatch[1];
}
const viewsMatch =
visibleText.match(
/([\d.,]+(?:\s?[KMB])?)\s+views?/i
);
if (
viewsMatch
) {
views =
viewsMatch[1];
}
return {
visible_likes:
likes,
visible_comments:
comments,
visible_views:
views
};
}
/* ============================================================
HASHTAGS / MENTIONS
============================================================ */
function analyzeCaption(
caption
) {
const hashtags =
unique(
caption.match(
/#[\p{L}\p{N}_]+/gu
) ||
[]
);
const mentions =
unique(
caption.match(
/@[\p{L}\p{N}._]+/gu
) ||
[]
);
const words =
caption
.split(
/\s+/
)
.filter(Boolean);
const sentences =
caption
.split(
/[.!?]+/
)
.map(
cleanText
)
.filter(Boolean);
return {
caption_word_count:
words.length,
caption_character_count:
caption.length,
caption_sentence_count:
sentences.length,
hashtags:
hashtags.join(
" | "
),
hashtag_count:
hashtags.length,
mentions:
mentions.join(
" | "
),
mention_count:
mentions.length
};
}
/* ============================================================
POST TYPE
============================================================ */
function detectPostType(
url,
article
) {
if (
url.includes(
"/reel/"
)
) {
return "Reel";
}
if (
article.querySelector(
'svg[aria-label="Next"], svg[aria-label="next"]'
)
) {
return "Carousel";
}
if (
article.querySelector(
"video"
)
) {
return "Video";
}
return "Image";
}
/* ============================================================
ACCOUNT USERNAME FROM POST
============================================================ */
function extractPostUsername(
doc,
article
) {
const ogTitle =
getMeta(
doc,
"og:title"
);
const match =
ogTitle.match(
/@([A-Za-z0-9._]+)/
);
if (
match?.[1]
) {
return match[1];
}
return (
PROFILE.profile_username
);
}
/* ============================================================
PROCESS ONE POST
SAME DETAILED RECORD
============================================================ */
async function processPost(
url,
rowNumber
) {
const record = {
row_number:
rowNumber,
/* PROFILE */
profile_username:
"",
profile_name:
PROFILE.profile_name,
profile_url:
PROFILE.profile_url,
profile_description:
PROFILE.profile_description,
profile_image_url:
PROFILE.profile_image_url,
/* POST */
post_shortcode:
getShortcode(
url
),
post_url:
url,
post_type:
"",
/* DATE */
posted_date:
"",
posted_datetime:
"",
displayed_date:
"",
/* CAPTION */
post_caption:
"",
caption_word_count:
0,
caption_character_count:
0,
caption_sentence_count:
0,
/* IMAGE */
image_alt_text:
"",
representative_image_url:
"",
/* TAGS */
hashtags:
"",
hashtag_count:
0,
mentions:
"",
mention_count:
0,
/* ENGAGEMENT */
visible_likes:
"",
visible_comments:
"",
visible_views:
"",
/* LOCATION */
location:
"",
/* RAW */
raw_visible_post_text:
"",
raw_meta_description:
"",
/* COLLECTION */
collection_status:
"processing",
collection_error:
"",
collected_at:
new Date()
.toISOString()
};
try {
processingWindow.location.href =
url;
const loaded =
await waitForPost(
processingWindow
);
if (
!loaded
) {
throw new Error(
"Post page did not load."
);
}
await sleep(
SETTINGS.postLoadDelay
);
const doc =
processingWindow.document;
const article =
doc.querySelector(
"article"
);
if (
!article
) {
throw new Error(
"Instagram post content was not found."
);
}
/* PROFILE */
const username =
extractPostUsername(
doc,
article
);
record.profile_username =
username;
record.profile_url =
username
? `https://www.instagram.com/${username}/`
: PROFILE.profile_url;
/* POST TYPE */
record.post_type =
detectPostType(
url,
article
);
/* DATE */
const date =
extractDate(
article
);
record.posted_date =
date.posted_date;
record.posted_datetime =
date.posted_datetime;
record.displayed_date =
date.displayed_date;
/* CAPTION */
const caption =
extractCaption(
doc,
article
);
record.post_caption =
caption;
const captionData =
analyzeCaption(
caption
);
record.caption_word_count =
captionData
.caption_word_count;
record.caption_character_count =
captionData
.caption_character_count;
record.caption_sentence_count =
captionData
.caption_sentence_count;
/* IMAGE ALT */
record.image_alt_text =
extractImageAltText(
article
);
/* IMAGE URL */
record.representative_image_url =
extractRepresentativeImage(
doc,
article
);
/* TAGS */
record.hashtags =
captionData.hashtags;
record.hashtag_count =
captionData
.hashtag_count;
record.mentions =
captionData.mentions;
record.mention_count =
captionData
.mention_count;
/* ENGAGEMENT */
const engagement =
extractEngagement(
doc,
article
);
record.visible_likes =
engagement
.visible_likes;
record.visible_comments =
engagement
.visible_comments;
record.visible_views =
engagement
.visible_views;
/* LOCATION */
record.location =
extractLocation(
article
);
/* RAW */
record.raw_visible_post_text =
cleanText(
article.innerText ||
""
);
record.raw_meta_description =
cleanText(
getMeta(
doc,
"og:description"
)
);
record.collection_status =
"success";
} catch (error) {
record.collection_status =
"error";
record.collection_error =
cleanText(
error.message ||
String(error)
);
}
return record;
}
/* ============================================================
PROCESS ONLY POSTS NOT ALREADY IN PREVIOUS CSV
============================================================ */
async function processAllPosts() {
let urls =
[
...postUrls.keys()
];
/*
* IMPORTANT:
* skip everything already contained
* in previous CSV.
*/
urls =
urls.filter(
url =>
!processedUrls.has(
url
)
);
if (
SETTINGS.maxPosts >
0
) {
urls =
urls.slice(
0,
SETTINGS.maxPosts
);
}
if (
!urls.length
) {
setStatus(
"No new posts to process."
);
return;
}
setStatus(
`Starting from row ${nextRowNumber}. ${urls.length} new posts remaining.`
);
for (
let index = 0;
index < urls.length;
index++
) {
if (
stopRequested
) {
break;
}
const url =
urls[index];
setStatus(
`Processing new post ${index + 1} / ${urls.length} | CSV row ${nextRowNumber}`
);
const record =
await processPost(
url,
nextRowNumber
);
results.push(
record
);
/*
* Mark as processed so Start cannot
* collect it again during this session.
*/
processedUrls.add(
url
);
nextRowNumber++;
updateCounters();
/*
* Same safety behavior:
* stop on Instagram interruption.
*/
if (
record.collection_error &&
/verification|temporarily|login|interrupted/i
.test(
record.collection_error
)
) {
stopRequested =
true;
setStatus(
"Instagram interrupted collection. Existing data can still be downloaded."
);
break;
}
await sleep(
SETTINGS.betweenPostsDelay
);
}
if (
!stopRequested
) {
setStatus(
`Finished. ${results.length} new posts added.`
);
}
}
/* ============================================================
CSV EXPORT
PREVIOUS CSV + NEW ROWS
============================================================ */
function escapeCSV(value) {
return (
'"' +
String(
value ?? ""
)
.replace(
/"/g,
'""'
) +
'"'
);
}
function buildNewCsvRows() {
return results.map(
record =>
COLUMNS
.map(
column =>
escapeCSV(
record[
column
]
)
)
.join(",")
);
}
function downloadCSV() {
if (
!previousCsvText &&
!results.length
) {
alert(
"No detailed post data has been collected or imported."
);
return;
}
const newRows =
buildNewCsvRows();
let parts =
[
"\uFEFF"
];
/*
* Previous CSV already contains
* the header and old rows.
*/
if (
previousCsvText
) {
parts.push(
previousCsvText
);
if (
newRows.length
) {
parts.push(
"\n" +
newRows.join(
"\n"
)
);
}
} else {
/*
* No previous CSV:
* normal fresh export.
*/
parts.push(
COLUMNS.join(
","
)
);
if (
newRows.length
) {
parts.push(
"\n" +
newRows.join(
"\n"
)
);
}
}
const blob =
new Blob(
parts,
{
type:
"text/csv;charset=utf-8;"
}
);
const objectUrl =
URL.createObjectURL(
blob
);
const link =
document.createElement(
"a"
);
link.href =
objectUrl;
link.download =
`${
PROFILE.profile_username ||
"instagram"
}-detailed-post-data-continued.csv`;
document.body.appendChild(
link
);
link.click();
link.remove();
URL.revokeObjectURL(
objectUrl
);
}
/* ============================================================
STOP
============================================================ */
function stop() {
stopRequested =
true;
setStatus(
"Stopping..."
);
}
/* ============================================================
CLEAR
============================================================ */
function clearData() {
if (
running
) {
alert(
"Stop the collector before clearing data."
);
return;
}
const confirmed =
confirm(
"Clear imported CSV data, discovered posts, and new collected records?"
);
if (
!confirmed
) {
return;
}
postUrls.clear();
processedUrls.clear();
results.length =
0;
previousCsvText =
"";
previousCsvFileName =
"";
previousRowCount =
0;
nextRowNumber =
1;
updateCounters();
setStatus(
"All collector data cleared."
);
}
/* ============================================================
START FULL PROCESS
============================================================ */
async function start() {
if (
running
) {
console.log(
"Collector is already running."
);
return;
}
running =
true;
stopRequested =
false;
processingWindow =
window.open(
"about:blank",
"instagramResearchWindow",
"width=1100,height=850"
);
if (
!processingWindow
) {
running =
false;
alert(
"Please allow pop-ups for instagram.com and click Start again."
);
return;
}
try {
/*
* Same previous V3 flow:
*
* scan profile fully
* then process posts
*
* Difference:
* imported URLs are skipped.
*/
await scanProfile();
if (
!stopRequested
) {
await processAllPosts();
}
/*
* Automatic final download.
*/
if (
!stopRequested
) {
setStatus(
`Finished. ${
previousRowCount +
results.length
} total rows. Downloading CSV...`
);
await sleep(
500
);
downloadCSV();
setStatus(
`Finished. ${
previousRowCount +
results.length
} total rows. CSV downloaded.`
);
}
} catch (error) {
setStatus(
`Stopped: ${error.message}`
);
console.error(
error
);
} finally {
running =
false;
}
}
/* ============================================================
FLOATING PANEL
============================================================ */
const panel =
document.createElement(
"div"
);
panel.style.cssText = `
position:fixed;
right:20px;
bottom:20px;
width:305px;
padding:16px;
background:#ffffff;
color:#111;
border:1px solid #ddd;
border-radius:14px;
box-shadow:
0 8px 35px rgba(0,0,0,.24);
font-family:
Arial,sans-serif;
font-size:14px;
z-index:2147483647;
`;
panel.innerHTML = `
<div
style="
font-size:16px;
font-weight:700;
margin-bottom:3px;
"
>
Instagram Post Research
</div>
<div
style="
font-size:12px;
color:#666;
margin-bottom:12px;
"
>
Continue From Previous CSV
</div>
<div
id="ig-v3c-imported"
style="
margin-bottom:4px;
"
>
Previous CSV: not loaded
</div>
<div
id="ig-v3c-found"
style="
margin-bottom:4px;
"
>
Posts found: 0
</div>
<div
id="ig-v3c-processed"
style="
margin-bottom:8px;
"
>
Total records: 0 | New this run: 0
</div>
<div
id="ig-v3c-status"
style="
min-height:42px;
padding:8px;
margin-bottom:10px;
background:#f5f5f5;
border-radius:7px;
color:#555;
font-size:12px;
"
>
Ready
</div>
<button
id="ig-v3c-load"
style="
width:100%;
padding:10px;
margin-bottom:7px;
border:0;
border-radius:7px;
background:#262626;
color:#fff;
font-weight:700;
cursor:pointer;
"
>
Load Previous CSV
</button>
<button
id="ig-v3c-start"
style="
width:100%;
padding:11px;
margin-bottom:7px;
border:0;
border-radius:7px;
background:#0095f6;
color:#fff;
font-weight:700;
cursor:pointer;
"
>
Start / Continue Collection
</button>
<button
id="ig-v3c-stop"
style="
width:100%;
padding:9px;
margin-bottom:7px;
cursor:pointer;
"
>
Stop
</button>
<button
id="ig-v3c-download"
style="
width:100%;
padding:10px;
margin-bottom:7px;
cursor:pointer;
"
>
Download CSV
</button>
<button
id="ig-v3c-clear"
style="
width:100%;
padding:8px;
cursor:pointer;
"
>
Clear Data
</button>
`;
document.body.appendChild(
panel
);
/* ============================================================
BUTTON EVENTS
============================================================ */
document
.getElementById(
"ig-v3c-load"
)
.addEventListener(
"click",
loadPreviousCSV
);
document
.getElementById(
"ig-v3c-start"
)
.addEventListener(
"click",
start
);
document
.getElementById(
"ig-v3c-stop"
)
.addEventListener(
"click",
stop
);
document
.getElementById(
"ig-v3c-download"
)
.addEventListener(
"click",
downloadCSV
);
document
.getElementById(
"ig-v3c-clear"
)
.addEventListener(
"click",
clearData
);
/* ============================================================
CONSOLE API
============================================================ */
window.igResearchCollectorV3Continue = {
start,
stop,
loadPreviousCSV,
download:
downloadCSV,
clear:
clearData,
getNewData:
() => results,
getPostUrls:
() =>
[
...postUrls.keys()
],
getProcessedUrls:
() =>
[
...processedUrls
],
settings:
SETTINGS
};
collectPostUrls();
updateCounters();
console.log(`
Instagram V3 Continue Collector loaded.
IF STARTING FRESH:
1. Click Start / Continue Collection.
2. CSV automatically downloads when finished.
IF CONTINUING FROM YOUR EXISTING FILE:
1. Click Load Previous CSV.
2. Select:
champions_ride-detailed-post-data (1).csv
3. Wait for message showing imported rows.
4. Click Start / Continue Collection.
Example:
Previous CSV ends at row 1327
→ existing post URLs are skipped
→ next new post becomes row 1328
→ row 1329
→ row 1330
→ etc.
FINAL DOWNLOAD:
Old CSV rows
+
new collected rows
=
one combined continued CSV.
Manual options are also available:
Stop
Download CSV
Clear Data
`);
})();