Comment accélérer le Scraping web : guide complet

Apprenez à optimiser votre processus de Scraping web grâce à des techniques avancées pour une récupération plus rapide des données.
28 min de lecture
How to Make Web Scraping Faster

Dans cet article, vous découvrirez :

  • Les principales causes d’un processus de Scraping web lent
  • Diverses techniques pour accélérer le Scraping web
  • Comment optimiser un exemple de script de scraping Python pour une récupération plus rapide des données

C’est parti !

Les raisons pour lesquelles votre processus de scraping est lent

Découvrez les principales raisons pour lesquelles votre processus de Scraping web peut être lent.

Raison n° 1 : réponses lentes du serveur

L’un des facteurs les plus importants qui affectent la vitesse de votre Scraping web est le temps de réponse du serveur. Lorsque vous envoyez une requête à un site web, le serveur traite votre requête et y répond. Si le serveur est lent, vos requêtes prendront plus de temps à être traitées. Les raisons pour lesquelles un serveur peut être lent sont un trafic important, des ressources limitées ou des ralentissements du réseau.

Malheureusement, vous ne pouvez pas faire grand-chose pour accélérer un serveur cible. Cela échappe à votre contrôle, à moins que le ralentissement ne soit dû à un nombre trop important de requêtes de votre part. Si tel est le cas, étalez vos requêtes sur une plus longue durée en ajoutant des délais aléatoires entre elles.

Raison n° 2 : lenteur du traitement du processeur

La vitesse de traitement du processeur joue un rôle crucial dans la rapidité d’exécution de vos scripts de scraping. Lorsque vous exécutez vos scripts de manière séquentielle, votre processeur doit traiter chaque opération une par une, ce qui peut prendre beaucoup de temps. Cela est particulièrement visible lorsque vos scripts impliquent des calculs complexes ou des transformations de données.

De plus, l’analyse HTML prend un certain temps et peut ralentir considérablement votre processus de scraping. Pour en savoir plus, consultez notre article sur le scraping web.

Raison n° 3 : opérations d’E/S limitées

Les opérations d’entrée/sortie (E/S) peuvent facilement devenir le goulot d’étranglement de votre opération de scraping. Cela est particulièrement vrai lorsque votre site cible comprend plusieurs pages. Si votre script est conçu pour attendre les réponses de ressources externes avant de continuer, cela peut entraîner des retards considérables.

Envoyer une requête, attendre la réponse du serveur, la traiter, puis passer à la requête suivante n’est pas une manière efficace d’effectuer le Scraping web.

Autres raisons

Voici d’autres raisons qui ralentissent votre script de Scraping web :

  • Code inefficace: une logique de scraping mal optimisée peut ralentir l’ensemble du processus de scraping. Évitez les structures de données inefficaces, les boucles inutiles ou la journalisation excessive.
  • Limitation du débit: si le site cible limite le nombre de requêtes qu’un utilisateur peut effectuer dans un délai donné, votre Scraper automatisé sera ralenti. La solution ? Les services Proxy !
  • CAPTCHAs et autres solutions anti-scraping: les CAPTCHAs et les mesures anti-bot peuvent interrompre votre processus de scraping en exigeant une interaction de l’utilisateur. Découvrez d’autres techniques anti-scraping.

Techniques pour accélérer le Scraping web

Dans cette section, vous découvrirez les méthodes les plus populaires pour accélérer le Scraping web. Nous commencerons par un script de scraping Python de base et démontrerons l’impact de diverses optimisations sur celui-ci.

Remarque: les techniques explorées ici fonctionnent avec n’importe quel langage de programmation ou technologie. Python est utilisé uniquement pour des raisons de simplicité et parce qu’il s’agit de l’un des meilleurs langages de programmation pour le Scraping web.

Voici le script Python initial de scraping :

import requests
from bs4 import BeautifulSoup
import csv
import time

def scrape_quotes_to_scrape():
    # tableau contenant les URL de la page à scraper
    urls = [
        "http://quotes.toscrape.com/",
        "https://quotes.toscrape.com/page/2/",
        "https://quotes.toscrape.com/page/3/",
        "https://quotes.toscrape.com/page/4/",
        "https://quotes.toscrape.com/page/5/",
        "https://quotes.toscrape.com/page/6/",
        "https://quotes.toscrape.com/page/7/",
        « https://quotes.toscrape.com/page/8/ »,
        « https://quotes.toscrape.com/page/9/ »,
        « https://quotes.toscrape.com/page/10/ »
    ]

    # où stocker les données récupérées
    quotes = []

    # extraire les pages séquentiellement
    for url in urls:
        print(f"Extraction de la page : '{url}'")

        # envoyer une requête GET pour obtenir le code HTML de la page
        response = requests.get(url)
        # analyser le code HTML de la page à l'aide de BeautifulSoup
        soup = BeautifulSoup(response.content, "html.parser")

        # sélectionner tous les éléments de citation sur la page
        quote_html_elements = soup.select(".quote")

        # parcourir les éléments de citation et extraire leur contenu
        for quote_html_element in quote_html_elements:
            # extraire le texte de la citation
            text = quote_html_element.select_one(".text").get_text()
            # extraire l'auteur de la citation
            author = quote_html_element.select_one(".author").get_text()
            # extraire les balises associées à la citation
            tags = [tag.get_text() for tag in quote_html_element.select(".tag")]

            # remplir un nouvel objet citation et l'ajouter à la liste
            quote = {
                "text": text,
                "author": author,
                "tags": ", ".join(tags)
            }
            quotes.append(quote)

        print(f"Page '{url}' scraped successfullyn")

    print("Exporting scraped data to CSV")

    # exporter les citations récupérées vers un fichier CSV
    with open("quotes.csv", "w", newline="", encoding="utf-8") as csvfile:
        fieldnames = ["text", "author", "tags"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

        writer.writeheader()
        writer.writerows(quotes)

    print("Citations exportées vers CSVn")

# mesurer le temps d'exécution
start_time = time.time()
scrape_quotes_to_scrape()
end_time = time.time()

execution_time = end_time - start_time
print(f"Temps d'exécution : {execution_time:.2f} secondes")

Le Scraper ci-dessus cible 10 URL paginées du site web Quotes to Scrape. Pour chaque URL, le script effectue les opérations suivantes :

  1. Envoie une requête GET à l’aide de requests pour récupérer le code HTML de la page.
  2. Analyse le contenu HTML avec BeautifulSoup.
  3. Il extrait le texte de la citation, l’auteur et les balises pour chaque élément de citation sur la page.
  4. Stocke les données extraites dans une liste de dictionnaires.

Enfin, il exporte les données récupérées dans un fichier CSV nommé quotes.csv.

Pour exécuter le script, installez les bibliothèques requises à l’aide de :

pip install requests beautifulsoup4

L’appel de la fonction scrape_quotes_to_scrape() est encapsulé dans des appels time.time() afin de mesurer la durée du processus de collecte. Sur notre machine, le script initial prend environ 4,51 secondes pour s’exécuter.

L’exécution du script génère un fichier quotes.csv dans le dossier de votre projet. De plus, vous verrez des journaux similaires à ceux-ci :

Récupération de la page : « http://quotes.toscrape.com/ »
Page « http://quotes.toscrape.com/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/2/ »
Page « https://quotes.toscrape.com/page/2/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/3/ »
Page « https://quotes.toscrape.com/page/3/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/4/ »
Page « https://quotes.toscrape.com/page/4/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/5/ »
Page « https://quotes.toscrape.com/page/5/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/6/ »
Page « https://quotes.toscrape.com/page/6/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/7/ »
Page « https://quotes.toscrape.com/page/7/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/8/ »
Page « https://quotes.toscrape.com/page/8/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/9/ »
Page « https://quotes.toscrape.com/page/9/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/10/ »
Page « https://quotes.toscrape.com/page/10/ » récupérée avec succès

Exportation des données récupérées vers CSV
Citations exportées vers CSV

Temps d'exécution : 4,63 secondes

Ce résultat montre clairement que le script extrait séquentiellement chaque page web paginée de Quotes to Scrape. Comme vous allez le voir, certaines optimisations vont considérablement modifier le déroulement et la vitesse de ce processus.

Voyons maintenant comment accélérer le Scraping web !

1. Utilisez une bibliothèque d’analyse HTML plus rapide

L’analyse des données consomme du temps et des ressources, et différents analyseurs HTML utilisent diverses approches pour accomplir cette tâche. Certains se concentrent sur la fourniture d’un ensemble de fonctionnalités riches avec une API auto-descriptive, tandis que d’autres privilégient les performances. Pour plus de détails, consultez notre guide sur les meilleurs analyseurs HTML.

En Python, Beautiful Soup est l’analyseur HTML le plus populaire, mais il n’est pas nécessairement le plus rapide. Consultez quelques benchmarks pour plus de contexte.

En réalité, Beautiful Soup agit simplement comme une enveloppe autour de différents analyseurs sous-jacents. Vous pouvez spécifier l’analyseur que vous souhaitez utiliser lors de son initialisation, via le deuxième argument :

soup = BeautifulSoup(response.content, "html.parser")

En général, Beautiful Soup est utilisé en combinaison avec html.parser, l’analyseur intégré de la bibliothèque standard de Python. Cependant, si vous recherchez la vitesse, vous devriez envisager lxml. Il s’agit de l’un des analyseurs HTML les plus rapides disponibles en Python, car il est basé sur une implémentation en C.

Pour installer lxml, exécutez la commande suivante :

pip install lxml

Une fois installé, vous pouvez l’utiliser avec Beautiful Soup comme suit :

soup = BeautifulSoup(response.content, "lxml")

Maintenant, exécutez à nouveau votre script de scraping Python. Cette fois, vous devriez voir le résultat suivant :

# omis pour plus de concision...

Temps d'exécution : 4,35 secondes

Le temps d’exécution est passé de 4,61 secondes à 4,35 secondes. Bien que ce changement puisse sembler minime, l’impact de cette optimisation dépend fortement de la taille et de la complexité des pages HTML analysées et du nombre d’éléments sélectionnés.

Dans cet exemple, le site cible comporte des pages avec une structure DOM simple, courte et peu profonde. Néanmoins, obtenir une amélioration de la vitesse d’environ 6 % avec seulement une petite modification du code est un gain appréciable !

👍 Avantages:

  • Facile à mettre en œuvre dans Beautiful Soup

👎 Inconvénients:

  • Avantage limité
  • Ne fonctionne que sur les pages avec des structures DOM complexes
  • Les analyseurs HTML plus rapides peuvent avoir une API plus complexe

2. Mettre en œuvre le scraping multiprocessus

Le multiprocessing est une approche d’exécution parallèle dans laquelle un programme génère plusieurs processus. Chacun de ces processus fonctionne en parallèle et indépendamment sur un cœur de processeur pour effectuer des tâches simultanément plutôt que séquentiellement.

Cette méthode est particulièrement avantageuse pour les opérations liées aux E/S, telles que le Scraping web. En effet, le principal goulot d’étranglement est souvent le temps passé à attendre les réponses des serveurs web. En utilisant plusieurs processus, vous pouvez envoyer des requêtes à plusieurs pages en même temps, ce qui réduit le temps global de scraping.

Pour adapter votre script de scraping au multiprocessing, vous devez apporter quelques modifications importantes à la logique d’exécution. Suivez les étapes ci-dessous pour transformer votre Scraper Python d’une approche séquentielle à une approche multiprocessing.

Pour commencer à utiliser le multitraitement en Python, la première étape consiste à importer Pool et cpu_count à partir du module multiprocessing:

from multiprocessing import Pool, cpu_count

Pool fournit ce dont vous avez besoin pour gérer un pool de processus de travail. Quant à cpu_count, il vous aide à déterminer le nombre de cœurs de processeur disponibles pour le traitement parallèle.

Ensuite, isolez la logique permettant de scraper une seule URL dans une fonction :

def scrape_page(url):
    print(f"Scraping page: '{url}'")

    response = requests.get(url)
    soup = BeautifulSoup(response.content, "html.parser")
    quote_html_elements = soup.select(".quote")

    quotes = []
    for quote_html_element in quote_html_elements:
        text = quote_html_element.select_one(".text").get_text()
        author = quote_html_element.select_one(".author").get_text()
        tags = [tag.get_text() for tag in quote_html_element.select(".tag")]
        quotes.append({
            "text": text,
            "author": author,
            "tags": ", ".join(tags)
        })

    print(f"Page '{url}' scraped successfullyn")

    return quotes

La fonction ci-dessus sera appelée par chaque processus de travail et exécutée sur un cœur de processeur à la fois.

Remplacez ensuite le flux de scraping séquentiel par une logique de multiprocessing :

def scrape_quotes():
    urls = [
        "http://quotes.toscrape.com/",
        "https://quotes.toscrape.com/page/2/",
        "https://quotes.toscrape.com/page/3/",
        « https://quotes.toscrape.com/page/4/ »,
        « https://quotes.toscrape.com/page/5/ »,
        « https://quotes.toscrape.com/page/6/ »,
        « https://quotes.toscrape.com/page/7/ »,
        « https://quotes.toscrape.com/page/8/ »,
        « https://quotes.toscrape.com/page/9/ »,
        « https://quotes.toscrape.com/page/10/ »
    ]

    # créer un pool de processus
    with Pool(processes=cpu_count()) as pool:
        results = pool.map(scrape_page, urls)

    # aplatir la liste des résultats
    quotes = [quote for sublist in results for quote in sublist]

    print("Exportation des données extraites vers CSV")

    with open("quotes_multiprocessing.csv", "w", newline="", encoding="utf-8") as csvfile:
        fieldnames = ["text", "author", "tags"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(quotes)

    print("Citations exportées vers CSVn")

Enfin, exécutez la fonction scrape_quotes() tout en mesurant le temps d’exécution :

if __name__ == "__main__":
    start_time = time.time()
    scrape_quotes()
    end_time = time.time()

    execution_time = end_time - start_time
    print(f"Execution time: {execution_time:.2f} seconds")

Notez que la construction if __name__ == "__main__": est nécessaire pour empêcher certaines parties de votre code d’être exécutées lorsque le module est importé. Sans cette vérification, le module multiprocessing peut tenter de générer de nouveaux processus qui peuvent entraîner un comportement inattendu, en particulier sous Windows.

Assemblez le tout et vous obtiendrez :

from multiprocessing import Pool, cpu_count
import requests
from bs4 import BeautifulSoup
import csv
import time

def scrape_page(url):
    print(f"Scraping page: '{url}'")

    response = requests.get(url)
    soup = BeautifulSoup(response.content, "html.parser")
    quote_html_elements = soup.select(".quote")

    quotes = []
    for quote_html_element in quote_html_elements:
        text = quote_html_element.select_one(".text").get_text()
        author = quote_html_element.select_one(".author").get_text()
        tags = [tag.get_text() for tag in quote_html_element.select(".tag")]
        quotes.append({
            "text": text,
            "author": author,
            "tags": ", ".join(tags)
        })

    print(f"Page '{url}' scraped successfullyn")

    return quotes

def scrape_quotes():
    urls = [
        "http://quotes.toscrape.com/",
        "https://quotes.toscrape.com/page/2/",
        "https://quotes.toscrape.com/page/3/",
        « https://quotes.toscrape.com/page/4/ »,
        « https://quotes.toscrape.com/page/5/ »,
        « https://quotes.toscrape.com/page/6/ »,
        « https://quotes.toscrape.com/page/7/ »,
        « https://quotes.toscrape.com/page/8/ »,
        « https://quotes.toscrape.com/page/9/ »,
        « https://quotes.toscrape.com/page/10/ »
    ]

    # créer un pool de processus
    with Pool(processes=cpu_count()) as pool:
        results = pool.map(scrape_page, urls)

    # aplatir la liste des résultats
    quotes = [quote for sublist in results for quote in sublist]

    print("Exportation des données extraites vers CSV")

    with open("quotes_multiprocessing.csv", "w", newline="", encoding="utf-8") as csvfile:
        fieldnames = ["text", "author", "tags"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(quotes)

    print("Citations exportées vers CSVn")

if __name__ == "__main__":
    start_time = time.time()
    scrape_quotes()
    end_time = time.time()

    execution_time = end_time - start_time
    print(f"Execution time: {execution_time:.2f} seconds")

Exécutez à nouveau le script. Cette fois-ci, il produira les journaux suivants :

Récupération de la page : « http://quotes.toscrape.com/ »
Récupération de la page : « https://quotes.toscrape.com/page/2/ »
Récupération de la page : « https://quotes.toscrape.com/page/3/ »
Récupération de la page : « https://quotes.toscrape.com/page/4/ »
Page récupérée : « https://quotes.toscrape.com/page/5/ »
Page récupérée : « https://quotes.toscrape.com/page/6/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/7/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/8/ »
Page « http://quotes.toscrape.com/ » scrapée avec succès

Page en cours de scraping : « https://quotes.toscrape.com/page/9/ »
Page « https://quotes.toscrape.com/page/3/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/10/ »
Page « https://quotes.toscrape.com/page/4/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/5/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/6/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/7/ » récupérée avec succès.

Page « https://quotes.toscrape.com/page/2/ » récupérée avec succès.

Page « https://quotes.toscrape.com/page/8/ » récupérée avec succès.

Page « https://quotes.toscrape.com/page/9/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/10/ » récupérée avec succès

Exportation des données récupérées au format CSV
Citations exportées au format CSV

Durée d'exécution : 1,87 seconde

Comme vous pouvez le constater, l’ordre d’exécution n’est plus séquentiel. Votre script peut désormais extraire plusieurs pages simultanément. Plus précisément, il peut extraire jusqu’à un nombre de pages égal au nombre de cœurs disponibles sur votre processeur (8, dans notre cas).

Le traitement parallèle permet un gain de temps d’environ 145 %, réduisant le temps d’exécution de 4,61 secondes à 1,87 seconde. C’est impressionnant !

👍 Avantages:

  • Amélioration considérable du temps d’exécution
  • Prise en charge native par la plupart des langages de programmation

👎 Inconvénients:

  • Limité par le nombre de cœurs disponibles sur votre machine
  • Ne respecte pas l’ordre des URL dans la liste
  • Nécessite de nombreuses modifications du code

3. Mettre en œuvre le scraping multithreading

Le multithreading est une technique de programmation permettant d’exécuter plusieurs threads simultanément au sein d’un même processus. Cela permet à votre script d’effectuer plusieurs tâches simultanément, chaque tâche étant gérée par un thread dédié.

Bien que similaire au multitraitement, le multithreading ne nécessite pas nécessairement plusieurs cœurs de processeur. En effet, un seul cœur de processeur peut exécuter simultanément de nombreux threads, partageant le même espace mémoire. Approfondissez ce concept dans notre guide sur la concurrence et le parallélisme.

Gardez à l’esprit que la transformation d’un script de scraping d’une approche séquentielle à une approche multithread nécessite des modifications similaires à celles décrites dans le chapitre précédent.

Dans cette implémentation, nous utiliserons ThreadPoolExecutor du module Python concurrent.futures. Vous pouvez l’importer comme suit :

from concurrent.futures import ThreadPoolExecutor

ThreadPoolExecutor fournit une interface de haut niveau pour gérer un pool de threads, en les exécutant simultanément pour vous.

Comme précédemment, commencez par isoler la logique de scraping d’une seule URL dans une fonction, comme nous l’avons fait dans le chapitre précédent. La différence principale est que vous devez désormais utiliser ThreadPoolExecutor pour exécuter la fonction dans plusieurs threads :

quotes = []

# créer un pool de threads avec jusqu'à 10 workers
with ThreadPoolExecutor(max_workers=10) as executor:
    # utiliser map pour appliquer la fonction scrape_page à chaque URL
    results = executor.map(scrape_page, urls)

# combiner les résultats de tous les threads
for result in results:
    quotes.extend(result)

Par défaut, si max_workers est None ou non spécifié, la valeur par défaut sera le nombre de processeurs de votre machine, multiplié par 5. Dans ce cas, nous n’avons que 10 pages, donc le régler à 10 conviendra parfaitement. N’oubliez pas que l’ouverture d’un trop grand nombre de threads peut ralentir votre système et entraîner des baisses de performances au lieu d’améliorations.

Le script de scraping complet contiendra le code suivant :

from concurrent.futures import ThreadPoolExecutor
import requests
from bs4 import BeautifulSoup
import csv
import time

def scrape_page(url):
    print(f"Scraping page: '{url}'")

    response = requests.get(url)
    soup = BeautifulSoup(response.content, "html.parser")
    quote_html_elements = soup.select(".quote")

    quotes = []
    for quote_html_element in quote_html_elements:
        text = quote_html_element.select_one(".text").get_text()
        author = quote_html_element.select_one(".author").get_text()
        tags = [tag.get_text() for tag in quote_html_element.select(".tag")]
        quotes.append({
            "text": text,
            "author": author,
            "tags": ", ".join(tags)
        })

    print(f"Page '{url}' scraped successfullyn")

    return quotes

def scrape_quotes():
    urls = [
        "http://quotes.toscrape.com/",
        "https://quotes.toscrape.com/page/2/",
        "https://quotes.toscrape.com/page/3/",
        « https://quotes.toscrape.com/page/4/ »,
        « https://quotes.toscrape.com/page/5/ »,
        « https://quotes.toscrape.com/page/6/ »,
        « https://quotes.toscrape.com/page/7/ »,
        « https://quotes.toscrape.com/page/8/ »,
        « https://quotes.toscrape.com/page/9/ »,
        « https://quotes.toscrape.com/page/10/ »
    ]
    
    # où stocker les données récupérées
    quotes = []

    # créer un pool de threads avec jusqu'à 10 workers
    with ThreadPoolExecutor(max_workers=10) as executor:
        # utiliser map pour appliquer la fonction scrape_page à chaque URL
        results = executor.map(scrape_page, urls)

    # combiner les résultats de tous les threads
    for result in results:
        quotes.extend(result)

    print("Exportation des données récupérées vers CSV")

    with open("quotes_multiprocessing.csv", "w", newline="", encoding="utf-8") as csvfile:
        fieldnames = ["text", "author", "tags"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(quotes)

    print("Citations exportées vers CSVn")

if __name__ == "__main__":
    start_time = time.time()
    scrape_quotes()
    end_time = time.time()

    execution_time = end_time - start_time
    print(f"Execution time: {execution_time:.2f} seconds")

Lancez-le, et il enregistrera les messages ci-dessous :

Récupération de la page : 'http://quotes.toscrape.com/'
Récupération de la page : 'https://quotes.toscrape.com/page/2/'
Récupération de la page : 'https://quotes.toscrape.com/page/3/'
Récupération de la page : 'https://quotes.toscrape.com/page/4/'
Page en cours d'extraction : « https://quotes.toscrape.com/page/5/ »
Page en cours d'extraction : « https://quotes.toscrape.com/page/6/ »
Page en cours d'extraction : « https://quotes.toscrape.com/page/7/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/8/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/9/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/10/ »
Page « http://quotes.toscrape.com/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/6/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/7/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/10/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/8/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/5/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/9/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/4/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/3/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/2/ » récupérée avec succès

Exportation des données récupérées vers CSV
Citations exportées vers CSV

Temps d'exécution : 0,52 seconde

Comme pour le scraping multiprocessus, l’ordre d’exécution des pages n’est plus séquentiel. Cette fois-ci, l’amélioration des performances est encore plus importante qu’avec le multiprocessus. En effet, le script peut désormais exécuter 10 requêtes simultanément, dépassant la limite précédente de 8 requêtes (le nombre de cœurs de processeur).

Le gain de temps est considérable, passant de 4,61 secondes à 0,52 seconde, soit une réduction d’environ 885 % !

👍 Avantages:

  • Amélioration considérable du temps d’exécution
  • Prise en charge native par la plupart des technologies

👎 Inconvénients:

  • Il n’est pas facile de trouver le bon nombre de threads
  • Ne respecte pas l’ordre des URL dans la liste
  • Nécessite de nombreuses modifications dans le code

4. Utilisation du scraping asynchrone/Await

La programmation asynchrone est un paradigme de programmation moderne qui vous permet d’écrire du code non bloquant. L’idée est de donner aux développeurs la possibilité de gérer des opérations simultanées sans avoir à gérer explicitement le multithreading ou le multitraitement.

Dans une approche synchrone traditionnelle, chaque opération doit se terminer avant que la suivante ne commence. Cela peut entraîner des inefficacités, en particulier dans les tâches liées aux E/S telles que le Scraping web. Avec la programmation asynchrone, vous pouvez lancer plusieurs opérations d’E/S simultanément, puis attendre qu’elles se terminent. Cela permet à votre script de rester réactif et efficace.

En Python, le scraping asynchrone est généralement implémenté à l’aide du module asyncio de la bibliothèque standard. Ce package fournit l’infrastructure nécessaire pour écrire du code concurrent à thread unique à l’aide de coroutines, via les mots-clés async et await.

Cependant, les bibliothèques HTTP standard telles que requests ne prennent pas en charge les opérations asynchrones. Vous devez donc utiliser un client HTTP asynchrone tel que AIOHTTP, spécialement conçu pour fonctionner de manière transparente avec asyncio. Cette combinaison vous permet d’envoyer plusieurs requêtes HTTP simultanément sans bloquer l’exécution de votre script.

Installez AIOHTTP à l’aide de la commande suivante :

pip install aiohttp

Ensuite, importez asyncio et aiohttp:

import asyncio
import aiohttp

Comme dans les chapitres précédents, encapsulez la logique de scraping d’une seule URL dans une fonction. Cependant, cette fois-ci, la fonction sera asynchrone :

async def scrape_url(session, url):
    async with session.get(url) as response:
        print(f"Scraping page: '{url}'")

        html_content = await response.text()
        soup = BeautifulSoup(html_content, "html.parser")
        # scraping logic...

Notez l’utilisation de la fonction await pour récupérer le code HTML de la page web.

Pour exécuter la fonction en parallèle, créez une session AIOHTTP et rassemblez plusieurs tâches de scraping :

# exécution simultanée des tâches de scraping
async with aiohttp.ClientSession() as session:
    tasks = [scrape_url(session, url) for url in urls]
    results = await asyncio.gather(*tasks)

# aplatissement de la liste des résultats
quotes = [quote for sublist in results for quote in sublist]

Enfin, utilisez asyncio.run() pour exécuter votre fonction principale de scraping asynchrone :

if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(scrape_quotes())
    end_time = time.time()

    execution_time = end_time - start_time
    print(f"Execution time: {execution_time:.2f} seconds")

Votre script de scraping asynchrone en Python contiendra les lignes de code suivantes :

import asyncio
import aiohttp
from bs4 import BeautifulSoup
import csv
import time

async def scrape_url(session, url):
    async with session.get(url) as response:
        print(f"Récupération de la page : '{url}'")

        html_content = await response.text()
        soup = BeautifulSoup(html_content, "html.parser")
        quote_html_elements = soup.select(".quote")

        quotes = []
        for quote_html_element in quote_html_elements:
            text = quote_html_element.select_one(".text").get_text()
            author = quote_html_element.select_one(".author").get_text()
            tags = [tag.get_text() for tag in quote_html_element.select(".tag")]
            quotes.append({
                "text": text,
                "author": author,
                "tags": ", ".join(tags)
            })

        print(f"Page '{url}' scraped successfullyn")

        return quotes

async def scrape_quotes():
    urls = [
        "http://quotes.toscrape.com/",
        "https://quotes.toscrape.com/page/2/",
        "https://quotes.toscrape.com/page/3/",
        "https://quotes.toscrape.com/page/4/",
        "https://quotes.toscrape.com/page/5/",
        "https://quotes.toscrape.com/page/6/",
        "https://quotes.toscrape.com/page/7/",
        « https://quotes.toscrape.com/page/8/ »,
        « https://quotes.toscrape.com/page/9/ »,
        « https://quotes.toscrape.com/page/10/ »
    ]

    # exécution simultanée des tâches de scraping
    async with aiohttp.ClientSession() as session:
        tasks = [scrape_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)

    # aplatissement de la liste des résultats
    quotes = [quote for sublist in results for quote in sublist]

    print("Exportation des données extraites vers CSV")

    with open("quotes_multiprocessing.csv", "w", newline="", encoding="utf-8") as csvfile:
        fieldnames = ["text", "author", "tags"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(quotes)

    print("Citations exportées vers CSVn")

if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(scrape_quotes())
    end_time = time.time()

    execution_time = end_time - start_time
    print(f"Execution time: {execution_time:.2f} seconds")

Lancez-le et vous obtiendrez un résultat similaire à celui-ci :

Récupération de la page : 'http://quotes.toscrape.com/'
Page 'http://quotes.toscrape.com/' récupérée avec succès                                                                

Récupération de la page : 'https://quotes.toscrape.com/page/3/'
Récupération de la page : « https://quotes.toscrape.com/page/7/ »
Récupération de la page : « https://quotes.toscrape.com/page/9/ »
Récupération de la page : « https://quotes.toscrape.com/page/6/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/8/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/10/ »
Page « https://quotes.toscrape.com/page/3/ » scrapée avec succès

Page en cours de scraping : « https://quotes.toscrape.com/page/5/ »
Page en cours de scraping : « https://quotes.toscrape.com/page/4/ »
Page « https://quotes.toscrape.com/page/7/ » scrapée avec succès

Page « https://quotes.toscrape.com/page/9/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/6/ » récupérée avec succès

Récupération de la page : « https://quotes.toscrape.com/page/2/ »
Page « https://quotes.toscrape.com/page/10/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/5/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/4/ » récupérée avec succès

Page « https://quotes.toscrape.com/page/8/ » récupérée avec succès.

Page « https://quotes.toscrape.com/page/2/ » récupérée avec succès.

Exportation des données récupérées au format CSV.
Citations exportées au format CSV.

Temps d'exécution : 0,51 seconde.

Notez que le temps d’exécution est similaire à celui de l’approche multithreading, mais avec l’avantage supplémentaire de ne pas avoir à gérer manuellement les threads.

👍 Avantages:

  • Gain considérable en termes de temps d’exécution
  • La programmation moderne est basée sur une logique asynchrone
  • Ne nécessite pas de gestion manuelle des threads ou des processus

👎 Inconvénients:

  • Pas si facile à maîtriser
  • Ne respecte pas l’ordre des URL dans la liste
  • Nécessite des bibliothèques asynchrones dédiées

5. Autres conseils et approches pour accélérer le scraping

Voici d’autres moyens d’accélérer le Scraping web :

  • Optimisation du taux de requêtes: ajustez les intervalles entre les requêtes afin de trouver le meilleur équilibre entre vitesse et limitation du taux ou interdiction d’accès.
  • Proxys rotatifs: utilisez des proxys rotatifs pour répartir les requêtes sur plusieurs adresses IP, ce qui réduit les risques de blocage et permet un scraping plus rapide. Découvrez les meilleurs proxys rotatifs.
  • Scraping parallèle avec des systèmes distribués: répartissez les tâches de scraping sur plusieurs machines en ligne.
  • Réduction du rendu JavaScript: essayez d’éviter les outils d’automatisation des navigateurs, en préférant des outils tels que les clients HTTP comme analyseurs HTML. N’oubliez pas que les navigateurs consomment beaucoup de ressources et sont beaucoup plus lents que la plupart des analyseurs HTML traditionnels.

Conclusion

Dans ce guide, nous avons vu comment accélérer le Scraping web. Nous avons découvert les principales raisons pour lesquelles un script de scraping peut être lent et examiné diverses techniques pour résoudre ces problèmes à l’aide d’un exemple de script Python. Avec seulement quelques ajustements à la logique de scraping, nous avons obtenu une amélioration de 8 fois du temps d’exécution.

Si l’optimisation manuelle de votre logique de Scraping web est essentielle pour accélérer le processus de récupération des données, l’utilisation des bons outils est tout aussi importante. Lorsque vous ciblez des sites dynamiques qui nécessitent des solutions d’automatisation des navigateurs, les choses peuvent se compliquer, car les navigateurs ont tendance à être lents et gourmands en ressources.

Pour surmonter ces difficultés, essayezle Navigateur de scraping, une solution entièrement hébergée dans le cloud et conçue pour le scraping. Elle s’intègre parfaitement à Puppeteer, Selenium, Playwright et d’autres outils d’automatisation de navigateur populaires. Équipée d’un système de résolution automatique des CAPTCHA et soutenue par un réseau Proxy de plus de 150 millions d’IPs résidentielles, elle offre une évolutivité illimitée pour répondre à tous vos besoins en matière de scraping !

Inscrivez-vous dès maintenant et commencez votre essai gratuit.