Molekülstrukturen mit Python und RDKit zeichnen

Für die Filament-Übersicht brauchte ich Strukturbilder der Monomere -- Milchsäure, Styrol, Caprolactam und Co. Statt Bilder aus dem Internet zu klauen (Lizenzstress, unterschiedlicher Stil), einfach selbst generiert: mit Python und RDKit.

Was ist RDKit?

RDKit ist eine Open-Source-Cheminformatik-Bibliothek, die in Pharma und Chemie weit verbreitet ist. Kann Moleküle parsen, analysieren, vergleichen und zeichnen. Für uns interessant: aus einer Textbeschreibung (SMILES) eine saubere 2D-Strukturformel als SVG erzeugen.

Installation

pip install rdkit

Das Paket ist ~30 MB groß und bringt alles mit.

SMILES -- Moleküle als Text

SMILES (Simplified Molecular Input Line Entry System) beschreibt Moleküle als kurze Textstrings. Die Regeln sind schnell gelernt:

Beispiele der Filament-Monomere:

Monomer SMILES
Milchsäure C[C@@H](O)C(=O)O
Lactid C[C@@H]1OC(=O)[C@H](C)OC1=O
Styrol C=Cc1ccccc1
Acrylnitril C=CC#N
1,3-Butadien C=CC=C
ε-Caprolactam O=C1CCCCCN1
Bisphenol A Oc1ccc(C(C)(C)c2ccc(O)cc2)cc1

SMILES für gängige Moleküle findest du auf PubChem -- einfach den Namen suchen und unter "Canonical SMILES" nachschauen.

SVG erzeugen -- Minimalbeispiel

from rdkit import Chem
from rdkit.Chem import AllChem, Draw
from rdkit.Chem.Draw import rdMolDraw2D

# SMILES parsen
mol = Chem.MolFromSmiles('C=Cc1ccccc1')  # Styrol
AllChem.Compute2DCoords(mol)              # 2D-Layout berechnen

# SVG rendern
drawer = rdMolDraw2D.MolDraw2DSVG(400, 300)
drawer.DrawMolecule(mol)
drawer.FinishDrawing()
svg = drawer.GetDrawingText()

# Speichern
with open('styrol.svg', 'w') as f:
    f.write(svg)

Das erzeugt eine saubere SVG-Datei mit der Skelettformel von Styrol -- skaliert auf jede Größe, scharf auf jedem Display. Fertig.

Mehrere Moleküle auf einmal

Für die Filament-Seite hab ich ein kleines Script geschrieben, das alle Monomere in einem Rutsch generiert:

from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem.Draw import rdMolDraw2D
from pathlib import Path

output_dir = Path('img/filamente')
output_dir.mkdir(parents=True, exist_ok=True)

monomers = {
    'milchsaeure':      ('C[C@@H](O)C(=O)O',         'Milchsäure'),
    'lactid':           ('C[C@@H]1OC(=O)[C@H](C)OC1=O', 'Lactid'),
    'styrol':           ('C=Cc1ccccc1',                'Styrol'),
    'acrylnitril':      ('C=CC#N',                     'Acrylnitril'),
    'butadien':         ('C=CC=C',                     '1,3-Butadien'),
    'caprolactam':      ('O=C1CCCCCN1',                'ε-Caprolactam'),
    'terephthalsaeure': ('OC(=O)c1ccc(C(=O)O)cc1',    'Terephthalsäure'),
    'ethylenglykol':    ('OCCO',                       'Ethylenglykol'),
    'hexamethylendiamin': ('NCCCCCCN',                 'Hexamethylendiamin'),
    'adipinsaeure':     ('OC(=O)CCCCC(=O)O',           'Adipinsäure'),
    'bisphenol_a':      ('Oc1ccc(C(C)(C)c2ccc(O)cc2)cc1', 'Bisphenol A'),
    'mdi':              ('O=C=Nc1ccc(Cc2ccc(N=C=O)cc2)cc1', 'MDI'),
    'butylacrylat':     ('CCCCOC(=O)C=C',              'Butylacrylat'),
}

for filename, (smiles, title) in monomers.items():
    mol = Chem.MolFromSmiles(smiles)
    AllChem.Compute2DCoords(mol)

    drawer = rdMolDraw2D.MolDraw2DSVG(400, 300)
    opts = drawer.drawOptions()
    opts.padding = 0.15
    opts.bondLineWidth = 2.0
    drawer.DrawMolecule(mol)
    drawer.FinishDrawing()

    (output_dir / f'{filename}.svg').write_text(drawer.GetDrawingText())
    print(f'{filename}.svg -- {title}')

Darstellung anpassen

Der Drawer hat jede Menge Stellschrauben über drawOptions():

opts = drawer.drawOptions()
opts.bondLineWidth = 2.5          # dickere Linien
opts.padding = 0.2                # mehr Rand
opts.addAtomIndices = True        # Atom-Nummern anzeigen (für Debugging)
opts.addStereoAnnotation = True   # Stereochemie-Labels (R/S)
opts.backgroundColour = (1,1,1,0) # transparenter Hintergrund

Breite und Höhe werden beim Erstellen des Drawers festgelegt: MolDraw2DSVG(breite, höhe).

Warum SVG?

RDKit kann auch PNG (MolDraw2DCairo), aber für Webseiten ist SVG die bessere Wahl.

Weiterführendes


Erstellt: 02.03.2026