Web Scraping von Foren: Bürgergeld Forum

Ein Tool zur Extraktion und Analyse von Forenbeiträgen aus dem Bürgergeld Forum, einschließlich der Verarbeitung und Visualisierung der Daten.

Lernziele

Web Scraping von Foren

  • Herunterladen und Speichern von HTML-Seiten eines Forums.
  • Extraktion und Analyse von Forenbeiträgen und Metadaten.
  • Verarbeitung und Bereinigung der extrahierten Daten mit Pandas.

HTML-Seiten herunterladen

Dieser Code lädt HTML-Seiten des Bürgergeld Forums herunter und speichert sie in einem Ordner.

# # prompt: schreibe python code um die html webseiten https://www.buergergeld.org/forum/board/63-anspruch-und-leistungen/?pageNo=1 https://www.buergergeld.org/forum/board/63-anspruch-und-leistungen/?pageNo=2 https://www.buergergeld.org/forum/board/63-anspruch-und-leistungen/?pageNo=3 bis https://www.buergergeld.org/forum/board/63-anspruch-und-leistungen/?pageNo=95 herunterzuladen und in einem ordner zu speichern

# import requests
# import os

# def download_pages(base_url, start_page, end_page, output_dir):
#   """Downloads HTML pages from a given URL range and saves them to a directory.

#   Args:
#     base_url: The base URL of the pages to download.
#     start_page: The starting page number.
#     end_page: The ending page number.
#     output_dir: The directory to save the downloaded pages.
#   """

#   if not os.path.exists(output_dir):
#     os.makedirs(output_dir)

#   for page_number in range(start_page, end_page + 1):
#     url = f"{base_url}?pageNo={page_number}"
#     try:
#       response = requests.get(url)
#       response.raise_for_status()  # Raise an exception for bad status codes

#       filename = os.path.join(output_dir, f"page_{page_number}.html")
#       with open(filename, "w", encoding="utf-8") as file:
#         file.write(response.text)
#       print(f"Downloaded page {page_number} to {filename}")

#     except requests.exceptions.RequestException as e:
#       print(f"Error downloading page {page_number}: {e}")

# # Example usage
# base_url = "https://www.buergergeld.org/forum/board/63-anspruch-und-leistungen/"
# start_page = 1
# end_page = 95
# output_directory = "buergergeld_forum"

# download_pages(base_url, start_page, end_page, output_directory)

Um das wiederholte Abfragen gleicher Inhalte zu vermeiden, können die gesammelten Seiten als Ordner heruntergeladen werden: buergergeld_forum.zip

Daten extrahieren

Dieser Code extrahiert die Texte der Elemente <li class="columnSubject">, <li class="columnStats"> und <li class="columnLastPost"> aus den HTML-Dateien und speichert sie in einem Pandas DataFrame.

# prompt: bitte loope über alle html dateien im ordner buergergeld_forum und extrahiere die texte der elemente <li class="columnSubject"> <li class="columnStats"> und <li class="columnLastPost"> und speichere sie in einem pandas dataframe

import pandas as pd
import os
from bs4 import BeautifulSoup

def extract_data(directory):
    data = []
    for filename in os.listdir(directory):
        if filename.endswith(".html"):
            filepath = os.path.join(directory, filename)
            with open(filepath, "r", encoding="utf-8") as file:
                html_content = file.read()
                soup = BeautifulSoup(html_content, "html.parser")

                # Find all relevant elements on the page
                for subject, stats, last_post in zip(soup.find_all("li", class_="columnSubject"),
                                                     soup.find_all("li", class_="columnStats"),
                                                     soup.find_all("li", class_="columnLastPost")):
                    #Extract text from each element, handle potential missing elements gracefully
                    subject_text = subject.text.strip() if subject else ""
                    stats_text = stats.text.strip() if stats else ""
                    last_post_text = last_post.text.strip() if last_post else ""

                    data.append([subject_text, stats_text, last_post_text])

    return pd.DataFrame(data, columns=["Subject", "Stats", "LastPost"])


# Example usage
directory = "buergergeld_forum"
df = extract_data(directory)
df

Daten reinigen

Spalte ‘LastPost’ aufteilen

Dieser Code teilt die Spalte df.LastPost in Nutzer_LastPost und Datum_LastPost auf.

# prompt: bitte separariere die spalte df.lastpost in nutzername last post und datum last post indem die spalte an der stelle " \n" getrennt wird

import pandas as pd

# Assuming df is your DataFrame and it has a column named 'LastPost'
# Split the 'LastPost' column at the newline character '\n'
df[['User_LastPost', 'Date_LastPost']] = df['LastPost'].str.split('\n', n=1, expand=True)

# Display the updated DataFrame
df

Spalte ‘Stats’ aufteilen

Dieser Code teilt die Spalte df.Stats in Antworten und Zugriffe auf.

# prompt: bitte teile die spalte df.Stats in zwei spalten an der stelle des ersten vorkommens von "\n\n\n". nenne die erste neue spalte Antworten und die zweite neue Spalte Zugriffe

# Split the 'Stats' column at the first occurrence of "\n\n\n"
df[['Antworten', 'Zugriffe']] = df['Stats'].str.split('\n\n\n', n=1, expand=True)

# Display the updated DataFrame
df

Spalte ‘Zugriffe’ bereinigen

Dieser Code bereinigt die Spalte df.Zugriffe, indem nur der Inhalt bis zum ersten Vorkommen von \n\n\n behalten wird.

# prompt: reinige die spalte df.Zugriffe indem nur der inhalt bis zum ersten vorkommen von "\n\n\n" behalten wird

# Clean the 'Zugriffe' column by keeping only the content up to the first occurrence of "\n\n\n"
df['Zugriffe'] = df['Zugriffe'].str.split('\n\n\n', n=1).str[0]

Spalten ‘Antworten’ und ‘Zugriffe’ weiter aufteilen

Dieser Code teilt die Spalten df.Antworten und df.Zugriffe jeweils an der Stelle \n und behält jeweils den zweiten Teil.

# prompt: für die spalten df.Antworten und df.Zugriffe splitte jeweils an der Stelle "\n" und behalte jeweils die zweiten part

# Split the 'Antworten' column at the newline character '\n' and keep the second part
df['Antworten'] = df['Antworten'].str.split('\n').str[1]

# Split the 'Zugriffe' column at the newline character '\n' and keep the second part
df['Zugriffe'] = df['Zugriffe'].str.split('\n').str[1]

Spalte ‘Subject’ aufteilen

Dieser Code teilt die Spalte df.Subject am ersten Vorkommen von \n in zwei separate Spalten.

# prompt: in der spalte df.Subject trenne am ersten vorkommen von "\n" in zwei separate spalten

# Split the 'Subject' column at the first occurrence of "\n"
df[['Subject_Part1', 'Subject_Part2']] = df['Subject'].str.split('\n', n=1, expand=True)
# prompt: trenne die spalte df.Subject_Part2 an der stelle "\n\n\n\n\n\n\n\n\n\n\n\n" in zwei separate spalten

# Split the 'Subject_Part2' column at the specified string
df[['Subject_Part2_1', 'Subject_Part2_2']] = df['Subject_Part2'].str.split('\n\n\n\n\n\n\n\n\n\n\n\n', n=1, expand=True)
# prompt: trenne die spalte df.Subject_Part2_2 in zwei separate spalten an der stelle "\n\n\n"

# Split the 'Subject_Part2_2' column at the specified string
df[['Subject_Part2_2_1', 'Subject_Part2_2_2']] = df['Subject_Part2_2'].str.split('\n\n\n', n=1, expand=True)
# prompt: trenne die beiden spalten df.Subject_Part2_2_1    und df.Subject_Part2_2_2 jeweils an der stelle "\n" in separate spalten

# Split the 'Subject_Part2_2_1' and 'Subject_Part2_2_2' columns at the newline character '\n'
df[['Subject_Part2_2_1_split', 'Subject_Part2_2_1_rest']] = df['Subject_Part2_2_1'].str.split('\n', n=1, expand=True)
df[['Subject_Part2_2_2_split', 'Subject_Part2_2_2_rest']] = df['Subject_Part2_2_2'].str.split('\n', n=1, expand=True)

Unnötige Spalten entfernen

Dieser Code entfernt die Spalten Stats, LastPost, Subject_Part2, Subject_Part2_2, Subject_Part2_2_1, Subject und Subject_Part2_2_2 aus dem DataFrame df.

# prompt: entferne die spalten Stats, LastPost, Subject_Part2, Subject_Part2_2, Subject_Part2_2_1, Subject und Subject_Part2_2_2 aus dataframe df

df = df.drop(columns=['Stats', 'LastPost', 'Subject_Part2', 'Subject_Part2_2', 'Subject_Part2_2_1', 'Subject', 'Subject_Part2_2_2'])

Spalteninhalte vergleichen

Dieser Code vergleicht die Inhalte der Spalten Antworten und Subject_Part2_1.

# prompt: hat die spalte Antworten den gleichen inhalt wie die spalte Subject_Part2_1 ?

# Check if the 'Antworten' column has the same content as the 'Subject_Part2_1' column
comparison_result = df['Antworten'].equals(df['Subject_Part2_1'])

print(f"Do the 'Antworten' and 'Subject_Part2_1' columns have the same content?: {comparison_result}")

Weitere Spalteninhalte vergleichen

Dieser Code vergleicht die Inhalte der Spalten User_LastPost und Subject_Part2_2_2_split sowie Date_LastPost und Subject_Part2_2_2_rest.

# prompt: haben die spalten user_lastpost und date_lastpost die gleichen inhalte wie Subject_Part2_2_2_split und Subject_Part2_2_2_rest

# Check if 'user_lastpost' and 'date_lastpost' have the same content as 'Subject_Part2_2_2_split' and 'Subject_Part2_2_2_rest' respectively
comparison_result_user = df['User_LastPost'].equals(df['Subject_Part2_2_2_split'])
comparison_result_date = df['Date_LastPost'].equals(df['Subject_Part2_2_2_rest'])

print(f"Do the 'User_LastPost' and 'Subject_Part2_2_2_split' columns have the same content?: {comparison_result_user}")
print(f"Do the 'Date_LastPost' and 'Subject_Part2_2_2_rest' columns have the same content?: {comparison_result_date}")

Spalten umbenennen

Dieser Code benennt die Spalten im DataFrame df um.

# prompt: bitte nenne folgende spalten um im dataframe df : Subject_Part1 in "Thema", User_LastPost in "Nutzer_Letzter_Post", Date_LastPost in "Datum_Letzter_Post", Subject_Part2_1 in "Antworten2", Subject_Part2_2_1_split in "Nutzer", Subject_Part2_2_1_rest in "Datum2", Subject_Part2_2_2_split in "Nutzer3", Subject_Part2_2_2_rest in "Datum3"

df = df.rename(columns={
    'Subject_Part1': 'Thema',
    'User_LastPost': 'Nutzer_Letzter_Post',
    'Date_LastPost': 'Datum_Letzter_Post',
    'Subject_Part2_1': 'Antworten2',
    'Subject_Part2_2_1_split': 'Nutzer2',
    'Subject_Part2_2_1_rest': 'Datum2',
    'Subject_Part2_2_2_split': 'Nutzer3',
    'Subject_Part2_2_2_rest': 'Datum3'
})

Spaltenreihenfolge ändern

Dieser Code ändert die Reihenfolge der Spalten im DataFrame df.

# prompt: bitte ändere die reihenfolge der spalten folgendermaßen: Thema, Nutzer_Letzter_Post, Datum_Letzter_Post, Antworten, Zugriffe, und dann die restlichen spalten

# Define the desired column order
new_column_order = ['Thema', 'Nutzer_Letzter_Post', 'Datum_Letzter_Post', 'Antworten', 'Zugriffe']

# Get the remaining columns not in the new order
remaining_columns = [col for col in df.columns if col not in new_column_order]

# Combine the new column order with the remaining columns
final_column_order = new_column_order + remaining_columns

# Reorder the DataFrame columns
df = df[final_column_order]

Spalte ‘Zugriffe’ formatieren

Dieser Code formatiert die Spalte df.Zugriffe als ganze Zahl.

# prompt: bitte formatiere die spalte df.Zugriffe von als ganze zahl, beachte die schreibweise mit , als tausendertrenner und k als abkürzung für tausend

import pandas as pd

# Assuming df is your DataFrame and 'Zugriffe' column needs formatting
# Replace 'k' with '000' and remove ','
df['Zugriffe'] = df['Zugriffe'].str.replace('k', '000', regex=False).str.replace('.', '', regex=False).str.replace(',', '', regex=False)


# Convert the column to integers, handling potential errors
df['Zugriffe'] = pd.to_numeric(df['Zugriffe'], errors='coerce').fillna(0).astype(int)

Spalten ‘Antworten’ und ‘Antworten2’ formatieren

Dieser Code formatiert die Spalten df.Antworten und df.Antworten2 als ganze Zahl.

# prompt: bitte ändere die spalte df.Antworten df.Antworten2 in ganze zahl

# Convert 'Antworten' column to integers, handling errors
df['Antworten2'] = pd.to_numeric(df['Antworten2'], errors='coerce').fillna(0).astype(int)

Jahr aus ‘Datum_Letzter_Post’ extrahieren

Dieser Code extrahiert Jahreszahlen aus der Spalte df.Datum_Letzter_Post und speichert sie in der Spalte Jahr_Letzter_Post.

# prompt: extrahiere zahlen mit jahresformat in der spalte df.Datum_Letzter_Post   und speichere sie in der Spalte "Jahr_Letzter_Post" und setzt die neue spalte als datentyp ganze zahl

import pandas as pd

# Assuming df is your DataFrame and 'Datum_Letzter_Post' column contains dates
# Extract the year from the 'Datum_Letzter_Post' column and store it in 'Jahr_Letzter_Post'
df['Jahr_Letzter_Post'] = df['Datum_Letzter_Post'].str.extract(r'(\d{4})')

# Convert the 'Jahr_Letzter_Post' column to integers
df['Jahr_Letzter_Post'] = pd.to_numeric(df['Jahr_Letzter_Post'], errors='coerce').astype('Int64')

DataFrame-Informationen anzeigen

Dieser Code zeigt Informationen über den DataFrame df an.

df.info()

Transponierte Daten anzeigen

Dieser Code zeigt die transponierten Daten des DataFrame df an.

df.T[0]

Analyse

Dieser Abschnitt enthält die Analyse der Daten.

Wordcloud der Themen

Dieser Code zeigt die Themen mit den meisten Zugriffen als Wordcloud und entfernt zuvor deutsche Stopwörter.

# prompt: zeige die themen mit den meisten zugriffen als wordcloud und entferne deutsche stopwörter zuvor

from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt

# Add German stopwords to the default set
german_stopwords = ["und", "der", "die", "das", "in", "den", "zu", "von", "mit", "sich", "des", "eine", "dem", "als", "ist", "für", "auf", "nicht", "auch", "so", "ein", "es", "im", "am", "zum", "über", "er", "sie", "an", "einem", "einer", "werden", "da", "dass", "was", "aber", "wenn", "wie", "wir", "noch", "hat", "man", "hier", "bei", "oder", "nur", "mehr", "nun", "durch", "wieder", "gibt", "wird", "einem", "seine", "ihre", "ihren", "ihrem", "eines", "alles", "einige", "anderer", "anderen", "andere", "dieses", "unser", "euer", "deren"]
stopwords = set(STOPWORDS).union(german_stopwords)

# Assuming 'df' is your DataFrame and 'Thema' is the column with topics
text = " ".join(str(topic) for topic in df['Thema'])

# Generate the word cloud
wordcloud = WordCloud(width=800, height=400, background_color="white", stopwords=stopwords).generate(text)

# Display the word cloud
plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.show()

Lineplot der Jahreszahlen

Dieser Code zeigt die Spalte df.Jahr_Letzter_Post als Lineplot und stellt sicher, dass die x-Achse ganze Jahreszahlen zeigt.

# prompt: zeige die spalte df.Jahr_Letzter_Post  lineplot. beachte dass die x achse als ganze jahreszahlen zeigt

import matplotlib.pyplot as plt

# Assuming df is your DataFrame and 'Jahr_Letzter_Post' is the column with years
# Create the line plot
plt.figure(figsize=(10, 6))  # Adjust figure size as needed
df['Jahr_Letzter_Post'].value_counts().sort_index().plot(kind='line', marker='o')

# Customize the plot
plt.xlabel("Jahr")
plt.ylabel("Anzahl der Posts")
plt.title("Anzahl der Posts pro Jahr")
plt.xticks(rotation=45, ha='right') # Rotate x-axis labels for better readability
plt.grid(True) # Add grid for better visualization

# Show the plot
plt.tight_layout() # Adjust layout to prevent labels from overlapping
plt.show()

Themen mit den geringsten Jahreszahlen

Dieser Code zeigt die Themen und die Jahreszahl sowie Datum_Letzter_Post für die 10 Zeilen mit den geringsten Werten bei df.Jahr_Letzter_Post.

# prompt: für 10 zeilen mit den geringsten werten bei df.Jahr_Letzter_Post zeige die Themen und die Jahreszahl und Datum_Letzter_Post

# Display the 10 rows with the lowest values in 'Jahr_Letzter_Post'
# along with the 'Thema' and 'Datum_Letzter_Post' columns.
# Sort by 'Jahr_Letzter_Post' in ascending order and then display the first 10 rows
df_lowest_years = df.sort_values(by='Jahr_Letzter_Post', ascending=True).head(10)

# Select the desired columns
result = df_lowest_years[['Thema', 'Jahr_Letzter_Post', 'Datum_Letzter_Post']]
result

Themen mit den höchsten Jahreszahlen

Dieser Code zeigt die Themen und die Jahreszahl sowie Datum_Letzter_Post für die 10 Zeilen mit den höchsten Werten bei df.Jahr_Letzter_Post.

# prompt: für 10 zeilen mit den höchsten werten bei df.Jahr_Letzter_Post zeige die Themen und die Jahreszahl und Datum_Letzter_Post

# Display the 10 rows with the highest values in 'Jahr_Letzter_Post'
# along with the 'Thema' and 'Datum_Letzter_Post' columns.
# Sort by 'Jahr_Letzter_Post' in descending order and then display the first 10 rows
df_highest_years = df.sort_values(by='Jahr_Letzter_Post', ascending=False).head(10)

# Select the desired columns
result = df_highest_years[['Thema', 'Jahr_Letzter_Post', 'Datum_Letzter_Post']]
result

Speichern

Dieser Abschnitt enthält den Code zum Speichern der Daten.

Ordner komprimieren

Dieser Code komprimiert den Ordner /content/buergergeld_forum.

# # prompt: bitte kompriere den ordner /content/buergergeld_forum

# import zipfile
# import os

# def compress_directory(directory_path, output_filename):
#     """Compresses a directory into a zip file.

#     Args:
#         directory_path: The path to the directory to compress.
#         output_filename: The name of the output zip file.
#     """

#     try:
#         with zipfile.ZipFile(output_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
#             for root, _, files in os.walk(directory_path):
#                 for file in files:
#                     file_path = os.path.join(root, file)
#                     arcname = os.path.relpath(file_path, directory_path)
#                     zipf.write(file_path, arcname)
#         print(f"Successfully compressed '{directory_path}' to '{output_filename}'")

#     except FileNotFoundError:
#         print(f"Error: Directory '{directory_path}' not found.")
#     except Exception as e:
#         print(f"An error occurred: {e}")

# # Example usage:
# directory_to_compress = "/content/buergergeld_forum"
# output_zip_filename = "/content/buergergeld_forum.zip"  # Specify the desired output filename

# compress_directory(directory_to_compress, output_zip_filename)

Fazit

In diesem Notebook haben wir die grundlegenden Techniken des Web Scraping anhand des Bürgergeld Forums erlernt und angewendet. Die wichtigsten gelernten Inhalte umfassen:

  1. HTML-Seiten herunterladen:
    • Wir haben gelernt, wie man HTML-Seiten eines Forums herunterlädt und in einem Ordner speichert.
  2. Daten extrahieren:
    • Wir haben die Texte der relevanten HTML-Elemente extrahiert und in einem Pandas DataFrame gespeichert.
  3. Daten bereinigen und verarbeiten:
    • Wir haben die extrahierten Daten bereinigt und in verschiedene Spalten aufgeteilt, um sie besser analysieren zu können.
  4. Daten analysieren:
    • Wir haben verschiedene Analysetechniken angewendet, darunter das Erstellen von Wordclouds und Lineplots, um die Daten zu visualisieren und zu interpretieren.
  5. Daten speichern und komprimieren:
    • Wir haben gelernt, wie man die verarbeiteten Daten speichert und den Ordner mit den HTML-Dateien komprimiert.

Diese Schritte haben uns gezeigt, wie man Web Scraping effektiv einsetzen kann, um wertvolle Informationen aus Online-Foren zu extrahieren und zu analysieren.