Création du robot d'exploration Web - AWS Directives prescriptives

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Création du robot d'exploration Web

Comme décrit dans la Architecture section, l'application s'exécute par lots, un par entreprise.

Capture et traitement du fichier robots.txt

Après avoir préparé le jeu de données, vous devez vérifier si le domaine possède un fichier robots.txt. Pour les robots d'exploration Web et autres robots, le fichier robots.txt indique les sections du site Web qu'ils sont autorisés à visiter. Le respect des instructions contenues dans ce fichier est une bonne pratique importante pour explorer des sites Web de manière éthique. Pour plus d'informations, consultez la section Meilleures pratiques pour les robots d'exploration éthiques dans ce guide.

Pour capturer et traiter le fichier robots.txt
  1. Si ce n'est pas déjà fait, installez la requests bibliothèque en exécutant la commande suivante dans un terminal :

    pip install requests
  2. Exécutez le script suivant. Ce script permet d'effectuer les opérations suivantes :

    • Il définit une check_robots_txt fonction qui prend un domaine en entrée.

    • Il construit l'URL complète du fichier robots.txt.

    • Il envoie une requête GET à l'URL du fichier robots.txt.

    • Si la demande aboutit (code d'état 200), un fichier robots.txt existe.

    • Si la demande échoue ou renvoie un code d'état différent, le fichier robots.txt n'existe pas ou n'est pas accessible.

    import requests from urllib.parse import urljoin def check_robots_txt(domain): # Ensure the domain starts with a protocol if not domain.startswith(('http://', 'https://')): domain = 'https://' + domain # Construct the full URL for robots.txt robots_url = urljoin(domain, '/robots.txt') try: # Send a GET request to the robots.txt URL response = requests.get(robots_url, timeout=5) # Check if the request was successful (status code 200) if response.status_code == 200: print(f"robots.txt found at {robots_url}") return True else: print(f"No robots.txt found at {robots_url} (Status code: {response.status_code})") return False except requests.RequestException as e: print(f"Error checking {robots_url}: {e}") return False
    Note

    Ce script gère les exceptions liées aux erreurs réseau ou à d'autres problèmes.

  3. Si un fichier robots.txt existe, utilisez le script suivant pour le télécharger :

    import requests def download(self, url): response = requests.get(url, headers=self.headers, timeout=5) response.raise_for_status() # Raise an exception for non-2xx responses return response.text def download_robots_txt(self): # Append '/robots.txt' to the URL to get the robots.txt file's URL robots_url = self.url.rstrip('/') + '/robots.txt' try: response = download(robots_url) return response except requests.exceptions.RequestException as e: print(f"Error downloading robots.txt: {e}, \nGenerating sitemap using combinations...") return e
    Note

    Ces scripts peuvent être personnalisés ou modifiés en fonction de votre cas d'utilisation. Vous pouvez également combiner ces scripts.

Capture et traitement du plan du site

Ensuite, vous devez traiter le plan du site. Vous pouvez utiliser le plan du site pour concentrer l'exploration sur les pages importantes. Cela améliore l'efficacité du rampage. Pour plus d'informations, consultez la section Meilleures pratiques pour les robots d'exploration éthiques dans ce guide.

Pour capturer et traiter le plan du site
  • Exécutez le script suivant. Ce script définit une check_and_download_sitemap fonction qui :

    • Accepte une URL de base, une URL de plan de site facultative provenant du fichier robots.txt et une chaîne d'agent utilisateur.

    • Vérifie plusieurs emplacements potentiels du plan du site, y compris celui du fichier robots.txt (s'il est fourni).

    • Tente de télécharger le plan du site depuis chaque emplacement.

    • Vérifie que le contenu téléchargé est au format XML.

    • Appelle la parse_sitemap fonction pour extraire le URLs. Cette fonction :

      • Analyse le contenu XML du plan du site.

      • Gère à la fois les plans de site classiques et les fichiers d'index des plans de site.

      • Récupère récursivement les sous-sitemaps si un index de plan de site est détecté.

    import requests from urllib.parse import urljoin import xml.etree.ElementTree as ET def check_and_download_sitemap(base_url, robots_sitemap_url=None, user_agent='SitemapBot/1.0'): headers = {'User-Agent': user_agent} sitemap_locations = [robots_sitemap_url, urljoin(base_url, '/sitemap.xml'), urljoin(base_url, '/sitemap_index.xml'), urljoin(base_url, '/sitemap/'), urljoin(base_url, '/sitemap/sitemap.xml')] for sitemap_url in sitemap_locations: if not sitemap_url: continue print(f"Checking for sitemap at: {sitemap_url}") try: response = requests.get(sitemap_url, headers=headers, timeout=10) if response.status_code == 200: content_type = response.headers.get('Content-Type', '') if 'xml' in content_type: print(f"Successfully downloaded sitemap from {sitemap_url}") return parse_sitemap(response.text) else: print(f"Found content at {sitemap_url}, but it's not XML. Content-Type: {content_type}") except requests.RequestException as e: print(f"Error downloading sitemap from {sitemap_url}: {e}") print("No sitemap found.") return [] def parse_sitemap(sitemap_content): urls = [] try: root = ET.fromstring(sitemap_content) # Handle both sitemap and sitemapindex for loc in root.findall('.//{http://www.sitemaps.org/schemas/sitemap/0.9}loc'): urls.append(loc.text) # If it's a sitemap index, recursively fetch each sitemap if root.tag.endswith('sitemapindex'): all_urls = [] for url in urls: print(f"Fetching sub-sitemap: {url}") sub_sitemap_urls = check_and_download_sitemap(url) all_urls.extend(sub_sitemap_urls) return all_urls except ET.ParseError as e: print(f"Error parsing sitemap XML: {e}") return urls if __name__ == "__main__": base_url = input("Enter the base URL of the website: ") robots_sitemap_url = input("Enter the sitemap URL from robots.txt (or press Enter if none): ").strip() or None urls = check_and_download_sitemap(base_url, robots_sitemap_url) print(f"Found {len(urls)} URLs in sitemap:") for url in urls[:5]: # Print first 5 URLs as an example print(url) if len(urls) > 5: print("...")

Conception du crawler

Ensuite, vous concevez le robot d'exploration Web. Le crawler est conçu pour suivre les meilleures pratiques décrites Bonnes pratiques pour les robots d'exploration éthiques dans ce guide. Ce EthicalCrawler cours présente plusieurs principes clés de l'exploration éthique :

  • Récupération et analyse du fichier robots.txt : le robot d'exploration récupère le fichier robots.txt pour le site Web cible.

  • Respect des autorisations d'exploration : avant d'explorer une URL, le robot d'exploration vérifie si les règles du fichier robots.txt autorisent l'exploration de cette URL. Si une URL n'est pas autorisée, le robot d'exploration l'ignore et passe à l'URL suivante.

  • Respect du délai d'exploration : le robot vérifie la présence d'une directive crawl-delay dans le fichier robots.txt. Si un tel délai est spécifié, le robot utilise ce délai entre les demandes. Dans le cas contraire, il utilise un délai par défaut.

  • Identification de l'agent utilisateur — Le robot utilise une chaîne d'agent utilisateur personnalisée pour s'identifier auprès des sites Web. Si nécessaire, les propriétaires de sites Web peuvent définir des règles spécifiques pour restreindre ou autoriser votre robot d'exploration.

  • Gestion des erreurs et dégradation progressive — Si le fichier robots.txt ne peut pas être extrait ou analysé, le robot d'exploration applique des règles par défaut prudentes. Il gère les erreurs réseau et les réponses HTTP autres que 200.

  • Exploration limitée : pour éviter de surcharger le serveur, le nombre de pages pouvant être explorées est limité.

Le script suivant est un pseudocode qui explique le fonctionnement du robot d'exploration Web :

import requests from urllib.parse import urljoin, urlparse import time class EthicalCrawler: def __init__(self, start_url, user_agent='EthicalBot/1.0'): self.start_url = start_url self.user_agent = user_agent self.domain = urlparse(start_url).netloc self.robots_parser = None self.crawl_delay = 1 # Default delay in seconds def can_fetch(self, url): if self.robots_parser: return self.robots_parser.allowed(url, self.user_agent) return True # If no robots.txt, assume allowed but crawl conservatively def get_crawl_delay(self): if self.robots_parser: delay = self.robots_parser.agent(self.user_agent).delay if delay is not None: self.crawl_delay = delay print(f"Using crawl delay of {self.crawl_delay} seconds") def crawl(self, max_pages=10): self.get_crawl_delay() pages_crawled = 0 urls_to_crawl = [self.start_url] while urls_to_crawl and pages_crawled < max_pages: url = urls_to_crawl.pop(0) if not self.can_fetch(url): print(f"robots.txt disallows crawling: {url}") continue try: response = requests.get(url, headers={'User-Agent': self.user_agent}) if response.status_code == 200: print(f"Successfully crawled: {url}") # Here you would typically parse the content, extract links, etc. # For this example, we'll just increment the counter pages_crawled += 1 else: print(f"Failed to crawl {url}: HTTP {response.status_code}") except Exception as e: print(f"Error crawling {url}: {e}") # Respect the crawl delay time.sleep(self.crawl_delay) print(f"Crawling complete. Crawled {pages_crawled} pages.")
Pour créer un robot d'exploration Web avancé et éthique qui collecte des données ESG
  1. Copiez l'exemple de code suivant pour le robot d'exploration éthique avancé utilisé dans ce système :

    import requests from urllib.parse import urljoin, urlparse import time from collections import deque import random from bs4 import BeautifulSoup import re import csv import os class EnhancedESGCrawler: def __init__(self, start_url): self.start_url = start_url self.domain = urlparse(start_url).netloc self.desktop_user_agent = 'ESGEthicalBot/1.0' self.mobile_user_agent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1' self.robots_parser = None self.crawl_delay = None self.urls_to_crawl = deque() self.crawled_urls = set() self.max_retries = 2 self.session = requests.Session() self.esg_data = [] self.news_links = [] self.pdf_links = [] def setup(self): self.fetch_robots_txt() # Provided in Previous Snippet self.fetch_sitemap() # Provided in Previous Snippet def can_fetch(self, url, user_agent): if self.robots_parser: return self.robots_parser.allowed(url, user_agent) return True def delay(self): if self.crawl_delay is not None: time.sleep(self.crawl_delay) else: time.sleep(random.uniform(1, 3)) def get_headers(self, user_agent): return {'User-Agent': user_agent, 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate, br', 'DNT': '1', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1'} def extract_esg_data(self, url, html_content): soup = BeautifulSoup(html_content, 'html.parser') esg_data = { 'url': url, 'environmental': self.extract_environmental_data(soup), 'social': self.extract_social_data(soup), 'governance': self.extract_governance_data(soup) } self.esg_data.append(esg_data) # Extract news links and PDFs self.extract_news_links(soup, url) self.extract_pdf_links(soup, url) def extract_environmental_data(self, soup): keywords = ['carbon footprint', 'emissions', 'renewable energy', 'waste management', 'climate change'] return self.extract_keyword_data(soup, keywords) def extract_social_data(self, soup): keywords = ['diversity', 'inclusion', 'human rights', 'labor practices', 'community engagement'] return self.extract_keyword_data(soup, keywords) def extract_governance_data(self, soup): keywords = ['board structure', 'executive compensation', 'shareholder rights', 'ethics', 'transparency'] return self.extract_keyword_data(soup, keywords) def extract_keyword_data(self, soup, keywords): text = soup.get_text().lower() return {keyword: len(re.findall(r'\b' + re.escape(keyword) + r'\b', text)) for keyword in keywords} def extract_news_links(self, soup, base_url): news_keywords = ['news', 'press release', 'article', 'blog', 'sustainability'] for a in soup.find_all('a', href=True): if any(keyword in a.text.lower() for keyword in news_keywords): full_url = urljoin(base_url, a['href']) if full_url not in self.news_links: self.news_links.append({'url': full_url, 'text': a.text.strip()}) def extract_pdf_links(self, soup, base_url): for a in soup.find_all('a', href=True): if a['href'].lower().endswith('.pdf'): full_url = urljoin(base_url, a['href']) if full_url not in self.pdf_links: self.pdf_links.append({'url': full_url, 'text': a.text.strip()}) def is_relevant_to_sustainable_finance(self, text): keywords = ['sustainable finance', 'esg', 'green bond', 'social impact', 'environmental impact', 'climate risk', 'sustainability report', 'corporate responsibility'] return any(keyword in text.lower() for keyword in keywords) def attempt_crawl(self, url, user_agent): for _ in range(self.max_retries): try: response = self.session.get(url, headers=self.get_headers(user_agent), timeout=10) if response.status_code == 200: print(f"Successfully crawled: {url}") if response.headers.get('Content-Type', '').startswith('text/html'): self.extract_esg_data(url, response.text) elif response.headers.get('Content-Type', '').startswith('application/pdf'): self.save_pdf(url, response.content) return True else: print(f"Failed to crawl {url}: HTTP {response.status_code}") except requests.RequestException as e: print(f"Error crawling {url} with {user_agent}: {e}") self.delay() return False def crawl_url(self, url): if not self.can_fetch(url, self.desktop_user_agent): print(f"Robots.txt disallows desktop user agent: {url}") if self.can_fetch(url, self.mobile_user_agent): print(f"Attempting with mobile user agent: {url}") return self.attempt_crawl(url, self.mobile_user_agent) else: print(f"Robots.txt disallows both user agents: {url}") return False return self.attempt_crawl(url, self.desktop_user_agent) def crawl(self, max_pages=100): self.setup() if not self.urls_to_crawl: self.urls_to_crawl.append(self.start_url) pages_crawled = 0 while self.urls_to_crawl and pages_crawled < max_pages: url = self.urls_to_crawl.popleft() if url not in self.crawled_urls: if self.crawl_url(url): pages_crawled += 1 self.crawled_urls.add(url) self.delay() print(f"Crawling complete. Successfully crawled {pages_crawled} pages.") self.save_esg_data() self.save_news_links() self.save_pdf_links() def save_esg_data(self): with open('esg_data.csv', 'w', newline='', encoding='utf-8') as file: writer = csv.DictWriter(file, fieldnames=['url', 'environmental', 'social', 'governance']) writer.writeheader() for data in self.esg_data: writer.writerow({ 'url': data['url'], 'environmental': ', '.join([f"{k}: {v}" for k, v in data['environmental'].items()]), 'social': ', '.join([f"{k}: {v}" for k, v in data['social'].items()]), 'governance': ', '.join([f"{k}: {v}" for k, v in data['governance'].items()]) }) print("ESG data saved to esg_data.csv") def save_news_links(self): with open('news_links.csv', 'w', newline='', encoding='utf-8') as file: writer = csv.DictWriter(file, fieldnames=['url', 'text', 'relevant']) writer.writeheader() for news in self.news_links: writer.writerow({ 'url': news['url'], 'text': news['text'], 'relevant': self.is_relevant_to_sustainable_finance(news['text']) }) print("News links saved to news_links.csv") def save_pdf_links(self): # Code for saving PDF in S3 or filesystem def save_pdf(self, url, content): # Code for saving PDF in S3 or filesystem # Example usage if __name__ == "__main__": start_url = input("Enter the starting URL to crawl for ESG data and news: ") crawler = EnhancedESGCrawler(start_url) crawler.crawl(max_pages=50)
  2. Configurez les différents attributs, notamment les agents utilisateurs, les collections vides pour URLs et les listes de stockage de données.

  3. Ajustez les mots clés et les critères de pertinence de la is_relevant_to_sustainable_finance() méthode en fonction de vos besoins spécifiques.

  4. Assurez-vous que le fichier robots.txt autorise l'exploration du site Web et que vous utilisez le délai d'exploration et l'agent utilisateur spécifiés dans le fichier robots.txt.

  5. Envisagez d'apporter les personnalisations suivantes au script d'exploration Web fourni, selon les besoins de votre organisation :

    • Implémentez la fetch_sitemap() méthode pour une découverte d'URL plus efficace.

    • Améliorez la journalisation et la gestion des erreurs pour une utilisation en production.

    • Mettez en œuvre une analyse de pertinence du contenu plus sophistiquée.

    • Ajoutez des contrôles de profondeur et de largeur pour limiter la portée de l'exploration.