
Webscraping in python: rotazione di proxy e fake user agent
La pratica del webscraping, ovvero il reperimento di informazioni da portali e siti web utilizzando procedure automatizzate, ha acquisito sempre maggiore rilevanza: la disponibilità di dati organizzati e facilmente reperibili riguardo a un target qualunque costituisce infatti il punto di forza di qualsiasi strategia aziendale e di vendita, ma anche semplicemente l’oggetto di studio di qualsivoglia analisi statistica, magari intorno a uno studio specifico (anche accademico).
Ma il webscraping può essere molto utile anche per testare una web app o a fini di testing dell’interfaccia grafica o per verificare la congruenza dei dati (magari dopo una migrazione da un database a un altro).
Sezioni
Problematiche note del webscraping
Per forza di cose, tuttavia, non sempre il webscraping è ben visto dagli amministratori di rete e dai webmaster, che possono, giustamente adottare delle misure preventive: se, infatti, una procedura automatica risulta essere troppo aggressiva, rischia di compromettere le prestazioni della webapp target della raccolta di informazioni; inoltre, spesso i contenuti pubblicati sono coperti da copyright e in qualche modo si cerca di evitare il plagio. Uno degli esiti più comuni di operazioni di webscraping compiute in rapida successione è quello di vedere il proprio ip bannato oppure di finire redirezionati verso qualche link honeypot.
In questo articolo spiegherò come servirsi di due strategie efficaci per non finire in queste trappole, e cioè la rotazione di server proxy e l’utilizzo di fake user agent. Presenterò una semplice classe python che ottiene un indirizzo random da una lista di server proxy gratuiti e fa altrettanto nell’utilizzo di uno user agent fittizio.
Ricordo che la pratica del webscraping può facilmente oltrepassare il limite della legalità e che il presente articolo viene pubblicato solo come oggetto di studio, per cui l’autore non si assume alcuna responsabilità dell’uso che si farà del codice pubblicato
La classe RequestFactory
Per la presentazione di questo caso di studio ho scelto python per le caratteristiche di essenzialità e sinteticità dei costrutti di questo elegante linguaggio di programmazione, oltre che per la disponibilità di librerie altrettanto semplici ed efficaci.
Verranno utilizzate infatti:
- requests, che permette di creare rapidamente richieste http
- fake_useragent, con la quale otterremo una lista di user agent fittizi per simulare il browser utente
- lxml, con cui effettueremo il parsing di una pagina contenente una lista aggiornata di proxy gratuiti
Verrà presentata una classe che, ottenuto un ip e porta random dalla lista dei server, se ne servirà per creare un’istanza di requests (a cui si passerà come argomento anche il fake user agent) da utilizzare per eseguire chiamate di scraping “camuffate”.
Il codice della classe di esempio è reperibile presso questo repository github.
La lista dei proxy server
Come anticipato, utilizzeremo una lista di proxy server, reperendola da questo sito con il metodo get_proxies:
def get_proxies(self):
url = 'https://free-proxy-list.net/'
response = requests.get(url)
parser = fromstring(response.text)
proxies = []
parser = fromstring(response.text)
for td in parser.xpath('//tbody/tr')[:5]:
ip_address = td.xpath('.//td[1]/text()')[0]
port = td.xpath('.//td[2]/text()')[0]
proxy = ":".join([ip_address, port])
print(proxy)
proxies.append(proxy)
return proxies
Il metodo esegue una prima richiesta get al sito web citato, che in home page contiene una table html con gli ultimi ip pubblicati. Ottenuta la response in formato raw text, la si passa alla funzione fromstring della libreria lxml per poi ciclare tutte le righe della tabella. Le prime due celle di ogni riga (i tag td) contengono rispettivamente l’ip e la porta del proxy, che vengono concatenati nel formato “<ip>:<porta>” e aggiunti a una lista di proxy. La lista viene limitata a 5 elementi e alla fine del ciclo for viene restituita al chiamante.
Il proxy casuale
L’istanza di requests che dobbiamo ottenere, accetta l’assegnazione del proxy tramite la proprietà pubblica proxies, che non è nient’altro che un dizionario. Quindi, utilizziamo il metodo randint del namespace random per ottenere un indice casuale compreso tra zero e la lunghezza della lista meno 1:
def get_requests_proxy_params(self):
proxies = self.get_proxies()
index = randint(0, len(proxies) -1)
proxy = proxies[index]
proxy_params={"http": proxy}
return proxy_params
Restituendo un dizionario che come chiave ha il protocollo (per semplicità ci limitiamo ad http) e come valore la stringa random ottenuta contenente server e porta separati da due punti.
Il fake user agent casuale
A riprova della semplicità di python e delle librerie a corredo, per ottenere uno user agent casuale e simulare una request da un browser utente, basta utilizzare un’istanza della classe UserAgent della libreria fake_useragent:
def get_fake_user_agent(self):
ua = UserAgent()
ua.update()
return ua.random
Eseguiamo un update della lista di user agent e ne restituiamo uno a caso con la proprietà random dell’istanza ottenuta.
L’istanza della classe requests
Combinando il tutto nel metodo get_request, ottenuti proxy e user agent casuali, li assegniamo alle proprietà headers e proxies dell’istanza di requests creata dal metodo Session, restituendola al chiamante (notare che, per comodità, la proprietà verify viene impostata a false per non eseguire il check del certificato ssl)
def get_request(self):
proxy_params = self.get_requests_proxy_params()
print(proxy_params)
fake_headers = {'User-Agent': self.get_fake_user_agent()}
print(fake_headers)
r = requests.Session()
r.headers = fake_headers
r.verify = False
r.proxies = proxy_params
return r
Test della classe
Per testare la classe, viene aggiunto il main nello stesso file, si crea un’istanza di RequestFactory e si ottiene l’istanza di requests, con cui si prova a chiamare ipify, stampando sul terminale lo status code http della response e il json appena richiesto:
if __name__ == '__main__':
request = RequestFactory().get_request()
response = request.get("https://api.ipify.org?format=json")
print(response.status_code)
print(response.json())
Se tutto va a buon fine, dovremmo ottenere a schermo il codice di stato http 200 e l’indirizzo ip del proxy in formato json e non il nostro, a conferma che la request è “schermata”.
Conclusioni
Una tecnica utile per evitare il ban da un sito o una webapp verso la quale vengono eseguite procedure automatiche di webscraping, è la rotazione degli ip e l’utilizzo di un fake user agent. In questo articolo ho presentato del semplice codice python che ottiene da una lista di proxy server gratuiti un indirizzo random e simula il browser utente per camuffare una richiesta http. Il codice è stato pubblicato a fini di studio, invitando il lettore a farne un uso responsabile.