Nota importante: Questo articolo si concentra esclusivamente sull'accesso a dati pubblicamente disponibili su Instagram. Rispetta sempre i Termini di Servizio di Instagram, il file robots.txt, e le leggi applicabili come il CFAA negli Stati Uniti, il GDPR nell'UE, e il DLGS 196/2003 in Italia. L'automazione non autorizzata dell'accesso a sistemi informatici può costituire un reato. Non tentare mai di automatizzare il login o di accedere a dati privati.
Perché Instagram è Difficile da Scrapare su Scala
Instagram rappresenta una delle sfide più complesse per chi costruisce pipeline di data extraction. A differenza di molti siti web, Meta ha investito massicciamente in sistemi anti-automazione sofisticati che vanno oltre i semplici rate limit.
Rate Limiting Aggressivo
Instagram applica limiti rigorosi sulle richieste per indirizzo IP. Superare queste soglie comporta blocchi temporanei (HTTP 429) o permanenti. I limiti esatti non sono pubblici, ma le evidenze empiriche suggeriscono:
- Utenti non loggati: circa 50-100 richieste per ora per IP
- Pagine profilo: limiti più severi, spesso 20-30 richieste per ora
- Hashtag e location: limiti variabili basati sul volume di contenuti
Login Wall e Accesso Limitato
Progressivamente, Instagram ha ridotto i contenuti accessibili senza autenticazione. Molte sezioni richiedono ora il login per essere visualizzate, rendendo lo scraping anonimo più restrittivo. Questo è intenzionale: Meta vuole tracciare chi accede ai dati.
Sistemi Anti-Bot e Device Fingerprinting
Instagram utilizza tecniche avanzate di fingerprinting:
- TLS fingerprinting: analisi del handshake TLS per identificare client HTTP vs browser reali
- JavaScript challenges: verifiche che richiedono esecuzione JS
- Behavioral analysis: pattern di navigazione, timing delle richieste, sequenze di pagine visitate
- Header analysis: verifica coerenza tra User-Agent, Accept headers, e comportamento
Il fingerprinting TLS è particolarmente efficace: librerie come
requestsohttpxhanno firme TLS diverse dai browser reali, permettendo a Instagram di bloccarle anche con header perfetti.
IP Reputation e Datacenter Blocking
Instagram mantiene database di reputazione IP. Gli IP dei datacenter (AWS, GCP, Azure, DigitalOcean) hanno reputazione intrinsecamente bassa perché associati all'automazione. Gli IP residenziali, invece, appartengono a veri ISP domestici e sono significativamente meno sospetti.
Cosa è Accessibile Senza Login
Nonostante le restrizioni, diversi contenuti rimangono accessibili pubblicamente:
Pagine Profilo Pubbliche
I profili pubblici (non privati) mostrano: username, nome visualizzato, bio, numero di follower/following, link nel profilo, e i post più recenti (tipicamente 12-18). I profili privati mostrano solo informazioni di base.
Pagine Hashtag
Le pagine hashtag (instagram.com/explore/tags/nome) mostrano una griglia di post popolari e recenti. Tuttavia, Instagram ha ristretto l'accesso senza login: spesso solo i primi risultati sono visibili.
Pagine Location
Le pagine località mostrano post geotaggati in un luogo specifico. L'accesso è simile alle pagine hashtag, con limitazioni crescenti per utenti non autenticati.
Reels e Contenuti Video
I Reels hanno endpoint dedicati ma con accesso fortemente limitato senza login. La maggior parte dei contenuti video richiede autenticazione per essere visualizzata.
Endpoint JSON (Legacy)
Historicamente, l'aggiunta di ?__a=1 a qualsiasi URL Instagram restituiva JSON strutturato. Meta ha progressivamente limitato questo endpoint:
- 2020: rimosso l'accesso per utenti non loggati
- 2021: richiede header specifici anche per utenti loggati
- 2024: quasi completamente bloccato per automazione
Perché i Proxy Residenziali sono Essenziali per Instagram
La scelta del tipo di proxy è critica per Instagram. A differenza di altri target dove i datacenter proxy funzionano accettabilmente, Instagram li blocca sistematicamente.
| Caratteristica | Datacenter Proxy | Residential Proxy | Mobile Proxy |
|---|---|---|---|
| Rilevamento IP | Alto rischio | Basso rischio | Minimo rischio |
| Reputazione | Povera | Alta (ISP reali) | Massima (carrier mobili) |
| Costo | Basso | Medio | Alto |
| Success rate IG | 10-30% | 70-90% | 85-98% |
| Rotazione IP | Limitata | Flessibile | Naturale |
Il Problema dei Datacenter IP
Gli IP datacenter sono identificabili tramite ASN (Autonomous System Number). Instagram sa che un IP proveniente da AWS, Google Cloud, o Hetzner non è un utente reale che naviga da casa. Questi IP vengono:
- Pre-emptivamente bloccati in molti casi
- Sottoposti a challenge più severe
- Rate limited più aggressivamente
Vantaggio dei Residential Proxy
I proxy residenziali utilizzano IP assegnati a veri ISP domestici (TIM, Vodafone, Fastweb in Italia; Comcast, AT&T, Verizon negli USA). Per Instagram, il traffico appare provenire da utenti legittimi, non da server cloud.
Quando Considerare Mobile Proxy
I mobile proxy usano IP di reti cellulari (4G/5G). Hanno la massima reputazione perché Instagram non può distinguere il traffico proxy dal traffico dell'app mobile ufficiale. Sono ideali per operazioni ad alto rischio ma hanno costi significativamente maggiori.
Implementazione Python con Proxy Residenziali Rotanti
Vediamo un'implementazione pratica utilizzando la libreria requests con un pool di proxy residenziali ProxyHat.
Configurazione Base
import requests
import time
import random
from urllib.parse import quote
# Configurazione ProxyHat
PROXY_GATEWAY = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "your_username"
PROXY_PASS = "your_password"
def get_residential_proxy(country=None, session_id=None):
"""Genera URL proxy con targeting geografico e sessione sticky."""
username_parts = [PROXY_USER]
if country:
username_parts.append(f"country-{country}")
if session_id:
username_parts.append(f"session-{session_id}")
username = "-".join(username_parts)
proxy_url = f"http://{username}:{PROXY_PASS}@{PROXY_GATEWAY}:{PROXY_PORT}"
return {
"http": proxy_url,
"https": proxy_url
}
# Header realistici
def get_realistic_headers():
"""Genera header HTTP che mimano un browser reale."""
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15"
]
return {
"User-Agent": random.choice(user_agents),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"Accept-Language": "it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Cache-Control": "max-age=0"
}
Scraping Profilo Pubblico
def scrape_instagram_profile(username, country="US"):
"""Scrapea un profilo Instagram pubblico."""
url = f"https://www.instagram.com/{username}/"
# Usa sessione sticky per mantenere lo stesso IP durante la richiesta
session_id = f"ig_{username}_{int(time.time())}"
proxies = get_residential_proxy(country=country, session_id=session_id)
session = requests.Session()
session.headers.update(get_realistic_headers())
try:
# Prima richiesta per ottenere la pagina
response = session.get(url, proxies=proxies, timeout=30)
if response.status_code == 429:
print(f"Rate limited. Aspetto 60 secondi...")
time.sleep(60)
return scrape_instagram_profile(username, country)
if response.status_code != 200:
print(f"Errore: HTTP {response.status_code}")
return None
# Parsing dei dati dal HTML
# Instagram embedda i dati in un script JSON
import re
import json
pattern = r'window\._sharedData\s*=\s*({.+?});\s*</script>'
match = re.search(pattern, response.text)
if match:
data = json.loads(match.group(1))
profile_data = data.get('entry_data', {}).get('ProfilePage', [{}])[0]
user_data = profile_data.get('graphql', {}).get('user', {})
return {
"username": user_data.get('username'),
"full_name": user_data.get('full_name'),
"biography": user_data.get('biography'),
"followers": user_data.get('edge_followed_by', {}).get('count'),
"following": user_data.get('edge_follow', {}).get('count'),
"posts_count": user_data.get('edge_owner_to_timeline_media', {}).get('count'),
"is_private": user_data.get('is_private'),
"is_verified": user_data.get('is_verified'),
"profile_pic_url": user_data.get('profile_pic_url_hd')
}
return None
except requests.exceptions.RequestException as e:
print(f"Errore di connessione: {e}")
return None
# Esempio di utilizzo
if __name__ == "__main__":
profile = scrape_instagram_profile("instagram", country="US")
print(profile)
Rotazione IP e Rate Limiting
class InstagramScraper:
"""Classe per scraping Instagram con rotazione IP e rate limiting."""
def __init__(self, proxy_user, proxy_pass, requests_per_ip=20):
self.proxy_user = proxy_user
self.proxy_pass = proxy_pass
self.requests_per_ip = requests_per_ip
self.request_count = 0
self.current_session = None
def _rotate_proxy(self, country=None):
"""Ruota il proxy residenziale."""
session_id = f"rot_{int(time.time() * 1000)}"
self.current_session = session_id
self.request_count = 0
return get_residential_proxy(country=country, session_id=session_id)
def _should_rotate(self):
"""Determina se è necessario ruotare l'IP."""
return self.request_count >= self.requests_per_ip
def _make_request(self, url, country=None):
"""Esegue richiesta con gestione automatica rotazione."""
if self._should_rotate():
proxies = self._rotate_proxy(country)
else:
proxies = get_residential_proxy(
country=country,
session_id=self.current_session
)
headers = get_realistic_headers()
# Delay umano
time.sleep(random.uniform(2, 5))
try:
response = requests.get(
url,
proxies=proxies,
headers=headers,
timeout=30
)
self.request_count += 1
if response.status_code == 429:
# Rate limited - ruota IP e riprova
print("Rate limited. Rotazione IP...")
self._rotate_proxy(country)
time.sleep(random.uniform(30, 60))
return self._make_request(url, country)
return response
except requests.exceptions.RequestException as e:
print(f"Errore: {e}")
self._rotate_proxy(country)
return None
# Utilizzo
scraper = InstagramScraper(
proxy_user="your_username",
proxy_pass="your_password",
requests_per_ip=15 # Conservativo per Instagram
)
Esempio Node.js con Axios
const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');
// Configurazione ProxyHat
const PROXY_CONFIG = {
host: 'gate.proxyhat.com',
port: 8080,
username: 'your_username',
password: 'your_password'
};
function getProxyAgent(country = 'US', sessionId = null) {
let username = `${PROXY_CONFIG.username}-country-${country}`;
if (sessionId) {
username += `-session-${sessionId}`;
}
const proxyUrl = `http://${username}:${PROXY_CONFIG.password}@${PROXY_CONFIG.host}:${PROXY_CONFIG.port}`;
return new HttpsProxyAgent(proxyUrl);
}
const USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
];
async function scrapeProfile(username) {
const url = `https://www.instagram.com/${username}/`;
const sessionId = `ig_${Date.now()}`;
const config = {
method: 'get',
url: url,
httpsAgent: getProxyAgent('US', sessionId),
headers: {
'User-Agent': USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)],
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'it-IT,it;q=0.9,en;q=0.8',
},
timeout: 30000
};
try {
const response = await axios(config);
// Parsing logic here
console.log(`Status: ${response.status}`);
return response.data;
} catch (error) {
if (error.response?.status === 429) {
console.log('Rate limited. Waiting...');
await new Promise(r => setTimeout(r, 60000));
return scrapeProfile(username);
}
throw error;
}
}
module.exports = { scrapeProfile };
Instagram-Specific Technical Quirks
L'Endpoint ?__a=1 (Deprecato)
Storicamente, aggiungere ?__a=1 a qualsiasi URL Instagram restituiva JSON. Questo endpoint è stato progressivamente limitato:
- Ora richiede header specifici (
x-ig-app-id) - Spesso restituisce 403 per IP non autorizzati
- I dati disponibili sono ridotti rispetto al passato
GraphQL Queries
Instagram usa GraphQL internamente. Reverse-engineering di queste query è possibile ma complesso:
# Esempio di header richiesti per GraphQL
headers = {
"x-ig-app-id": "936619743392459", # Web app ID
"x-csrftoken": "", # Ottenuto dalla prima richiesta
"x-requested-with": "XMLHttpRequest",
}
Attenzione: L'uso di endpoint GraphQL non documentati viola i Termini di Servizio e aumenta il rischio di blocco.
HTTPS/TLS Fingerprinting
Instagram può rilevare client HTTP tramite TLS fingerprinting. Soluzioni:
- tls-client (Python): libreria che mimica il fingerprint TLS di browser reali
- curl-impersonate: curl modificato con fingerprint browser
- Playwright/Puppeteer: browser headless con fingerprint autentico
Mobile API Reverse Engineering
L'app mobile Instagram usa API private più ricche. Tuttavia:
- Richiedono token di autenticazione ottenibili solo tramite login
- Le chiavi API cambiano frequentemente
- Automatizzare il login viola i ToS e rischia ban permanenti
Non raccomandiamo l'uso di API private o reverse engineering. Limitati ai dati pubblicamente accessibili senza autenticazione.
Best Practice per Scraping Etico
Rispetta robots.txt
Instagram's robots.txt (https://www.instagram.com/robots.txt) delinea cosa è permesso. Al momento, è molto restrittivo:
User-agent: *
Disallow: /
Questo indica che formalmente nulla dovrebbe essere scrapato. Considera se i tuoi use case giustificano l'accesso e consulta un legale.
Auto-imposta Rate Limit
Anche se tecnicamente potresti fare più richieste, auto-limitati:
- 1 richiesta ogni 3-5 secondi per IP
- Max 100-200 richieste per ora per IP
- Pause più lunghe durante ore di punta
Mai Automatizzare il Login
Il login automatizzato:
- Viola esplicitamente i Termini di Servizio
- Rischia ban permanente dell'account
- Può costituire violazione del CFAA (US) o leggi equivalenti
- Esponi credenziali in codice/log
Considera API Ufficiali
Meta offre API ufficiali per alcuni use case:
- Instagram Graph API: per business account, insight, media management
- Instagram Basic Display API: per visualizzare contenuti utente (richiede OAuth)
- Instagram Messaging API: per messaggistica business
Se il tuo use case è coperto da API ufficiali, usale sempre preferibilmente allo scraping.
Gestione Errori e Resilienza
Costruisci pipeline resilienti:
- Implementa retry con exponential backoff
- Logga tutti gli errori per analisi
- Monitora success rate per IP/proxy
- Prevedi circuit breaker per evitare waste di risorse
Punti Chiave
- Instagram è ostile allo scraping: rate limit aggressivi, fingerprinting, e datacenter blocking rendono l'accesso difficile
- I proxy residenziali sono essenziali: gli IP datacenter vengono bloccati sistematicamente
- Limitati ai dati pubblici: mai automatizzare login o accedere contenuti privati
- Usa header realistici: User-Agent, Accept, e Sec-Fetch header coerenti con browser reali
- Rotazione IP intelligente: cambia IP ogni 15-20 richieste con delay umani
- Considera API ufficiali: se disponibili per il tuo use case, sono sempre preferibili
- Rispetta limiti etici e legali: GDPR, CFAA, e ToS devono guidare le tue decisioni
Conclusione
Lo scraping di dati pubblici Instagram rimane tecnicamente possibile ma richiede approccio metodico. I proxy residenziali come quelli offerti da ProxyHat sono fondamentali per evitare blocchi immediati. La chiave è costruire pipeline che mimano comportamento umano: rate limiting conservativo, rotazione IP intelligente, e header realistici.
Per progetti di social-listening o brand monitoring, valuta sempre prima se le API ufficiali di Meta possono soddisfare le tue esigenze. Quando lo scraping è necessario, mantieni standard etici rigorosi: rispetta i dati, gli utenti, e le piattaforme che li ospitano.
Per iniziare con proxy residenziali ottimizzati per social media, visita ProxyHat Pricing o esplora le nostre locazioni disponibili in oltre 190 paesi.






