Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

PyScript / Python im Browser

PyScript ermöglicht es, Python-Code direkt im Browser auszuführen – ohne Server-Backend. Es basiert auf Pyodide (CPython kompiliert zu WebAssembly).

1 Was ist PyScript?

PyScript ist ein Framework von Anaconda, das Python im Browser lauffähig macht:

  • Kein Server nötig: Python läuft komplett client-seitig
  • WebAssembly-basiert: Nutzt Pyodide (CPython → WASM)
  • HTML-Integration: Python-Code direkt in HTML einbetten
  • Pakete verfügbar: NumPy, Pandas, Matplotlib, Scikit-learn, etc.

Offizielle Website: https://pyscript.net

2 Erste Schritte

2.1 Minimales Beispiel

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PyScript Demo</title>

    <!-- PyScript einbinden -->
    <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" />
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
</head>
<body>
    <h1>Hello from PyScript!</h1>

    <!-- Python-Code ausführen -->
    <py-script>
        print("Hello, World!")
        print(f"2 + 2 = {2 + 2}")
    </py-script>
</body>
</html>

2.2 Output anzeigen

<body>
    <h1>Calculator</h1>

    <div id="output"></div>

    <py-script>
        from pyscript import display

        result = 10 * 5
        display(f"10 × 5 = {result}", target="output")
    </py-script>
</body>

3 Python-Pakete verwenden

3.1 Pakete deklarieren

<head>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>

    <!-- Pakete konfigurieren -->
    <py-config>
        packages = ["numpy", "pandas", "matplotlib"]
    </py-config>
</head>

<body>
    <py-script>
        import numpy as np
        import pandas as pd

        # NumPy Array
        arr = np.array([1, 2, 3, 4, 5])
        print(f"Mean: {np.mean(arr)}")

        # Pandas DataFrame
        df = pd.DataFrame({
            'A': [1, 2, 3],
            'B': [4, 5, 6]
        })
        display(df)
    </py-script>
</body>

3.2 Verfügbare Pakete

PyScript/Pyodide unterstützt viele populäre Pakete:

Wissenschaftlich:

  • NumPy, SciPy, Pandas
  • Matplotlib, Plotly
  • Scikit-learn
  • SymPy

Web/Utility:

  • Requests (pyodide-http)
  • BeautifulSoup
  • Pillow
  • PyYAML

Vollständige Liste: https://pyodide.org/en/stable/usage/packages-in-pyodide.html

4 DOM-Manipulation

4.1 JavaScript-Interoperabilität

<body>
    <button id="myButton">Click Me!</button>
    <div id="result"></div>

    <py-script>
        from pyscript import document

        def button_click(event):
            result_div = document.querySelector("#result")
            result_div.innerText = "Button was clicked!"

        # Event Listener
        button = document.querySelector("#myButton")
        button.addEventListener("click", button_click)
    </py-script>
</body>

4.2 HTML-Elemente erstellen

<body>
    <div id="container"></div>

    <py-script>
        from pyscript import document

        container = document.querySelector("#container")

        # Neues Element erstellen
        new_div = document.createElement("div")
        new_div.innerText = "Created with Python!"
        new_div.style.color = "blue"

        container.appendChild(new_div)
    </py-script>
</body>

5 Interaktive Anwendungen

5.1 Eingabefelder verarbeiten

<body>
    <input type="text" id="nameInput" placeholder="Enter your name">
    <button id="greetBtn">Greet</button>
    <div id="greeting"></div>

    <py-script>
        from pyscript import document

        def greet(event):
            name_input = document.querySelector("#nameInput")
            greeting_div = document.querySelector("#greeting")

            name = name_input.value
            greeting_div.innerText = f"Hello, {name}!"

        btn = document.querySelector("#greetBtn")
        btn.addEventListener("click", greet)
    </py-script>
</body>

5.2 Formular mit Validierung

<body>
    <form id="myForm">
        <input type="number" id="num1" placeholder="Number 1">
        <input type="number" id="num2" placeholder="Number 2">
        <button type="submit">Calculate</button>
    </form>
    <div id="result"></div>

    <py-script>
        from pyscript import document

        def calculate(event):
            event.preventDefault()

            num1 = float(document.querySelector("#num1").value or 0)
            num2 = float(document.querySelector("#num2").value or 0)

            result = num1 + num2

            result_div = document.querySelector("#result")
            result_div.innerText = f"{num1} + {num2} = {result}"

        form = document.querySelector("#myForm")
        form.addEventListener("submit", calculate)
    </py-script>
</body>

6 Datenvisualisierung

6.1 Matplotlib im Browser

<head>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    <py-config>
        packages = ["matplotlib", "numpy"]
    </py-config>
</head>

<body>
    <h1>Matplotlib Chart</h1>
    <div id="plot"></div>

    <py-script>
        import matplotlib.pyplot as plt
        import numpy as np
        from pyscript import display

        # Daten erstellen
        x = np.linspace(0, 2 * np.pi, 100)
        y = np.sin(x)

        # Plot erstellen
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set_title("Sine Wave")
        ax.set_xlabel("x")
        ax.set_ylabel("sin(x)")

        # Im Browser anzeigen
        display(fig, target="plot")
    </py-script>
</body>

6.2 Interaktive Plots

<body>
    <h1>Interactive Plot</h1>
    <label>Frequency: <input type="range" id="freq" min="1" max="10" value="1"></label>
    <div id="plot"></div>

    <py-script>
        import matplotlib.pyplot as plt
        import numpy as np
        from pyscript import document, display

        def update_plot(event):
            freq = int(document.querySelector("#freq").value)

            x = np.linspace(0, 2 * np.pi, 100)
            y = np.sin(freq * x)

            plt.clf()
            plt.plot(x, y)
            plt.title(f"sin({freq}x)")

            display(plt.gcf(), target="plot", append=False)

        # Initial plot
        update_plot(None)

        # Event Listener
        slider = document.querySelector("#freq")
        slider.addEventListener("input", update_plot)
    </py-script>
</body>

7 Dateien und Storage

7.1 Dateien hochladen

<body>
    <input type="file" id="fileInput" accept=".csv">
    <div id="preview"></div>

    <py-script>
        from pyscript import document, display
        import pandas as pd
        from io import StringIO

        async def handle_file(event):
            file = event.target.files.item(0)

            # Datei lesen
            text = await file.text()

            # Als Pandas DataFrame
            df = pd.read_csv(StringIO(text))

            display(df, target="preview")

        file_input = document.querySelector("#fileInput")
        file_input.addEventListener("change", handle_file)
    </py-script>
</body>

7.2 localStorage verwenden

<body>
    <input type="text" id="dataInput" placeholder="Enter data">
    <button id="saveBtn">Save</button>
    <button id="loadBtn">Load</button>
    <div id="output"></div>

    <py-script>
        from pyscript import document, window

        def save_data(event):
            data = document.querySelector("#dataInput").value
            window.localStorage.setItem("myData", data)
            document.querySelector("#output").innerText = "Saved!"

        def load_data(event):
            data = window.localStorage.getItem("myData")
            document.querySelector("#output").innerText = f"Loaded: {data}"

        document.querySelector("#saveBtn").addEventListener("click", save_data)
        document.querySelector("#loadBtn").addEventListener("click", load_data)
    </py-script>
</body>

8 HTTP-Requests

<body>
    <button id="fetchBtn">Fetch Data</button>
    <div id="data"></div>

    <py-script>
        from pyscript import document
        import json
        from pyodide.http import pyfetch

        async def fetch_data(event):
            response = await pyfetch("https://api.github.com/users/python")
            data = await response.json()

            output = f"Name: {data['name']}\n"
            output += f"Followers: {data['followers']}"

            document.querySelector("#data").innerText = output

        btn = document.querySelector("#fetchBtn")
        btn.addEventListener("click", fetch_data)
    </py-script>
</body>

9 Externe Python-Dateien

9.1 Dateien importieren

<!-- index.html -->
<head>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    <py-config>
        [fetch](fetch.md)
        files = ["utils.py"]
    </py-config>
</head>

<body>
    <py-script>
        from utils import greet, calculate

        print(greet("Alice"))
        print(calculate(10, 5))
    </py-script>
</body>
# utils.py
def greet(name):
    return f"Hello, {name}!"

def calculate(a, b):
    return a + b

10 Vor- und Nachteile

10.1 Vorteile

✅ Kein Server nötig

  • Statisches Hosting (GitHub Pages, Netlify)
  • Keine Backend-Infrastruktur
  • Kostenlos hostbar

✅ Python-Ökosystem

  • NumPy, Pandas, Matplotlib
  • Wissenschaftliche Bibliotheken
  • Bekannte Syntax

✅ Offline-fähig

  • Nach initialem Laden
  • Progressive Web Apps möglich

✅ Sicherheit

  • Browser-Sandbox
  • Kein direkter Dateisystem-Zugriff

10.2 Nachteile

❌ Ladezeit

  • Initial: 5-10 Sekunden (Pyodide + Pakete)
  • ~6 MB Download
  • Langsam auf mobilen Geräten

❌ Performance

  • Langsamer als natives JavaScript
  • WebAssembly-Overhead
  • Nicht für alle Use Cases geeignet

❌ Eingeschränkte Pakete

  • Nicht alle Python-Pakete verfügbar
  • C-Extensions müssen kompiliert sein
  • Keine native System-Calls

❌ Browser-Support

  • Moderne Browser nötig
  • WebAssembly erforderlich
  • IE nicht unterstützt

11 Use Cases

✅ Gut geeignet für:

  • Datenvisualisierung
  • Wissenschaftliche Demos
  • Interaktive Tutorials
  • Prototyping
  • Bildungs-Tools
  • Data Science Dashboards
  • Statische Webseiten mit Python-Logik

❌ Nicht geeignet für:

  • Performance-kritische Apps
  • Große Enterprise-Anwendungen
  • Real-time Anwendungen
  • Mobile-First Apps
  • SEO-kritische Seiten (initial render)

12 Alternativen

12.1 Pyodide (direkt)

PyScript basiert auf Pyodide. Kann auch direkt verwendet werden:

<script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>

<script type="text/javascript">
    async function main() {
        let pyodide = await loadPyodide();

        await pyodide.loadPackage("numpy");

        pyodide.runPython(`
            import numpy as np
            print(np.array([1, 2, 3]))
        `);
    }
    main();
</script>

Vorteile:

  • Mehr Kontrolle
  • Kleinerer Overhead
  • Direkter JavaScript-Zugriff

Nachteile:

  • Mehr Boilerplate
  • Weniger Python-freundlich

12.2 Brython

Browser-Python-Implementierung (kein WebAssembly):

<script src="https://cdn.jsdelivr.net/npm/brython@3/brython.min.js"></script>

<body onload="brython()">
    <script type="text/python">
        from browser import document, alert

        def hello(event):
            alert("Hello from Brython!")

        document["myButton"].bind("click", hello)
    </script>

    <button id="myButton">Click</button>
</body>

Vorteile:

  • Schnellerer Start
  • Leichtgewichtiger

Nachteile:

  • Weniger Pakete
  • Nicht vollständig Python-kompatibel
  • Langsamer bei Berechnungen

12.3 Skulpt

Weitere Browser-Python-Alternative:

<script src="http://skulpt.org/js/skulpt.min.js"></script>
<script src="http://skulpt.org/js/skulpt-stdlib.js"></script>

<script>
    function runit() {
        Sk.configure({output: console.log});
        Sk.importMainWithBody("<stdin>", false, `
print("Hello from Skulpt!")
        `);
    }
</script>

13 Vergleich

FrameworkBasisPaketePerformanceLadezeitPython-Version
PyScriptPyodide/WASM✅✅ Viele⚠️ Mittel⏱️ Lang3.11
PyodideWASM✅✅ Viele⚠️ Mittel⏱️ Lang3.11
BrythonJS⚠️ Wenige✅ Schneller✅ Kurz3.10
SkulptJS❌ Minimal✅ Schneller✅ Kurz2.x

14 Praktisches Beispiel: Todo-App

<!DOCTYPE html>
<html>
<head>
    <script defer src="https://pyscript.net/latest/pyscript.js"></script>
    <style>
        .todo-item { padding: 10px; margin: 5px; border: 1px solid #ccc; }
        .done { text-decoration: line-through; opacity: 0.5; }
    </style>
</head>
<body>
    <h1>PyScript Todo App</h1>

    <input type="text" id="todoInput" placeholder="New todo...">
    <button id="addBtn">Add</button>

    <div id="todoList"></div>

    <py-script>
        from pyscript import document

        todos = []

        def render_todos():
            todo_list = document.querySelector("#todoList")
            todo_list.innerHTML = ""

            for i, todo in enumerate(todos):
                item = document.createElement("div")
                item.className = "todo-item"
                if todo['done']:
                    item.classList.add("done")

                item.innerText = todo['text']
                item.addEventListener("click", lambda e, idx=i: toggle_todo(idx))

                todo_list.appendChild(item)

        def add_todo(event):
            input_elem = document.querySelector("#todoInput")
            text = input_elem.value.strip()

            if text:
                todos.append({'text': text, 'done': False})
                input_elem.value = ""
                render_todos()

        def toggle_todo(index):
            todos[index]['done'] = not todos[index]['done']
            render_todos()

        # Event Listeners
        add_btn = document.querySelector("#addBtn")
        add_btn.addEventListener("click", add_todo)

        input_elem = document.querySelector("#todoInput")
        input_elem.addEventListener("keypress",
            lambda e: add_todo(e) if e.key == "Enter" else None)
    </py-script>
</body>
</html>

15 Deployment

15.1 GitHub Pages

# 1. Erstelle Repository
git init
git add .
git commit -m "Initial commit"

# 2. Push zu GitHub
git remote add origin https://github.com/username/pyscript-app.git
git push -u origin main

# 3. GitHub Pages aktivieren (Settings → Pages)
# Source: main branch

# 4. App verfügbar unter:
# https://username.github.io/pyscript-app/

15.2 Netlify

# 1. netlify.toml erstellen
[build]
  publish = "."

# 2. Deploy
netlify deploy --prod

16 Best Practices

✅ DO:

  • Loading-Indicator während Pyodide lädt
  • Pakete nur wenn nötig laden
  • Code in externe .py-Dateien auslagern
  • Caching nutzen (Service Worker)
  • Progressive Enhancement (JS Fallback)

❌ DON’T:

  • Zu viele Pakete auf einmal laden
  • Große Berechnungen synchron
  • PyScript für SEO-kritische Seiten
  • Sensitive Daten client-seitig verarbeiten
  • Alte Browser erwarten

17 Zukunft von PyScript

Entwicklungen:

  • Performance-Verbesserungen
  • Kleinere Bundle-Größen
  • Mehr Pakete
  • Bessere Tooling
  • Framework-Integration (React, Vue)

Versionen:

  • 2023: PyScript 1.0 (stabil)
  • 2024: Verbesserte Performance, kleinere Bundles
  • Zukunft: Native WebAssembly-Integration

18 Zusammenfassung

AspektBewertung
Einstieg✅ Einfach (HTML + Python)
Performance⚠️ Langsamer als JS, aber akzeptabel
Use CasesVisualisierung, Demos, Prototyping
Produktion⚠️ Mit Vorsicht (Ladezeit beachten)
Zukunft✅ Aktive Entwicklung

Kernprinzip: PyScript ermöglicht Python im Browser für Visualisierung und interaktive Demos. Ideal für statische Seiten mit wissenschaftlichen Inhalten, aber nicht als vollständiger JavaScript-Ersatz. Beachte Ladezeiten und Browser-Kompatibilität.

Ressourcen:

  • Offizielle Docs: https://docs.pyscript.net/
  • Examples: https://pyscript.net/examples/
  • Pyodide: https://pyodide.org/
  • Dicord: https://discord.gg/pyscript