Guide du scraping de sites web dynamiques avec Python

Commentaires: 0

Une capacité importante pour obtenir des données à partir de pages web est le web scraping. Pinterest et Instagram, qui chargent dynamiquement du contenu grâce à l'interaction des utilisateurs avec eux, sont des exemples de ces types de sites web. Les méthodes de scraping habituelles sont insuffisantes lorsqu'il s'agit de traiter du matériel basé sur JavaScript. Dans cet article, nous développerons Playwright comme outil d'automatisation tandis que lxml sera utilisé pour l'extraction de données à partir de ces sites dynamiques qui nécessitent Javascript pour fonctionner correctement. À cet égard, nous pourrons discuter de l'utilisation de proxies dans Playwright pour éviter d'être détectés comme des bots. Dans ce tutoriel, nous allons gratter le profil Instagram pour récupérer toutes les URL des posts en simulant le comportement de l'utilisateur, comme le défilement et l'attente du chargement des posts.

Outils que nous utiliserons dans ce guide :

  • Playwright (pour l'automatisation du navigateur);
  • lxml (pour l'extraction de données à l'aide de XPath);
  • Python (notre langage de programmation).

Guide étape par étape pour le scraping des posts Instagram

Nous allons illustrer le processus à l'aide de l'exemple du scraping d'un profil Instagram pour extraire les URL des posts, en simulant des actions de l'utilisateur telles que le défilement de la page et l'attente du chargement de nouvelles données. Les sites web dynamiques chargent leur contenu de manière asynchrone via des requêtes AJAX, ce qui signifie que tout le contenu de la page n'est pas immédiatement accessible.

Étape 1. Installez les bibliothèques requises

Avant de commencer, installez les paquets nécessaires :


pip install playwright
pip install lxml

Vous devrez également installer les navigateurs Playwright :


playwright install

Étape 2. Configuration de Playwright pour le scraping dynamique de sites web

Nous utiliserons Playwright pour automatiser le navigateur, charger le contenu dynamique d'Instagram et faire défiler la page pour charger d'autres posts. Créons un script d'automatisation de base :

Script d'automatisation (Headless Browser) :


import asyncio
from playwright.async_api import async_playwright

async def scrape_instagram():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)  # Mode sans tête Pas de retour visuel
        page = await browser.new_page()
        
        # Visiter l'URL du profil
        await page.goto("https://www.instagram.com/profile name/", wait_until="networkidle")

        # Cliquez sur le bouton pour charger d'autres messages
        await page.get_by_role("button", name="Show more posts from").click()
        
        # Faire défiler la page pour charger un contenu dynamique
        scroll_count = 5  # Personnalisez cette fonction en fonction du nombre de fois que vous souhaitez faire défiler la page.
        for _ in range(scroll_count):
            await page.evaluate('window.scrollBy(0, 700);')
            await page.wait_for_timeout(3000)  # Attendre le chargement des messages
            await page.wait_for_load_state("networkidle")
        
        # Obtenir le contenu de la page
        content = await page.content()
        await browser.close()
        
        return content

# Exécuter la fonction asynchrone
asyncio.run(scrape_instagram())

Etape 3. Analyse de la page avec lxml et XPath

Une fois le contenu chargé, nous pouvons utiliser lxml pour analyser le HTML et extraire les données à l'aide de XPath. Dans ce cas, nous extrayons les URL de tous les messages du profil.

Analyse du contenu de la page et extraction des URL des messages :


from lxml import html
import json

def extract_post_urls(page_content):
    # Analyse du contenu HTML à l'aide de lxml
    tree = html.fromstring(page_content)
    
    # XPath pour l'extraction des URL des messages
    post_urls_xpath = '//a[contains(@href, "/p/")]/@href'
    
    # Extraire des URL
    post_urls = tree.xpath(post_urls_xpath)
    
    # Convertir les URL relatifs en URL absolus
    base_url = "https://www.instagram.com"
    post_urls = [f"{base_url}{url}" for url in post_urls]
    
    return post_urls

Exemple de fonction permettant d'enregistrer les données extraites au format JSON :


def save_data(profile_url, post_urls):
    data = {profile_url: post_urls}
    with open('instagram_posts.json', 'w') as json_file:
        json.dump(data, json_file, indent=4)

# Scraper et extraire des URL
page_content = asyncio.run(scrape_instagram())
post_urls = extract_post_urls(page_content)

# Enregistrer les URL extraites dans un fichier JSON
save_data("https://www.instagram.com/profile name/", post_urls)

Etape 4. Gérer le défilement infini avec Playwright

Pour récupérer des sites web dynamiques, il est souvent nécessaire de simuler un défilement infini. Dans notre script, nous faisons défiler la page à l'aide de JavaScript :


(window.scrollBy(0, 700))

Et attendez que le nouveau contenu se charge à l'aide de cette commande :


 wait_for_load_state("networkidle")

Étape 5. Utilisation de proxies avec Playwright

Instagram applique des limites de débit strictes et des mesures anti-bots. Pour éviter d'être bloqué, vous pouvez utiliser des proxys pour alterner les adresses IP et distribuer les demandes. Playwright facilite l'intégration des proxys dans votre automatisation du scraping.

Mise en œuvre de proxies dans Playwright :


async def scrape_with_proxy():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False, 
            proxy={"server": "http://your-proxy-server:port"}
        )
        page = await browser.new_page()
        await page.goto("https://www.instagram.com/profile name/", wait_until="networkidle")
        # Poursuivre le raclage comme précédemment...

Playwright prend également en charge le proxy à transmettre en tant que nom d'utilisateur, mot de passe et serveur, dont l'exemple est donné ci-dessous


async def scrape_with_proxy():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=False, 
            proxy={"server": "http://your-proxy-server:port", "username": "username", "password": "password"}
        )
        page = await browser.new_page()
        await page.goto("https://www.instagram.com/profile name/", wait_until="networkidle")
        # Poursuivre le raclage comme précédemment...

Les proxys permettent d'éviter les interdictions d'IP, les défis CAPTCHA, et garantissent un scraping fluide des sites web riches en données ou à accès restreint comme Instagram.

Code complet


import asyncio
from playwright.async_api import async_playwright
from lxml import html
import json

# Fonction permettant d'automatiser le navigateur et de récupérer du contenu dynamique à l'aide de proxies
async def scrape_instagram(profile_url, proxy=None):
    async with async_playwright() as p:
        # Configurer le navigateur avec un proxy s'il est fourni
        browser_options = {
            'headless': True,  # Utiliser le navigateur principal pour voir l'action (peut être réglé sur True pour le mode sans tête)
        }
        if proxy:
            browser_options['proxy'] = proxy

        # Lancer le navigateur
        browser = await p.chromium.launch(**browser_options)
        page = await browser.new_page()

        # Visiter la page de profil Instagram
        await page.goto(profile_url, wait_until="networkidle")
        
        # Essayez de cliquer sur le bouton "Afficher plus de messages" (facultatif, peut échouer si le bouton n'est pas trouvé).
        try:
           await page.click('button:has-text("Show more posts from")')
        except Exception as e:
           print(f"No 'Show more posts' button found: {e}")


        # Faire défiler la page pour afficher d'autres articles
        scroll_count = 5  # Number of scrolls to load posts
        for _ in range(scroll_count):
            await page.evaluate('window.scrollBy(0, 500);')
            await page.wait_for_timeout(3000)  # Attendre le chargement des nouveaux messages
            await page.wait_for_load_state("networkidle")

        # Obtenir le contenu complet de la page après défilement
        content = await page.content()
        await browser.close()  # Fermez le navigateur une fois que vous avez terminé
        
        return content

# Fonction permettant d'analyser le contenu de la page scannée et d'extraire les URL des messages.
def extract_post_urls(page_content):
    # Analyse du contenu HTML à l'aide de lxml
    tree = html.fromstring(page_content)
    
    # XPath pour l'extraction des URL des messages
    post_urls_xpath = '//a[contains(@href, "/p/")]/@href'
    
    # Extraire les URL des messages à l'aide de XPath
    post_urls = tree.xpath(post_urls_xpath)
    
    # Convertir les URL relatifs en URL absolus
    base_url = "https://www.instagram.com"
    post_urls = [f"{base_url}{url}" for url in post_urls]
    
    return post_urls

# Fonction permettant d'enregistrer les URL des messages extraits dans un fichier JSON
def save_data(profile_url, post_urls):
    # Structurer les données au format JSON
    data = {profile_url: post_urls}
    
    # Enregistrer les données dans un fichier
    with open('instagram_posts.json', 'w') as json_file:
        json.dump(data, json_file, indent=4)
    print(f"Data saved to instagram_posts.json")

# Fonction principale : faire fonctionner le racleur et enregistrer les données
async def main():
    # Définir l'URL du profil Instagram
    profile_url = "https://www.instagram.com/profile name/"
    
    # Si vous le souhaitez, vous pouvez mettre en place un proxy
    proxy = {"server": "server", "username": "username", "password": "password"}  # Use None if no proxy is required
    
    # Scraper la page Instagram à l'aide de proxies
    page_content = await scrape_instagram(profile_url, proxy=proxy)
    
    # Extraire les URL des messages à partir du contenu de la page scrappée
    post_urls = extract_post_urls(page_content)
    
    # Enregistrer les URL des messages extraits dans un fichier JSON
    save_data(profile_url, post_urls)

if __name__ == '__main__':
   asyncio.run(main())

Outils d'automatisation alternatifs pour le web scraping

Si Playwright est un excellent choix pour le scraping de sites web dynamiques, d'autres outils peuvent convenir à différents scénarios :

  1. Selenium : Selenium est l'un des plus anciens cadres d'automatisation de navigateur et fonctionne de manière similaire à Playwright. Il est très polyvalent, mais ne dispose pas de certaines fonctionnalités modernes offertes par Playwright, telles que la gestion de plusieurs navigateurs avec une API unique.
  2. Puppeteer : Puppeteer est un autre outil populaire pour l'automatisation des navigateurs, en particulier pour le scraping de sites web à forte composante JavaScript. Comme Playwright, il contrôle les navigateurs sans tête et permet d'interagir avec le contenu dynamique.
  3. Requests + BeautifulSoup : pour les sites web plus simples qui ne nécessitent pas de JavaScript pour charger le contenu, la bibliothèque Requests combinée à BeautifulSoup est une alternative légère. Cependant, elle ne gère pas bien le contenu dynamique.

Chaque outil offre des atouts uniques et peut être choisi en fonction des besoins et des conditions spécifiques du projet.

Pour un scraping réussi des sites web dynamiques qui utilisent activement JavaScript et les requêtes AJAX, des outils puissants capables de gérer efficacement le défilement infini et les éléments interactifs complexes sont nécessaires. L'une de ces solutions est Playwright - un outil de Microsoft qui offre une automatisation complète du navigateur, ce qui en fait un choix idéal pour des plateformes telles qu'Instagram. Combiné à la bibliothèque lxml pour l'analyse HTML, Playwright simplifie grandement l'extraction des données, permettant l'automatisation des interactions avec les éléments de la page et le processus d'analyse sans intervention manuelle. En outre, l'utilisation de serveurs proxy aide à contourner la protection anti-bot et empêche le blocage IP, garantissant des opérations de scraping stables et ininterrompues.

Commentaires:

0 Commentaires