# # 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)
Web Scraping von Foren: Bürgergeld Forum
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.
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"):
= os.path.join(directory, filename)
filepath with open(filepath, "r", encoding="utf-8") as file:
= file.read()
html_content = BeautifulSoup(html_content, "html.parser")
soup
# Find all relevant elements on the page
for subject, stats, last_post in zip(soup.find_all("li", class_="columnSubject"),
"li", class_="columnStats"),
soup.find_all("li", class_="columnLastPost")):
soup.find_all(#Extract text from each element, handle potential missing elements gracefully
= subject.text.strip() if subject else ""
subject_text = stats.text.strip() if stats else ""
stats_text = last_post.text.strip() if last_post else ""
last_post_text
data.append([subject_text, stats_text, last_post_text])
return pd.DataFrame(data, columns=["Subject", "Stats", "LastPost"])
# Example usage
= "buergergeld_forum"
directory = extract_data(directory)
df 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'
'User_LastPost', 'Date_LastPost']] = df['LastPost'].str.split('\n', n=1, expand=True)
df[[
# 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"
'Antworten', 'Zugriffe']] = df['Stats'].str.split('\n\n\n', n=1, expand=True)
df[[
# 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"
'Zugriffe'] = df['Zugriffe'].str.split('\n\n\n', n=1).str[0] df[
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
'Antworten'] = df['Antworten'].str.split('\n').str[1]
df[
# Split the 'Zugriffe' column at the newline character '\n' and keep the second part
'Zugriffe'] = df['Zugriffe'].str.split('\n').str[1] df[
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"
'Subject_Part1', 'Subject_Part2']] = df['Subject'].str.split('\n', n=1, expand=True) df[[
# 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
'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) df[[
# 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
'Subject_Part2_2_1', 'Subject_Part2_2_2']] = df['Subject_Part2_2'].str.split('\n\n\n', n=1, expand=True) df[[
# 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'
'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) df[[
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.drop(columns=['Stats', 'LastPost', 'Subject_Part2', 'Subject_Part2_2', 'Subject_Part2_2_1', 'Subject', 'Subject_Part2_2_2']) df
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
= df['Antworten'].equals(df['Subject_Part2_1'])
comparison_result
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
= df['User_LastPost'].equals(df['Subject_Part2_2_2_split'])
comparison_result_user = df['Date_LastPost'].equals(df['Subject_Part2_2_2_rest'])
comparison_result_date
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.rename(columns={
df '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
= ['Thema', 'Nutzer_Letzter_Post', 'Datum_Letzter_Post', 'Antworten', 'Zugriffe']
new_column_order
# Get the remaining columns not in the new order
= [col for col in df.columns if col not in new_column_order]
remaining_columns
# Combine the new column order with the remaining columns
= new_column_order + remaining_columns
final_column_order
# Reorder the DataFrame columns
= df[final_column_order] df
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 ','
'Zugriffe'] = df['Zugriffe'].str.replace('k', '000', regex=False).str.replace('.', '', regex=False).str.replace(',', '', regex=False)
df[
# Convert the column to integers, handling potential errors
'Zugriffe'] = pd.to_numeric(df['Zugriffe'], errors='coerce').fillna(0).astype(int) df[
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
'Antworten2'] = pd.to_numeric(df['Antworten2'], errors='coerce').fillna(0).astype(int) df[
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'
'Jahr_Letzter_Post'] = df['Datum_Letzter_Post'].str.extract(r'(\d{4})')
df[
# Convert the 'Jahr_Letzter_Post' column to integers
'Jahr_Letzter_Post'] = pd.to_numeric(df['Jahr_Letzter_Post'], errors='coerce').astype('Int64') df[
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.
0] df.T[
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
= ["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"]
german_stopwords = set(STOPWORDS).union(german_stopwords)
stopwords
# Assuming 'df' is your DataFrame and 'Thema' is the column with topics
= " ".join(str(topic) for topic in df['Thema'])
text
# Generate the word cloud
= WordCloud(width=800, height=400, background_color="white", stopwords=stopwords).generate(text)
wordcloud
# Display the word cloud
=(10, 5))
plt.figure(figsize="bilinear")
plt.imshow(wordcloud, interpolation"off")
plt.axis( 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
=(10, 6)) # Adjust figure size as needed
plt.figure(figsize'Jahr_Letzter_Post'].value_counts().sort_index().plot(kind='line', marker='o')
df[
# Customize the plot
"Jahr")
plt.xlabel("Anzahl der Posts")
plt.ylabel("Anzahl der Posts pro Jahr")
plt.title(=45, ha='right') # Rotate x-axis labels for better readability
plt.xticks(rotationTrue) # Add grid for better visualization
plt.grid(
# Show the plot
# Adjust layout to prevent labels from overlapping
plt.tight_layout() 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.sort_values(by='Jahr_Letzter_Post', ascending=True).head(10)
df_lowest_years
# Select the desired columns
= df_lowest_years[['Thema', 'Jahr_Letzter_Post', 'Datum_Letzter_Post']]
result 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.sort_values(by='Jahr_Letzter_Post', ascending=False).head(10)
df_highest_years
# Select the desired columns
= df_highest_years[['Thema', 'Jahr_Letzter_Post', 'Datum_Letzter_Post']]
result 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:
- HTML-Seiten herunterladen:
- Wir haben gelernt, wie man HTML-Seiten eines Forums herunterlädt und in einem Ordner speichert.
- Daten extrahieren:
- Wir haben die Texte der relevanten HTML-Elemente extrahiert und in einem Pandas DataFrame gespeichert.
- Daten bereinigen und verarbeiten:
- Wir haben die extrahierten Daten bereinigt und in verschiedene Spalten aufgeteilt, um sie besser analysieren zu können.
- Daten analysieren:
- Wir haben verschiedene Analysetechniken angewendet, darunter das Erstellen von Wordclouds und Lineplots, um die Daten zu visualisieren und zu interpretieren.
- 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.