Last active
October 19, 2024 03:19
-
-
Save Rikezenho/02becf6a3a274c1dc476a0dee805da79 to your computer and use it in GitHub Desktop.
Script to retrieve the followings of an Instagram profile - run it on the profile page in DevTools console
This file contains 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
(async function() { | |
// Check if we have cached results | |
if (window.instagramVerifiedFollowings) { | |
console.log("Using cached results"); | |
displayResults(window.instagramVerifiedFollowings); | |
return; | |
} | |
function extractUserId() { | |
const scripts = document.getElementsByTagName('script'); | |
for (const script of scripts) { | |
const match = script.textContent.match(/"user_id":"(\d+)"/); | |
if (match) { | |
return match[1]; | |
} | |
} | |
throw new Error('Unable to find user ID. Make sure you\'re on an Instagram profile page and logged in.'); | |
} | |
function getCSRFToken() { | |
const csrftoken = document.cookie.match(/csrftoken=([^;]+)/); | |
return csrftoken ? csrftoken[1] : null; | |
} | |
let fetchCount = 0; | |
let totalFollowings = 0; | |
async function fetchFollowings(userId, after = null, followings = []) { | |
try { | |
await new Promise(resolve => setTimeout(resolve, 10)); // 10ms delay | |
fetchCount++; | |
const variables = { | |
id: userId, | |
include_reel: true, | |
fetch_mutual: false, | |
first: 50, | |
after: after | |
}; | |
const csrfToken = getCSRFToken(); | |
if (!csrfToken) { | |
throw new Error('CSRF token not found. Make sure you\'re logged in.'); | |
} | |
const response = await fetch(`/graphql/query/?query_hash=d04b0a864b4b54837c0d870b0e77e076&variables=${JSON.stringify(variables)}`, { | |
headers: { | |
'X-CSRFToken': csrfToken, | |
'X-Instagram-AJAX': '1', | |
'X-IG-App-ID': '936619743392459', // This is a common App ID for Instagram web | |
} | |
}); | |
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
const data = await response.json(); | |
if (!data.data || !data.data.user || !data.data.user.edge_follow) { | |
throw new Error('Unexpected API response structure'); | |
} | |
const edges = data.data.user.edge_follow.edges; | |
followings = followings.concat(edges); | |
const pageInfo = data.data.user.edge_follow.page_info; | |
totalFollowings = data.data.user.edge_follow.count; | |
console.log(`Fetch ${fetchCount} complete. Retrieved ${followings.length}/${totalFollowings} followings.`); | |
if (pageInfo.has_next_page) { | |
return fetchFollowings(userId, pageInfo.end_cursor, followings); | |
} | |
return followings; | |
} catch (error) { | |
console.error(`Error fetching followings: ${error.message}`); | |
return followings; // Return what we have so far | |
} | |
} | |
function displayResults(verifiedProfiles) { | |
const floatingElement = document.createElement('div'); | |
floatingElement.style.cssText = ` | |
position: fixed; | |
top: 20px; | |
right: 20px; | |
width: 80vw; | |
max-width: 800px; | |
height: 80vh; | |
background-color: white; | |
border: 1px solid #ccc; | |
border-radius: 5px; | |
overflow-y: auto; | |
z-index: 9999; | |
padding: 10px; | |
font-family: Arial, sans-serif; | |
`; | |
// Add close button | |
const closeButton = document.createElement('button'); | |
closeButton.innerHTML = '✖'; // X symbol | |
closeButton.style.cssText = ` | |
color: #000; | |
position: absolute; | |
top: 5px; | |
right: 5px; | |
background: none; | |
border: none; | |
font-size: 20px; | |
cursor: pointer; | |
`; | |
closeButton.onclick = () => document.body.removeChild(floatingElement); | |
floatingElement.appendChild(closeButton); | |
// Add view controls | |
const controlsDiv = document.createElement('div'); | |
controlsDiv.style.cssText = ` | |
display: flex; | |
justify-content: center; | |
margin-bottom: 10px; | |
`; | |
const listViewButton = document.createElement('button'); | |
listViewButton.textContent = 'List View'; | |
listViewButton.style.marginRight = '10px'; | |
const gridViewButton = document.createElement('button'); | |
gridViewButton.textContent = 'Grid View'; | |
controlsDiv.appendChild(listViewButton); | |
controlsDiv.appendChild(gridViewButton); | |
floatingElement.appendChild(controlsDiv); | |
// Create content container | |
const contentDiv = document.createElement('div'); | |
floatingElement.appendChild(contentDiv); | |
function renderListView() { | |
contentDiv.innerHTML = ''; | |
contentDiv.style.cssText = ''; // Reset any grid styles | |
verifiedProfiles.forEach(profile => { | |
const profileElement = document.createElement('div'); | |
profileElement.style.cssText = ` | |
display: flex; | |
align-items: center; | |
margin-bottom: 10px; | |
`; | |
profileElement.innerHTML = ` | |
<img src="${profile.profilePic}" alt="${profile.username}" style="width: 50px; height: 50px; border-radius: 50%; margin-right: 10px;"> | |
<a href="https://www.instagram.com/${profile.username}" target="_blank" style="text-decoration: none; color: black;"> | |
<strong>${profile.username}</strong><br> | |
${profile.fullName} | |
</a> | |
`; | |
contentDiv.appendChild(profileElement); | |
}); | |
} | |
function renderGridView() { | |
contentDiv.innerHTML = ''; | |
contentDiv.style.cssText = ` | |
display: grid; | |
grid-template-columns: repeat(4, 1fr); | |
gap: 20px; | |
justify-items: center; | |
padding: 10px; | |
`; | |
verifiedProfiles.forEach(profile => { | |
const profileElement = document.createElement('div'); | |
profileElement.style.cssText = ` | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
text-align: center; | |
width: 100%; | |
`; | |
profileElement.innerHTML = ` | |
<a href="https://www.instagram.com/${profile.username}" target="_blank" style="text-decoration: none; color: black; display: flex; flex-direction: column; align-items: center;"> | |
<img src="${profile.profilePic}" alt="${profile.username}" style="width: 100px; height: 100px; border-radius: 50%; margin-bottom: 5px; object-fit: cover;"> | |
<strong style="display: block; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${profile.username}</strong> | |
</a> | |
`; | |
contentDiv.appendChild(profileElement); | |
}); | |
} | |
listViewButton.onclick = renderListView; | |
gridViewButton.onclick = renderGridView; | |
// Initial render | |
renderListView(); | |
document.body.appendChild(floatingElement); | |
} | |
try { | |
const userId = extractUserId(); | |
console.log("Extracted User ID:", userId); | |
const followings = await fetchFollowings(userId); | |
const verifiedProfiles = followings | |
.filter(edge => edge.node.is_verified) | |
.map(edge => ({ | |
username: edge.node.username, | |
fullName: edge.node.full_name, | |
profilePic: edge.node.profile_pic_url | |
})) | |
.sort((a, b) => a.username.localeCompare(b.username)); | |
// Cache the results | |
window.instagramVerifiedFollowings = verifiedProfiles; | |
console.log(`Found ${verifiedProfiles.length} verified profiles.`); | |
console.log(`Total API calls made: ${fetchCount}`); | |
displayResults(verifiedProfiles); | |
} catch (error) { | |
console.error('Fatal error:', error.message); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Created entirely on Claude 3.5 Sonnet in 10 minutes!