

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Den Webcrawler erstellen
<a name="building-crawler"></a>

Wie im [Architektur](architecture.md) Abschnitt beschrieben, wird die Anwendung in Batches ausgeführt — einer für jedes Unternehmen.

**Topics**
+ [Erfassen und Verarbeiten der Datei robots.txt](#building-crawler-robots-txt)
+ [Erfassung und Verarbeitung der Sitemap](#building-crawler-sitemap)
+ [Den Crawler entwerfen](#building-crawler-design)

## Erfassen und Verarbeiten der Datei robots.txt
<a name="building-crawler-robots-txt"></a>

Nachdem Sie den Datensatz vorbereitet haben, müssen Sie überprüfen, ob die Domain über eine Datei [robots.txt](https://en.wikipedia.org/wiki/Robots.txt) verfügt. Bei Webcrawlern und anderen Bots gibt die Datei robots.txt an, welche Bereiche der Website sie besuchen dürfen. Die Einhaltung der Anweisungen in dieser Datei ist eine wichtige bewährte Methode für das ethische Crawlen von Websites. Weitere Informationen finden Sie in diesem Leitfaden unter [Bewährte Methoden für ethische Webcrawler](best-practices.md).

**Um die Datei robots.txt zu erfassen und zu verarbeiten**

1. Falls Sie dies noch nicht getan haben, installieren Sie die `requests` Bibliothek, indem Sie den folgenden Befehl in einem Terminal ausführen:

   ```
   pip install requests
   ```

1. Führen Sie das folgende Skript aus. Dieses Skript führt folgende Aktionen aus:
   + Es definiert eine `check_robots_txt` Funktion, die eine Domain als Eingabe verwendet.
   + Es erstellt die vollständige URL für die Datei robots.txt.
   + Es sendet eine GET-Anfrage an die URL für die Datei robots.txt.
   + Wenn die Anfrage erfolgreich ist (Statuscode 200), ist eine Datei robots.txt vorhanden.
   + Wenn die Anfrage fehlschlägt oder einen anderen Statuscode zurückgibt, ist die Datei robots.txt nicht vorhanden oder es kann nicht darauf zugegriffen werden.

   ```
   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
   ```
**Anmerkung**  
Dieses Skript behandelt Ausnahmen für Netzwerkfehler oder andere Probleme.

1. Wenn eine Datei robots.txt existiert, verwenden Sie das folgende Skript, um sie herunterzuladen:

   ```
   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
   ```
**Anmerkung**  
Diese Skripts können je nach Anwendungsfall angepasst oder geändert werden. Sie können diese Skripte auch kombinieren.

## Erfassung und Verarbeitung der Sitemap
<a name="building-crawler-sitemap"></a>

Als Nächstes müssen Sie die [Sitemap](https://en.wikipedia.org/wiki/Site_map) bearbeiten. Sie können die Sitemap verwenden, um das Crawlen auf wichtige Seiten zu konzentrieren. Dies verbessert die Crawling-Effizienz. Weitere Informationen finden Sie in diesem Leitfaden unter [Bewährte Methoden für ethische Webcrawler](best-practices.md).

**Um die Sitemap zu erfassen und zu verarbeiten**
+ Führen Sie das folgende Skript aus. Dieses Skript definiert eine `check_and_download_sitemap` Funktion, die:
  + Akzeptiert eine Basis-URL, eine optionale Sitemap-URL aus robots.txt und eine User-Agent-Zeichenfolge.
  + Überprüft mehrere potenzielle Sitemap-Speicherorte, einschließlich des Speicherorts von robots.txt (falls angegeben).
  + Versucht, die Sitemap von jedem Standort herunterzuladen.
  + Überprüft, ob der heruntergeladene Inhalt im XML-Format vorliegt.
  + Ruft die `parse_sitemap` Funktion zum Extrahieren von auf URLs. Diese Funktion:
    + Analysiert den XML-Inhalt der Sitemap.
    + Verarbeitet sowohl reguläre Sitemaps als auch Sitemap-Indexdateien.
    + Ruft rekursiv Unter-Sitemaps ab, wenn ein Sitemap-Index gefunden wird.

  ```
  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("...")
  ```

## Den Crawler entwerfen
<a name="building-crawler-design"></a>

Als Nächstes entwerfen Sie den Webcrawler. Der Crawler ist so konzipiert, dass er den [Bewährte Methoden für ethische Webcrawler](best-practices.md) in diesem Handbuch beschriebenen bewährten Methoden folgt. In diesem `EthicalCrawler` Kurs werden mehrere wichtige Prinzipien des ethischen Crawlings vorgestellt:
+ Die **Datei robots.txt abrufen und analysieren — Der Crawler ruft die Datei** robots.txt für die Zielwebsite ab.
+ **Einhaltung der Crawling-Berechtigungen** — Vor dem Crawlen einer URL überprüft der Crawler, ob die Regeln in der Datei robots.txt das Crawlen für diese URL zulassen. Wenn eine URL nicht erlaubt ist, überspringt der Crawler sie und wechselt zur nächsten URL.
+ **Berücksichtigung der Crawlverzögerung — Der Crawler** sucht in der Datei robots.txt nach einer Crawl-Delay-Direktive. Wenn eine angegeben ist, verwendet der Crawler diese Verzögerung zwischen Anfragen. Andernfalls verwendet er eine Standardverzögerung.
+ **User-Agent-Identifikation** — Der Crawler verwendet eine benutzerdefinierte User-Agent-Zeichenfolge, um sich gegenüber Websites zu identifizieren. Bei Bedarf können Webseitenbesitzer spezifische Regeln festlegen, um deinen Crawler einzuschränken oder zuzulassen.
+ **Fehlerbehandlung und ordentliche Degradierung** — Wenn die Datei robots.txt nicht abgerufen oder analysiert werden kann, verwendet der Crawler konservative Standardregeln. Er behandelt Netzwerkfehler und HTTP-Antworten, die nicht 200 sind.
+ **Eingeschränktes Crawlen** — Um eine Überlastung des Servers zu vermeiden, gibt es ein Limit für die Anzahl der Seiten, die gecrawlt werden können.

Das folgende Skript ist Pseudocode, der erklärt, wie der Webcrawler funktioniert:

```
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.")
```

**Um einen fortschrittlichen, ethischen Webcrawler zu entwickeln, der ESG-Daten sammelt**

1. Kopieren Sie das folgende Codebeispiel für den fortschrittlichen ethischen Webcrawler, der in diesem System verwendet wird:

   ```
   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)
   ```

1. Richten Sie die verschiedenen Attribute ein, darunter Benutzeragenten, leere Sammlungen für URLs und Datenspeicherlisten.

1. Passen Sie die Schlüsselwörter und Relevanzkriterien in der `is_relevant_to_sustainable_finance()` Methode an Ihre spezifischen Bedürfnisse an.

1. Stellen Sie sicher, dass die Datei robots.txt das Crawlen der Website zulässt und dass Sie die in der Datei robots.txt angegebene Crawling-Verzögerung und den Benutzeragenten verwenden.

1. Erwägen Sie, das bereitgestellte Webcrawler-Skript nach Bedarf für Ihre Organisation wie folgt anzupassen:
   + Implementieren Sie die `fetch_sitemap()` Methode für eine effizientere URL-Erkennung.
   + Verbessern Sie die Fehlerprotokollierung und -behandlung für den Produktionseinsatz.
   + Implementieren Sie eine ausgefeiltere Analyse der Relevanz von Inhalten.
   + Fügen Sie Kontrollen für Tiefe und Breite hinzu, um den Umfang des Crawlings einzuschränken.