Einführung
Eine Redakteurin von Commonlight benötigt einen nutzbaren Fotokatalog, zunächst als kleine Stichprobe einer Seite. Du sendest echte HTTP-Anfragen, liest JSON, wählst Datensätze anhand ihrer Felder aus und speicherst eine CSV-Datei. Commonlight ist eine eigens erstellte lokale Demo-API: Datensätze und Namensnennungen sind erfunden; die Beispielbilder sind synthetische Illustrationen, keine dokumentarischen Fotos und keine Inhalte eines externen Fotodienstes.
Du solltest Python-Funktionen, Containerdatentypen wie Listen und Dictionaries, Schleifen, Ausnahmen und einfache Dateiausgabe kennen. HTTP, URLs, JSON und CSV werden hier eingeführt. Wähle in WebIDE den Menüpunkt Terminal → New Terminal (auf schmalen Bildschirmen kann das Terminal-Menü unter More liegen). Führe dort alle Terminalbefehle aus. Die vorbereitete Website erreichst du über Web 8080. Bearbeite nur client.py und storage.py; verwende die Standardbibliothek und Hilfsfunktionen innerhalb dieser Dateien, keine zusätzlichen Hilfsmodule oder Ressourcen. Liefere CSVs als echte reguläre Dateien im Arbeitsverzeichnis ab, nicht als symbolische Links; lasse catalog.py und /opt/commonlight unverändert. Die 40 Minuten für Lesen und Üben sind eine Schätzung des Autors, keine mit Anfängern gemessene Zeit.
Eine Katalogseite anfordern und verstehen
In diesem Schritt ordnest du drei API-Datensätze drei sichtbaren Karten zu und implementierst anschließend die Anfragefunktion. Wechsle zu Web 8080. Die Vorschau Harbor walks enthält Morning, market, Blue hour und Window light. Sie zeigt ausdrücklich eine Seite und nicht die gesamte Sammlung. Die Bilder liefern Kontext; dieser Kurs exportiert Metadaten, statt Bilder herunterzuladen.

Ordne die IDs unter den Illustrationen der Antwort zu, die du gleich anforderst.
Kehre zu WebIDE und dessen Terminal zurück. cd wechselt das Arbeitsverzeichnis; python3 führt Python aus. Verwende genau diesen Pfad:
cd /home/labex/project
HTTP ist das Anfrage-Antwort-Protokoll dieser lokalen API. Eine URL bezeichnet die Adresse: http ist das Protokoll, 127.0.0.1 steht für diese Umgebung selbst, 8080 ist der Dienstport und /api/harbor wählt eine Sammlung aus. Nach ? fordert die Abfrage page_size=3 höchstens drei Datensätze an. Diese Adresse gilt im Terminal; Web 8080 öffnet die von der Plattform bereitgestellte Browseradresse desselben Dienstes.
Das folgende Here-Dokument führt ein kleines Python-Programm aus. <<'PY' übergibt den unveränderten Text bis zum abschließenden PY an Pythons Standardeingabe; kopiere beide Begrenzungszeilen mit. urlopen sendet eine echte HTTP-Anfrage. timeout=3 begrenzt blockierende Vorgänge wie Verbindungsaufbau oder Warten auf einen Socket, nicht die Dauer jedes denkbaren vollständigen Auftrags. with schließt danach die Antwort. JSON ist strukturierter Text; json.load dekodiert die Antwort in Python-Daten.
python3 - <<'PY'
import json
from urllib.request import urlopen
with urlopen('http://127.0.0.1:8080/api/harbor?page_size=3', timeout=3) as response:
print('HTTP status:', response.status)
page = json.load(response)
for photo in page['items']:
print(photo['photo_id'], photo['title'])
print('More pages:', page['next_cursor'] is not None)
PY
HTTP status: 200
p01 Morning, market
p02 Blue hour
p03 Window light
More pages: True
Status 200 bedeutet, dass die Anfrage erfolgreich war. Das äußere JSON-Objekt wird zu einem Dictionary. Dessen Wert items ist eine Liste von Datensatz-Dictionaries, nicht die ganze Antwort. JSON-null wird zu Python-None. Hier ist next_cursor noch eine Zeichenkette, es gibt also weitere Seiten. Diese Einheit exportiert bewusst nur eine Seite; die nächste vervollständigt den Durchlauf.
Jeder Datensatz besitzt photo_id, title, category, orientation und credit. Eine stabile ID identifiziert ihn; sein Titel ist eine Anzeige und kann bei verschiedenen Fotos gleich sein. Den vorbereiteten Vertrag kannst du unter /opt/commonlight/api-contract.md lesen. cat zeigt eine Textdatei an:
cat /opt/commonlight/api-contract.md
Ergänze wiederverwendbare Anfrage- und Auswahlfunktionen. urlencode kodiert ein Dictionary als Abfragezeichenkette. Ein Cursor ist ein undurchsichtiges Fortsetzungs-Token: Kopiere ihn exakt, statt eine Zahl zu erraten oder seine Zeichen zu interpretieren. Das optionale Argument cursor=None lässt diesen Abfrageparameter in der ersten Anfrage weg. Spätere Seiten können Zeichen wie +, / und = enthalten, die kodiert werden müssen. Behalte URL und Seitengröße innerhalb eines Durchlaufs bei.
Die Auswahl vergleicht Kategorie und Ausrichtung exakt und beachtet Groß- und Kleinschreibung. Ohne Treffer ist eine leere Liste gültig. In der Listen-Comprehension besucht for photo in photos jeden Datensatz; if behält nur passende Datensätze; and verlangt zwei wahre Vergleiche. So entsteht eine neue Liste, ohne die empfangenen Datensätze zu verändern.
Der nächste Block verwendet cat mit >, um client.py durch den wörtlichen Python-Text zu ersetzen. Führe den gesamten Block einschließlich des letzten PY aus. Alternativ kannst du die Datei in WebIDE öffnen, den Inhalt ersetzen und mit Ctrl+S speichern (Cmd+S unter macOS). Dieser Block ändert nur diese Datei.
cat > client.py <<'PY'
"""The request and selection functions are completed in the first guided lab."""
import json
from urllib.parse import urlencode
from urllib.request import urlopen
def request_page(endpoint, page_size, cursor=None):
params = {'page_size': page_size}
if cursor is not None:
params['cursor'] = cursor
with urlopen(endpoint + '?' + urlencode(params), timeout=3) as response:
return json.load(response)
def select_photos(photos, category, orientation):
return [photo for photo in photos
if photo['category'] == category and photo['orientation'] == orientation]
def collect(endpoint, page_size):
return request_page(endpoint, page_size)['items']
PY
Die vorbereitete Befehlsschnittstelle ruft diese Funktionen auf. Vorläufig ist collect ausdrücklich ein Startpunkt für eine einzige Seite. Die Selbstprüfung testet die Anfrage gegen eine weitere echte lokale HTTP-Quelle. Deshalb muss die übergebene URL verwendet werden, statt eine feste Katalogantwort zurückzugeben. Die Prüfung erstellt deine CSV nicht für dich.
Falls ein Befehl eine Python-Datei nicht öffnen kann, zeige mit pwd das aktuelle Verzeichnis und mit ls die Dateinamen an. Kehre mit dem zuvor gezeigten cd nach /home/labex/project zurück, korrigiere den Dateinamen und versuche es erneut. Ein Zurücksetzen der VM ist nicht nötig.
Eine Seite filtern und eine echte CSV speichern
In diesem Schritt wählst du die Straßenaufnahmen im Querformat der ersten Seite aus und öffnest eine CSV mit ihren tatsächlichen Feldwerten. CSV steht für kommagetrennte Werte, aber ein Komma innerhalb eines Titels gehört zu einem einzigen Feld. Ein richtiger CSV-Writer setzt es in Anführungszeichen; bloßes Verketten oder Aufteilen an jedem Komma zerstört diesen Zusammenhang. UTF-8 erhält Namen wie Noé und nichtlateinische Schrift. newline='' überlässt dem csv-Modul die CSV-Zeilenenden.
FIELDS definiert die fünf Spaltennamen. Jeder erscheint genau einmal; eine gleichwertige Lösung darf Zeilen und Spalten anders anordnen. DictWriter ordnet jeden Dictionary-Wert seiner benannten Spalte zu. writeheader schreibt den Kopf und writerows die ausgewählten Datensätze; bei einer leeren Auswahl entsteht eine Datei nur mit Kopfzeile. Die Datei wird mit w geöffnet, wodurch ihr Inhalt ersetzt wird. Diese erste Einheit speichert eine Seitenstichprobe; die sichere Veröffentlichung eines vollständigen mehrseitigen Exports folgt danach.
Ersetze im Terminal von WebIDE nur storage.py:
cat > storage.py <<'PY'
"""Single-page CSV writer; safe publication is taught in the second guided lab."""
import csv
FIELDS = ['photo_id', 'title', 'category', 'orientation', 'credit']
def save_csv(output, photos):
with open(output, 'w', encoding='utf-8', newline='') as stream:
writer = csv.DictWriter(stream, fieldnames=FIELDS)
writer.writeheader()
writer.writerows(photos)
PY
Das vorbereitete catalog.py akzeptiert fünf Positionsargumente: Sammlungs-URL, Kategorie, Ausrichtung, maximale Seitengröße und Ausgabedateiname. --first-page wählt ausdrücklich den Stichprobenmodus. street und landscape sind die beiden exakten Filterwerte. Shell-Anführungszeichen halten eine URL oder einen Dateinamen mit Sonderzeichen zusammen.
python3 catalog.py 'http://127.0.0.1:8080/api/harbor' street landscape 3 page.csv --first-page
Saved 2 photos to page.csv
Lies das Ergebnis mit DictReader, das in Anführungszeichen gesetzte Felder wieder zusammensetzt. Dies liest die wirklich gespeicherte Datei, nicht die API:
python3 - <<'PY'
import csv
with open('page.csv', encoding='utf-8', newline='') as stream:
for photo in csv.DictReader(stream):
print(photo['photo_id'], '|', photo['title'], '|', photo['credit'])
PY
p01 | Morning, market | Mina Chen
p02 | Blue hour | Noé Silva
p03 fehlt, weil es ein Bild im Hochformat aus der Kategorie Zuhause ist. Das Komma im ersten Titel und der Akzent im Bildnachweis bleiben ihren Datensätzen zugeordnet.
Beobachte nun eine absichtlich ausgelöste HTTP-Ablehnung. Die API erlaubt Seitengrößen von 1–5; 9 liefert daher 400 statt einer erfolgreich gelesenen leeren Sammlung. Führe diesen Block aus; der erste Befehl soll fehlschlagen. $? ist der Exit-Status des unmittelbar vorherigen Befehls. printf zeigt ihn an, bevor ein anderer Befehl ihn ersetzt.
python3 catalog.py 'http://127.0.0.1:8080/api/harbor' street landscape 9 rejected.csv --first-page
printf 'Exit status: %s\n' "$?"
HTTP request refused: 400. No complete export published.
Exit status: 1
HTTPError bezeichnet diese HTTP-Ablehnung; ein Verbindungsfehler wird getrennt als Connection failed gemeldet. Beides ist kein gültiges Ergebnis ohne Treffer. Die vorbereitete Befehlsschnittstelle fängt die Ausnahme ab, erklärt sie und beendet sich mit einem Status ungleich null. Verwende zur Wiederherstellung wieder eine erlaubte Größe; verändere den Dienst nicht:
python3 catalog.py 'http://127.0.0.1:8080/api/harbor' street landscape 3 page.csv --first-page
Wechsle zu Web 8080, wähle Your exports und danach page.csv. Die Tabelle muss die beiden echten Datensätze von oben zeigen. Wähle Download CSV und öffne die heruntergeladene Datei als Text oder mit einem CSV-Leser. Der Download enthält die gespeicherten Dateibytes; die Seite lädt keine fehlenden Datensätze aus der Quelle nach. Kehre über die Seitennavigation zu Catalog und danach zur Selbstprüfung zu WebIDE zurück. Du hast eine wiederverwendbare Seitenstichprobe erstellt. Das noch vorhandene Fortsetzungs-Token zeigt weiterhin, dass dies kein vollständiger Katalog ist.
Zusammenfassung
Du hast echte HTTP-Anfragen gesendet, die JSON-Seitenhülle von ihren Datensätzen getrennt, exakte Feldwerte ausgewählt und eine UTF-8-CSV mit korrekt gesetzten Anführungszeichen geschrieben. HTTP-Ablehnung, Verbindungsfehler und ein gültiges leeres Ergebnis haben verschiedene Bedeutungen. Die Stichprobe mit zwei Datensätzen ist nützlich; die nächste Einheit folgt den restlichen Cursorn zum vollständigen Export.



