Perché lo Scraping di Instagram è Così Difficile
Se hai mai provato a raccogliere dati da Instagram a scala, sai già quanto possa essere frustrante. Quello che sembra un semplice GET request su una pagina profilo si trasforma rapidamente in un labirinto di redirect, challenge JavaScript e IP bannati. Instagram è una delle piattaforme più ostiche al mondo per l'estrazione automatizzata di dati, e non è un caso: Meta investe massicciamente in infrastrutture anti-bot.
Prima di addentrarci nelle soluzioni tecniche, un avvertimento fondamentale: rispetta sempre i Termini di Servizio di Instagram e le leggi applicabili (CFAA negli Stati Uniti, GDPR nell'UE, e le normative locali). Questa guida si concentra esclusivamente sull'accesso a dati pubblici, senza tentare login automation o bypass di paywall. Se un dato richiede autenticazione, non tentare di accedervi con scraping.
Le Difese di Instagram: Un Quadro Tecnico
Instagram utilizza un sistema di difesa a strati multipli:
- Rate limiting aggressivo: Un singolo IP può fare al massimo qualche decina di richieste prima di ricevere HTTP 429 o essere redirectato alla pagina di login.
- Login wall: Molte sezioni del sito richiedono autenticazione per essere visualizzate. Le pagine pubbliche esistono, ma l'accesso programmatico viene filtrato diversamente rispetto a un browser umano.
- Device fingerprinting: Instagram raccoglie user-agent, risoluzione dello schermo, plugin del browser, e persino caratteristiche della connessione TLS per profilare i client.
- Anti-bot JavaScript challenge: Le pagine HTML sono spesso shell vuote che richiedono esecuzione JavaScript per caricare il contenuto reale.
- IP reputation: Gli IP datacenter vengono flaggati quasi immediatamente. Le richieste da ASN di hosting (AWS, DigitalOcean, Hetzner) ricevono un trattamento molto più restrittivo.
Il risultato? Un approccio naïve con
requests.get()e un IP datacenter dura meno di 50 richieste prima di essere bloccato. Per costruire una pipeline affidabile, serve una strategia completamente diversa.
Che Cosa È Accessibile Senza Login
Non tutto è dietro il login wall. Instagram espone ancora alcuni dati pubblicamente, sia attraverso il sito web che tramite endpoint API interni:
- Pagine profilo pubblico: Username, bio, numero di follower/following, post count, e i post più recenti per account pubblici.
- Pagine hashtag: I post più recenti e più popolari associati a un hashtag, visibili senza login tramite l'URL
/explore/tags/{hashtag}. - Pagine località: Post geotaggati associati a un luogo, accessibili tramite
/explore/locations/{id}. - Feed Reels: Alcuni metadati dei Reels sono accessibili tramite endpoint mobili.
- Immagini e video singoli: I media individuali di account pubblici hanno URL diretti accessibili.
Quello che non è accessibile senza login include: DM, Stories, follower list completa, commenti annidati profondi, e qualsiasi contenuto di account privati. Non tentare di bypassare queste restrizioni.
Perché i Proxy Residenziali Sono Essenziali per Instagram
Instagram classifica gli IP in base all'ASN (Autonomous System Number). Un IP proveniente da Comcast, Vodafone o Telecom Italia viene trattato come un utente domestico legittimo. Un IP da AWS o OVH viene immediatamente marcato come sospetto.
La differenza è drammatica:
| Caratteristica | Proxy Datacenter | Proxy Residenziali |
|---|---|---|
| ASN reputation | Hosting/Cloud provider — immediatamente flaggato | ISP domestici — trattato come utente reale |
| Richieste prima del ban (IG) | 20–50 | 200–500+ (con rotazione) |
| Rischio di CAPTCHA | Alto | Basso–Medio |
| Velocità | Molto alta | Media |
| Costo | Basso | Medio–Alto |
| Geo-targeting preciso | Limited | Paese, città, ASN |
Per Instagram, i proxy residenziali sono praticamente obbligatori. Un pool rotante di IP residenziali permette di distribuire le richieste su centinaia o migliaia di identità IP diverse, ciascuna delle quali appare come un utente domestico legittimo.
I proxy mobili (3G/4G/5G) offrono un livello di affidabilità ancora superiore per Instagram, poiché l'app mobile è il client primario della piattaforma. Tuttavia, il costo è significativamente più alto e la banda è limitata.
Regola pratica: per scraping di massa su Instagram, usa proxy residenziali con rotazione per-request. Per task ad alta criticità (come monitoraggio di account specifici), considera sessioni sticky su proxy mobili.
Implementazione Python: Requests + Proxy Residenziali Rotanti
Vediamo un'implementazione pratica usando requests con il pool di proxy residenziali di ProxyHat. L'obiettivo è scrapare pagine profilo pubbliche con rotazione IP, user-agent rotation e session isolation.
Configurazione Base
import requests
import random
import time
from urllib.parse import quote
# --- Configurazione ProxyHat ---
PROXYHAT_USER = "your_username"
PROXYHAT_PASS = "your_password"
PROXYHAT_GATE = "gate.proxyhat.com"
PROXYHAT_PORT = 8080
def get_proxy_url(country=None, session_id=None):
"""Costruisce l'URL del proxy con geo-targeting e sessione opzionali."""
username = PROXYHAT_USER
if country:
username = f"{username}-country-{country}"
if session_id:
username = f"{username}-session-{session_id}"
return f"http://{username}:{PROXYHAT_PASS}@{PROXYHAT_GATE}:{PROXYHAT_PORT}"
# --- User-Agent Rotation ---
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 "
"(KHTML, like Gecko) Version/17.4 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) "
"Gecko/20100101 Firefox/126.0",
]
def get_headers():
"""Headers realistici per simulare traffico browser."""
return {
"User-Agent": random.choice(USER_AGENTS),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
}
Scraping di un Profilo Pubblico con Rotazione IP
def scrape_profile(username, max_retries=3):
"""Scrape i dati base di un profilo Instagram pubblico."""
url = f"https://www.instagram.com/{username}/"
for attempt in range(max_retries):
# Ogni tentativo usa un IP residenziale diverso (rotazione per-request)
proxy = {
"http": get_proxy_url(country="US"),
"https": get_proxy_url(country="US"),
}
try:
# Crea una nuova sessione per ogni tentativo (session isolation)
session = requests.Session()
session.headers.update(get_headers())
# Prima visita la homepage per ottenere cookie (simula navigazione reale)
session.get(
"https://www.instagram.com/",
proxies=proxy,
timeout=15,
)
time.sleep(random.uniform(1.5, 3.5))
# Ora visita il profilo target
resp = session.get(url, proxies=proxy, timeout=15)
if resp.status_code == 200 and "</html>" in resp.text.lower():
# Estrai i dati dal JSON embedded nella pagina
import re, json
match = re.search(
r'window\._sharedData\s*=\s*({.+?});</script>',
resp.text
)
if match:
data = json.loads(match.group(1))
user_data = (
data
.get("entry_data", {})
.get("ProfilePage", [{}])[0]
.get("graphql", {})
.get("user", {})
)
return {
"username": user_data.get("username"),
"full_name": user_data.get("full_name"),
"bio": user_data.get("biography"),
"followers": user_data.get("edge_followed_by", {}).get("count"),
"following": user_data.get("edge_follow", {}).get("count"),
"posts": user_data.get("edge_owner_to_timeline_media", {}).get("count"),
"is_private": user_data.get("is_private"),
"is_verified": user_data.get("is_verified"),
}
elif resp.status_code == 429:
wait = (2 ** attempt) + random.uniform(1, 5)
print(f"Rate limited. Attesa {wait:.1f}s...")
time.sleep(wait)
continue
else:
print(f"Status {resp.status_code}, tentativo {attempt+1}")
except requests.RequestException as e:
print(f"Errore: {e}, tentativo {attempt+1}")
time.sleep(2 ** attempt)
return None
# --- Esempio di utilizzo ---
result = scrape_profile("nasa")
if result:
print(f"Profilo: {result['username']}")
print(f"Follower: {result['followers']:,}")
print(f"Post: {result['posts']:,}")
Scraping Batch con Rate Limiting Autogestito
def scrape_profiles_batch(usernames, requests_per_minute=15):
"""Scrape multipli profili con rate limiting autogestito."""
delay = 60.0 / requests_per_minute # ~4s tra richieste
results = []
for i, username in enumerate(usernames):
print(f"[{i+1}/{len(usernames)}] Scraping @{username}...")
data = scrape_profile(username)
if data:
results.append(data)
print(f" ✓ {data['followers']:,} follower")
else:
print(f" ✗ Fallito")
# Rate limiting: non superare 15 req/min
jitter = random.uniform(0.5, 2.0)
time.sleep(delay + jitter)
return results
# Esecuzione
profiles = ["nasa", "natgeo", "bbc", "cnn", "spacex"]
data = scrape_profiles_batch(profiles, requests_per_minute=12)
Peculiarità Tecniche di Instagram: Cosa Devi Sapere
L'Endpoint ?__a=1 — Storia e Declino
Per anni, aggiungere ?__a=1 a qualsiasi URL di Instagram restituiva un JSON pulito con tutti i dati della pagina. Era il metodo preferito da ogni scraper. Purtroppo, Instagram ha deprecato e poi rimosso questo endpoint nella sua forma originale. Oggi, ?__a=1 richiede autenticazione e restituisce dati parziali o reindirizza al login.
La lezione: non dipendere da endpoint non documentati. Instagram li rimuove senza preavviso.
Query GraphQL e Header Specifici
Instagram usa GraphQL internamente per caricare i dati. Le query GraphQL richiedono header specifici che fungono da autenticazione implicita:
x-ig-app-id: L'ID dell'app Instagram web (tipicamente936619743392459). Senza questo header, le richieste API vengono rifiutate.x-csrftoken: Un token CSRF che Instagram imposta come cookie. Deve essere incluso negli header delle richieste POST e nelle query GraphQL.x-requested-with: Impostato aXMLHttpRequestper le chiamate AJAX.
# Esempio di chiamata GraphQL con header richiesti
def query_hashtag(tag_name, csrftoken, proxy_dict):
"""Query GraphQL per i post di un hashtag (dati pubblici limitati)."""
variables = json.dumps({
"tag_name": tag_name,
"first": 12,
})
params = {
"query_hash": "f92f56d2f3e0d7f4ec8f7c1d2c6e6b3a", # hash soggetto a cambiamento
"variables": variables,
}
headers = {
"x-ig-app-id": "936619743392459",
"x-csrftoken": csrftoken,
"x-requested-with": "XMLHttpRequest",
"User-Agent": random.choice(USER_AGENTS),
}
resp = requests.get(
"https://www.instagram.com/graphql/query/",
params=params,
headers=headers,
proxies=proxy_dict,
timeout=15,
)
return resp.json()
Attenzione: i query hash cambiano frequentemente. Instagram aggiorna questi valori con ogni deploy del frontend. Non hardcodarli — estraili dinamicamente dal JavaScript della pagina o tieni un sistema di fallback.
HTTPS Pinning e Reverse Engineering dell'API Mobile
L'app mobile di Instagram usa SSL pinning, ovvero verifica il certificato del server contro un hash hardcoded. Questo rende il man-in-the-middle dell'API mobile molto più complesso rispetto all'intercettazione del traffico web.
La tendenza nell'ecosistema dello scraping è passata dal parsing HTML all'reverse engineering dell'API mobile. L'app mobile espone endpoint più ricchi e stabili del sito web, ma accedervi richiede:
- Il bypass del SSL pinning (strumenti come Frida o Objection)
- La replica esatta della firma delle richieste (header
X-IG-Device-ID,X-IG-Android-ID,IG-Connection-Type, ecc.) - La gestione del challenge di login per molti endpoint
Questo approccio è significativamente più complesso e si avvicina al confine etico e legale. Per la maggior parte dei casi d'uso di social listening, i dati pubblici accessibili via web sono sufficienti.
Il Shift: Da HTML Scraping a Mobile API
Il panorama dello scraping di Instagram è cambiato radicalmente negli ultimi anni:
- 2018–2020: HTML scraping con
?__a=1, semplice e affidabile. - 2020–2022: Deprecation di
?__a=1, shift verso GraphQL query scraping. - 2022–2024: Endpoints GraphQL sempre più protetti, necessità di proxy residenziali obbligatoria.
- 2024+: Mobile API reverse engineering come approccio dominante, ma con barriere all'ingresso molto alte.
La strategia consigliata per il 2025: usa il parsing HTML del sito web per dati di base (profilo, bio, follower count) e integra con l'API ufficiale di Instagram per dati più dettagliati quando il budget lo permette.
Esempio Node.js: Profilo con Axios e Proxy Residenziali
const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');
const PROXYHAT_USER = 'your_username';
const PROXYHAT_PASS = 'your_password';
// Proxy residenziale HTTP con geo-targeting Italia
const proxyUrl = `http://${PROXYHAT_USER}-country-IT:${PROXYHAT_PASS}@gate.proxyhat.com:8080`;
const USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
];
async function scrapeInstagramProfile(username) {
const url = `https://www.instagram.com/${username}/`;
try {
// Prima richiesta alla homepage per cookie
await axios.get('https://www.instagram.com/', {
proxy: false,
httpsAgent: new (require('https-proxy-agent'))(proxyUrl),
headers: {
'User-Agent': USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)],
'Accept-Language': 'it-IT,it;q=0.9,en;q=0.8',
},
timeout: 15000,
});
await new Promise(r => setTimeout(r, 2000 + Math.random() * 2000));
// Richiesta al profilo
const resp = await axios.get(url, {
proxy: false,
httpsAgent: new (require('https-proxy-agent'))(proxyUrl),
headers: {
'User-Agent': USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)],
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'it-IT,it;q=0.9',
},
timeout: 15000,
});
// Parsing base del profilo
const followerMatch = resp.data.match(/"edge_followed_by":\{"count":(\d+)\}/);
const postMatch = resp.data.match(/"edge_owner_to_timeline_media":\{"count":(\d+)\}/);
return {
username,
followers: followerMatch ? parseInt(followerMatch[1]) : null,
posts: postMatch ? parseInt(postMatch[1]) : null,
};
} catch (err) {
console.error(`Errore per @${username}:`, err.message);
return null;
}
}
scrapeInstagramProfile('nasa').then(console.log);
Strategie Anti-Rilevamento Oltre i Proxy
I proxy residenziali risolvono il problema dell'IP reputation, ma non sono sufficienti da soli. Ecco le pratiche essenziali per evitare il rilevamento:
- Session isolation: Non riutilizzare mai cookie o sessioni tra richieste verso target diversi. Ogni richiesta o piccolo batch deve avere la sua sessione pulita.
- Simula il comportamento umano: Visita la homepage prima del profilo target. Aggiungi delay realistici (2–5 secondi tra richieste). Non fare burst di 50 richieste in 2 secondi.
- User-Agent rotation: Mantieni un pool di almeno 10–20 user-agent realistici e aggiornati. Assicurati che gli altri header siano coerenti con l'UA scelto.
- Geo-consistenza: Se scrapi profili italiani, usa proxy italiani. Un utente con UA italiano ma IP americano è sospetto.
- Rate limiting autogestito: Limitati a 10–20 richieste per minuto per IP. È meglio andare piano e non essere bloccati che andare veloci e dover cambiare IP ogni 5 minuti.
Quando Usare le API Ufficiali Invece dello Scraping
Meta offre l'Instagram Graph API per i business account. Se il tuo caso d'uso lo permette, è sempre preferibile:
- Stabilità: Le API ufficiali non cambiano senza preavviso (almeno, non senza un deprecation cycle).
- Legalità: L'accesso tramite API ufficiali è esplicitamente autorizzato dai ToS.
- Dati strutturati: JSON pulito, documentazione, pagination nativa.
I limiti principali dell'API ufficiale:
- Richiede un Facebook Business account e un'app approvata.
- Può accedere solo ai dati dei business/creator account (non dei profili personali).
- Rate limits stringenti ma prevedibili (200 chiamate/ora per utente).
- Non permette l'accesso ai dati di hashtag o location nella stessa misura dello scraping web.
La regola d'oro: se l'API ufficiale copre il tuo caso d'uso, usala. Lo scraping è giustificato solo quando l'API non offre i dati necessari (es. hashtag exploration, profili non-business, dati storici).
Scraping Etico: Linee Guida Pratiche
Lo scraping di dati pubblici è una zona grigia legale. Ecco come restare dalla parte giusta:
- Rispetta
robots.txt: Instagram permette l'accesso limitato ai bot. Controlla semprehttps://www.instagram.com/robots.txtprima di iniziare. - Rate-limitati da solo: Non superare 10–15 richieste al minuto per IP. Se devi scrapare migliaia di profili, usa un pool grande di proxy e distribuisci le richieste nel tempo.
- Mai login automation: Non tentare di automatizzare il login, creare account finti, o usare credenziali rubate. Questo viola i ToS e potenzialmente leggi come il CFAA.
- Dati pubblici solo: Se un profilo è privato, non tentare di accedervi. Se un dato richiede login, non è pubblico.
- Non archiviare PII: Riduci al minimo i dati personali che memorizzi. Se hai bisogno di username per analytics, non hai bisogno di email o numeri di telefono.
- GDPR compliance: Se processi dati di utenti europei, assicurati di avere una base legale e rispetta i diritti di cancellazione.
- Usa i dati responsabilmente: Non costruire database per stalking, harassment, o manipolazione. Lo scraping per social listening, ricerca accademica, e analytics di mercato è generalmente accettato.
Punti Chiave
- Instagram è una delle piattaforme più difficili da scrapare: rate limiting, login wall, device fingerprinting e IP reputation rendono l'approccio naïve impraticabile.
- I proxy residenziali sono essenziali: gli IP datacenter vengono flaggati quasi istantaneamente da Instagram.
- I dati accessibili senza login includono profili pubblici, hashtag, località e alcuni metadati dei Reels.
- L'endpoint
?__a=1è deprecato; il parsing HTML e le query GraphQL sono gli approcci attuali, ma richiedono header specifici (x-ig-app-id,x-csrftoken).- Session isolation, user-agent rotation, geo-consistenza e rate limiting autogestito sono indispensabili quanto i proxy stessi.
- Se l'API ufficiale copre il tuo caso d'uso, usala. Lo scraping è l'ultima risorsa, non la prima.
- Scraping etico: rispetta robots.txt, non fare login automation, accedi solo a dati pubblici, e conformati al GDPR.
Se stai costruendo una pipeline di social listening e hai bisogno di proxy residenziali affidabili per Instagram, dai un'occhiata alla nostra pagina prezzi o esplora le location disponibili per il geo-targeting. Per casi d'uso più generali di web scraping, consulta la nostra guida su web scraping con proxy residenziali.






