Comment alterner les proxies lors du scraping de données web

Commentaires: 0

Même si cette approche de la collecte de données semble bonne, elle est mal vue par de nombreux sites web, et il y a des conséquences à la poursuite du scraping, comme l'interdiction de notre IP.

D'un point de vue positif, les services de proxy permettent d'éviter ces conséquences. Ils nous permettent de prendre une autre IP pendant que nous collectons des données en ligne, et aussi sûr que cela puisse paraître, il est préférable d'utiliser plusieurs proxys. L'utilisation de plusieurs proxys pendant le scraping donne l'impression que l'interaction avec le site web est aléatoire et renforce la sécurité.

Le site web cible (source) de ce guide est une librairie en ligne. Il imite un site de commerce électronique pour les livres. On y trouve des livres avec un nom, un prix et une disponibilité. Comme ce guide ne se concentre pas sur l'organisation des données renvoyées mais sur la rotation des proxies, les données renvoyées ne seront présentées que dans la console.

Préparation de l'environnement de travail et intégration des serveurs mandataires

Installer et importer quelques modules Python dans notre fichier avant de pouvoir commencer à coder les fonctions qui aideraient à la rotation des proxies et au scraping du site web.

pip install requests beautifulSoup4 lxml

3 des 5 modules Python nécessaires à ce script de scraping peuvent être installés à l'aide de la commande ci-dessus. Requests nous permet d'envoyer des requêtes HTTP au site web, beautifulSoup4 nous permet d'extraire les informations de la page HTML fournie par les requêtes, et LXML est un analyseur HTML.

En outre, nous avons également besoin du module de threading intégré pour permettre des tests multiples des proxies pour voir s'ils fonctionnent et json pour lire à partir d'un fichier JSON.

import requests
import threading
from requests.auth import HTTPProxyAuth
import json
from bs4 import BeautifulSoup
import lxml
import time

url_to_scrape = "https://books.toscrape.com"
valid_proxies = []
book_names = []
book_price = []
book_availability = []
next_button_link = ""

Etape 1 : vérification du proxy à partir d'une liste de proxys

Construire un script de scraping qui fait tourner les proxies signifie que nous avons besoin d'une liste de proxies à choisir pendant la rotation. Certains proxys nécessitent une authentification, d'autres non. Nous devons créer une liste de dictionnaires avec les détails du proxy, y compris le nom d'utilisateur et le mot de passe du proxy si l'authentification est nécessaire.

La meilleure approche consiste à placer les informations relatives au proxy dans un fichier JSON distinct, organisé comme celui présenté ci-dessous :

[
  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  },

  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  },
  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  },
  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  }
]

Dans le champ "proxy_address", entrez l'adresse IP et le port, séparés par deux points. Dans les champs "proxy_username" et "proxy_password", indiquez le nom d'utilisateur et le mot de passe pour l'autorisation.

Voici le contenu d'un fichier JSON contenant quatre serveurs mandataires parmi lesquels le script peut choisir. Le nom d'utilisateur et le mot de passe peuvent être vides, ce qui indique que le proxy ne nécessite pas d'authentification.

def verify_proxies(proxy:dict):
    try:
        if proxy['proxy_username'] != "" and  proxy['proxy_password'] != "":
            proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
            res = requests.get(
                url_to_scrape,
                auth = proxy_auth,
                proxies={
                "http" : proxy['proxy_address']
                }
            )
        else:
            res = requests.get(url_to_scrape, proxies={
                "http" : proxy['proxy_address'],
            })
        
        if res.status_code == 200:
            valid_proxies.append(proxy)
            print(f"Proxy Validated: {proxy['proxy_address']}")
            
    except:
        print("Proxy Invalidated, Moving on")

Par précaution, cette fonction s'assure que les proxys fournis sont actifs et fonctionnent. Nous pouvons y parvenir en parcourant en boucle chaque dictionnaire du fichier JSON, en envoyant une requête GET au site web et, si un code de statut 200 est renvoyé, en ajoutant ce proxy à la liste des valid_proxies - une variable que nous avons créée plus tôt pour héberger les proxies qui fonctionnent à partir de la liste figurant dans le fichier. Si l'appel n'aboutit pas, l'exécution se poursuit.

Etape 2 : Envoi d'une requête de web scraping

Puisque beautifulSoup a besoin du code HTML du site web pour extraire les données dont nous avons besoin, nous avons créé request_function(), qui prend l'URL et le proxy de notre choix et renvoie le code HTML sous forme de texte. La variable proxy nous permet d'acheminer la requête à travers différents proxys, d'où la rotation du proxy.

def request_function(url, proxy):
    try:
        if proxy['proxy_username'] != "" and  proxy['proxy_password'] != "":
            proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
            response = requests.get(
                url,
                auth = proxy_auth,
                proxies={
                "http" : proxy['proxy_address']
                }
            )
        else:
            response = requests.get(url, proxies={
                "http" : proxy['proxy_address']
            })
        
        if response.status_code == 200:
            return response.text

    except Exception as err:
        print(f"Switching Proxies, URL access was unsuccessful: {err}")
        return None

Etape 3 : Extraction des données du site web cible

data_extract() extrait les données dont nous avons besoin du code HTML fourni. Il rassemble l'élément HTML contenant les informations relatives au livre, telles que le nom du livre, le prix et la disponibilité. Il extrait également le lien vers la page suivante.

Cette opération est particulièrement délicate car le lien est dynamique, et nous avons donc dû tenir compte de ce dynamisme. Enfin, il parcourt les livres et extrait le nom, le prix et la disponibilité, puis renvoie le lien du bouton suivant que nous utiliserions pour récupérer le code HTML de la page suivante.

def data_extract(response):
    soup = BeautifulSoup(response, "lxml")
    books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3")
    next_button_link = soup.find("li", class_="next").find('a').get('href')
    next_button_link=f"{url_to_scrape}/{next_button_link}" if "catalogue" in next_button_link else f"{url_to_scrape}/catalogue/{next_button_link}"

    for each in books:
        book_names.append(each.find("img").get("alt"))
        book_price.append(each.find("p", class_="price_color").text)
        book_availability.append(each.find("p", class_="instock availability").text.strip())

    return next_button_link

Etape 4 : Lier tout ensemble

Pour relier le tout, il faut :

  1. Charger les détails du proxy à partir du fichier JSON. Démarrer un thread pour chaque proxy en utilisant threading.Thread(). Cela nous aidera à tester plusieurs proxies à la fois. Les proxies valides sont ajoutés à valid_proxies().
  2. Chargez la page d'accueil de la source en utilisant un proxy valide. Si un proxy ne fonctionne pas, nous utilisons le suivant, tout cela pour nous assurer que la page d'accueil se charge ou ne renvoie pas None avant que l'exécution ne se poursuive.
  3. Puis nous parcourons les proxys actifs, utilisons la fonction request_function() pour créer une requête GET. Si nous recevons une requête GET, nous collectons les données du site.
  4. Enfin, nous imprimons les données recueillies dans la console.
with open("proxy-list.json") as json_file:
    proxies = json.load(json_file)
    for each in proxies:
        threading.Thread(target=verify_proxies, args=(each, )).start() 


time.sleep(4)

for i in range(len(valid_proxies)):
    response = request_function(url_to_scrape, valid_proxies[i])
    if response != None:
        next_button_link = data_extract(response)
        break
    else:
        continue

for proxy in valid_proxies:
   print(f"Using Proxy: {proxy['proxy_address']}")
   response = request_function(next_button_link, proxy)
   if response is not None:
       next_button_link = data_extract(response)
   else:
       continue


for each in range(len(book_names)):
    print(f"No {each+1}: Book Name: {book_names[each]} Book Price: {book_price[each]} and Availability {book_availability[each]}")

Code complet

import requests
import threading
from requests.auth import HTTPProxyAuth
import json
from bs4 import BeautifulSoup
import time

url_to_scrape = "https://books.toscrape.com"
valid_proxies = []
book_names = []
book_price = []
book_availability = []
next_button_link = ""


def verify_proxies(proxy: dict):
   try:
       if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
           proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
           res = requests.get(
               url_to_scrape,
               auth=proxy_auth,
               proxies={
                   "http": proxy['proxy_address'],
               }
           )
       else:
           res = requests.get(url_to_scrape, proxies={
               "http": proxy['proxy_address'],
           })

       if res.status_code == 200:
           valid_proxies.append(proxy)
           print(f"Proxy Validated: {proxy['proxy_address']}")

   except:
       print("Proxy Invalidated, Moving on")


# Récupère l'élément HTML d'une page
def request_function(url, proxy):
   try:
       if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
           proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
           response = requests.get(
               url,
               auth=proxy_auth,
               proxies={
                   "http": proxy['proxy_address'],
               }
           )
       else:
           response = requests.get(url, proxies={
               "http": proxy['proxy_address'],
           })

       if response.status_code == 200:
           return response.text

   except Exception as err:
       print(f"Switching Proxies, URL access was unsuccessful: {err}")
       return None


# Grattage
def data_extract(response):
   soup = BeautifulSoup(response, "lxml")
   books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3")
   next_button_link = soup.find("li", class_="next").find('a').get('href')
   next_button_link = f"{url_to_scrape}/{next_button_link}" if "catalogue" in next_button_link else f"{url_to_scrape}/catalogue/{next_button_link}"

   for each in books:
       book_names.append(each.find("img").get("alt"))
       book_price.append(each.find("p", class_="price_color").text)
       book_availability.append(each.find("p", class_="instock availability").text.strip())

   return next_button_link


# Obtenir un proxy à partir de JSON
with open("proxy-list.json") as json_file:
   proxies = json.load(json_file)
   for each in proxies:
       threading.Thread(target=verify_proxies, args=(each,)).start()

time.sleep(4)

for i in range(len(valid_proxies)):
   response = request_function(url_to_scrape, valid_proxies[i])
   if response is not None:
       next_button_link = data_extract(response)
       break
   else:
       continue

for proxy in valid_proxies:
   print(f"Using Proxy: {proxy['proxy_address']}")
   response = request_function(next_button_link, proxy)
   if response is not None:
       next_button_link = data_extract(response)
   else:
       continue

for each in range(len(book_names)):
   print(
       f"No {each + 1}: Book Name: {book_names[each]} Book Price: {book_price[each]} and Availability {book_availability[each]}")

Résultat final

Après une exécution réussie, les résultats ressemblent à ce qui suit. Il extrait ensuite des informations sur plus de 100 livres à l'aide des deux serveurs mandataires fournis.

1.png

2.png

3.png

4.png

L'utilisation de plusieurs proxys pour le web scraping permet d'augmenter le nombre de requêtes vers la ressource cible et de contourner le blocage. Pour maintenir la stabilité du processus de scraping, il est conseillé d'utiliser des adresses IP qui offrent une vitesse élevée et un facteur de confiance important, comme les proxys statiques des FAI et les proxys résidentiels dynamiques. En outre, la fonctionnalité du script fourni peut être facilement étendue pour répondre à diverses exigences en matière de récupération de données.

Commentaires:

0 Commentaires