Forum: PC-Programmierung Python HTML Tabelle Text mit Bild ersetzen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Chandler B. (chandler)


Lesenswert?

Guten morgen,
ich habe in Python eine HTML aus einer liste von dicts erstellt.
1
    data = pandas.DataFrame(myDict)
2
    with open(html_file, "w") as file:
3
        file.write("<!DOCTYPE html>\n")
4
        file.write("<html>\n")
5
        file.write(html_head)
6
        file.write("<body>\n")
7
        file.write("<input type=\"text\" id=\"myInput\" onkeyup=\"myFunction()\" placeholder=\"Search for names..\" title=\"Type in a name\">\n")
8
        html_tableData = data.to_html(file, border='0', classes='table table-stripped', table_id='myTable')
9
        file.write("\n</body>\n")
10
        file.write("\n")
11
        file.write("</html>\n")
12
        file.close()
wobei myDict die Daten meiner Tabelle enthält.
Ein element davon ist ein Link zu einem Bild. Dieses Bild habe ich auch 
schon lokal runtergeladen. Jeder Link/Bild hat auch eine eindeutige ID, 
womit ich jedes Bild genau dem entsprechendem Element hinterlegen kann.

Aber wie bekomme ich es hin, dass mir das Bild angzeigt wird?
Momentan wird einfach nur der Hyperlink (wo ich es rungergeladen habe) 
angezeigt.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Chandler B. schrieb:
> html_tableData =

Ich sehe nicht wo der Inhalt dieser Variable ausgegeben wird.

Brauchst du Hilfe zu HTML oder zu Python?

Was steht denn in deiner dict drin und wie soll das generierte HTML 
Dokument konkret aussehen? Und was willst du "mit Bild ersetzen"? Woas 
hat das <input> Element mit deiner Frage zu tun?

> Momentan wird einfach nur der Hyperlink (wo ich es rungergeladen habe) 
angezeigt.

Wo? Was meinst du damit? Zeige es mal!

: Bearbeitet durch User
von Chandler B. (chandler)


Angehängte Dateien:

Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Brauchst du Hilfe zu HTML oder zu Python?

Ich denke eher zu HTML.

Ich habe mal einen Ausschnitt angehangen. Wo jetzt der Link zum Bild 
ist, soll das Bild selber hin.

Sherlock 🕵🏽‍♂️ schrieb:
> Chandler B. schrieb:
>> html_tableData =
>
> Ich sehe nicht wo der Inhalt dieser Variable ausgegeben wird.

Dieser wird auch nicht ausgegeben. Es genügt, wenn ich nur
1
        data.to_html(file, border='0', classes='table table-stripped', table_id='myTable')
schreibe

: Bearbeitet durch User
von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Bilder bettet man so ein:

<img src="https://...">;

Du musst data entsprechend vorbereiten, oder den generierten HTML Code 
nachträglich ändern, oder to_html() durch eine eigene Methode ersetzen, 
die das erledigt.

: Bearbeitet durch User
von Chandler B. (chandler)


Angehängte Dateien:

Lesenswert?

Ich habe eine neue Funktion hinzugefügt, welche mir die Bilder im 
Nachhinein ersetzen soll. Dazu möchte ich in der HTML einfach die 
entsprechenden Stellen anpassen. Anstelle des Links steht schon nur die 
ID dort. Die Bilder haben die selbe ID
1
def replaceImages():
2
    with open(html_file, "r") as file:
3
        soup = BeautifulSoup(file, 'html.parser')
4
        tables = soup.findChildren('tbody')
5
        
6
        rows = tables[0].findChildren(['tr'])
7
        for row in rows:
8
            cells = row.findChildren('td')
9
            content = cells[7].string
10
            new_string = "<img src=\"images/" + cells[7].string + ".jpg\" width=\"180\" heigth=\"180\">"
11
            content.replace_with(new_string)
12
        file.close()
13
14
    with open(html_file, "w", encoding='utf-8') as file:
15
        file.write(str(soup))
16
        file.close()

Das Problem ist, dass das "<" und ">" zeichen nicht richtig aufgelöst 
werden. In der HTML steht dann "&lt;" bzw. "&gt;" .
Wenn ich die html öffne, steht dort schon "<" bzw. ">", aber die Bilder 
werden nicht angezeigt.(siehe anhang)
Wenn ich es händisch ersetze, werden die Bilder angezeigt

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


Lesenswert?

Jetzt hast du dir das Leben durch Einsatz einer weiteren Bibliothek (die 
du nicht kennst) schwer gemacht.

Offenbar ersetzt die Bibliothek Sonderzeichen wie < und > in Strings 
durch ihre Escape Sequenzen. Du musst also die Strings der Zellen durch 
Tags ersetzen. Vermutlich ungefähr so:
1
from bs4 import BeautifulSoup 
2
html = "<html><body><table><tbody><tr><td>A</td><td>http://path/to/image</tr></tbody></table></body></html>"
3
soup = BeautifulSoup(html,"html.parser")
4
tables = soup.findChildren('tbody')
5
rows = tables[0].findChildren(['tr'])
6
for row in rows:
7
    cells = row.findChildren('td')
8
    
9
    new_tag = soup.new_tag('img', src=cells[1].text+'.jpg')
10
    cells[1].string.replace_with(new_tag)
11
12
print(str(soup))

Ich würde die HTML Datei nicht mit BeautifulSoup verändern, sondern 
direkt richtig erstellen.

: Bearbeitet durch User
von Lenny D. (le-do)


Lesenswert?

Wie Sherlock sagt, würde das auch direkt beim Erstellen lösen. In beiden 
Fällen/Libraries muss man sich mit dem "Escapen" der HTML-Zeichen 
beschäftigen.

So müsste es deinen Anforderungen genügen:
1
import html # Python standard library ab Python >=3.2
2
import pandas as pd
3
4
myDict = {'title': ['Monkey', 'Banana on Sale <<SALE>>'],
5
          'img_src': [
6
              'https://upload.wikimedia.org/wikipedia/commons/4/43/Bonnet_macaque_%28Macaca_radiata%29_Photograph_By_Shantanu_Kuveskar.jpg',
7
              'https://upload.wikimedia.org/wikipedia/commons/thumb/3/31/Cavendish_banana_from_Maracaibo.jpg/800px-Cavendish_banana_from_Maracaibo.jpg',
8
              ]}
9
10
data = pd.DataFrame(myDict)
11
12
data['image'] = '<img height=100px src="' + data.img_src + '"/>'
13
data.drop(columns=['img_src'], inplace=True) # Spalte mit Bild-Link loswerden
14
data['title'] = data['title'].apply(lambda s: html.escape(s))
15
16
html_file = 'output.html'
17
html_head = ''
18
19
with open(html_file, "w") as file:
20
    file.write("<!DOCTYPE html>\n")
21
    file.write("<html>\n")
22
    file.write(html_head)
23
    file.write("<body>\n")
24
    file.write("<input type=\"text\" id=\"myInput\" onkeyup=\"myFunction()\" placeholder=\"Search for names..\" title=\"Type in a name\">\n")
25
    html_tableData = data.to_html(file, border='0', classes='table table-stripped', table_id='myTable', escape=False)
26
    file.write("\n</body>\n")
27
    file.write("\n")
28
    file.write("</html>\n")
29
    #file.close() # durch with: abgedeckt

Bei Pandas kann man das mit "to_html(..., escape=False)" umgehen. Leider 
geht das nicht pro Spalte, daher würde ich die normalen Textspalten 
nochmal manuell escapen, sonst kann es zu komischen Seiteneffekten 
kommen.

Gruß, Lenny

von Εrnst B. (ernst)


Lesenswert?

Chandler B. schrieb:
> Dieses Bild habe ich auch
> schon lokal runtergeladen

Willst du dann das lokal gespeicherte Bild in's HTML einsetzen, anstatt 
einen Link zum Webserver (egal ob eigener oder der Ursprungs-Webserver)?

-> geht per data-uris:
https://developer.mozilla.org/en-US/docs/Web/URI/Schemes/data

sind mit python auch leicht erstellt
1
with open("test.jpg", "rb") as f:
2
    uri = f"data:image/jpeg;base64,{base64.b64encode(f.read()).decode()}"
3
...

Das erzeugte HTML wird natürlich viel größer, funktioniert dann aber 
stand-alone ohne zusätzliche Bilddateien oder externe Verweise...

von Chandler B. (chandler)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> Ich würde die HTML Datei nicht mit BeautifulSoup verändern, sondern
> direkt richtig erstellen.

Das habe ich auch versucht, aber es kam immer wieder das selbe heraus.

Lenny D. schrieb:
> html_tableData = data.to_html(file, border='0', classes='table
> table-stripped', table_id='myTable', escape=False)

mit escape=False hat es nun letztendlich funktioniert.

Ohne BeautifulSoup.
1
def writeHtml(myDict):
2
3
    for liste in myDict:
4
        content = liste['offerId']
5
        newContent = {'offerId' : "<img src=\"images/" + content + ".jpg\" width=\"180\" heigth=\"180\">"}
6
        liste.update(newContent)
7
8
    datas = pd.DataFrame(myDict)
9
... ... ...

danke für eure tips.

von Weingut P. (weinbauer)


Lesenswert?

PS: alle die selbe ID ist nicht gut, darf eigentlich nicht sein. Dann 
besser keine ID setzen.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Anstatt über einen (externen) Link <img src=yxz.com ... > kann man die 
Bilddaten auch als Base64-codierten Text direkt in die HTML-Datei 
einbetten.
Guckst du hier:

https://sentry.io/answers/how-do-i-display-a-base64-image-in-html/

Hat den Vorteil, dass das Anzeigen auch offline sicher funzt und kein 
zweiter Browswer-Link aufgemnacht werden muss, egal ob als File oder 
WEB-URL...

<img src="data:image/png;base64, 
iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXD 
IBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="  />

Wird bei gößeren Bildern unpraktisch, ist klar, aber z.B. für Icons, 
Logos und einfache (und damit gut komprimierbare) Diagramme klappt das 
recht gut.

von Sheeva P. (sheevaplug)


Lesenswert?

Chandler B. schrieb:
> ich habe in Python eine HTML aus einer liste von dicts erstellt.
>
1
>     data = pandas.DataFrame(myDict)
2
>     with open(html_file, "w") as file:
3
>         # [...]
4
>         file.close()
5
>

Der ganze Trick an einem Context Manager ("with") ist, daß er seine 
lokalen Ressourcen vollautomatisch freigibt (hier: Deine Datei 
automatisch schließt), sobald sein Kontext (Scope) verlassen wird. Das 
"file.close()" ist deswegen  überflüssig, wenn Du die Datei in einem 
"with"-Kontext öffnest.

> Aber wie bekomme ich es hin, dass mir das Bild angzeigt wird?
> Momentan wird einfach nur der Hyperlink (wo ich es rungergeladen habe)
> angezeigt.

Dann ersetz' Deinen Bild-URL durch entsprechenden HTML-Code:
1
#!/usr/bin/env python
2
3
DATA = [
4
    {
5
        "shop": "Kaufland",
6
        "title": "Span. Bio-Kaki",
7
        "price": 1.49,
8
        "from": "2024-11-07",
9
        "unit": "2-Stk-Packg.",
10
        "picture": "https://kaufland.media.schwarz/is/image/schwarz/00374264FR-2",
11
        "category": float("nan"),
12
        "brand": float("nan"),
13
    },
14
    {
15
        "shop": "Kaufland",
16
        "title": "Dän. Snackmöhren",
17
        "price": 1.49,
18
        "from": "2024-11-07",
19
        "unit": "200-g-Packg.",
20
        "picture": "https://kaufland.media.schwarz/is/image/schwarz/00964299",
21
        "category": "Obst, Gemüse, Pflanzen",
22
        "brand": "",
23
    }
24
]
25
26
import sys
27
import pandas as pd
28
29
if __name__ == '__main__':
30
    df = pd.DataFrame(DATA)
31
    print(df)
32
    df.picture = df.picture.apply(lambda x: '<img src="%s">'%(x))
33
    print(df)
34
    df.to_html(sys.stdout)

Zusätzlich möchte ich anmerken, daß es für Python ein paar 
leistungsfähige Template-Engines gibt. Besonders beliebt ist Jinja2 [1], 
das zum Beispiel im Flask-Projekt [2] verwendet wird und dem Templating 
von Django [2] ähnelt. So eine Template-Engine hat Platzhalter, die dann 
beim Rendern mit den Inhalten (Werten) Deiner übergebenen Variablen 
befüllt werden.

Ein Jinja2-Template "tabelle.html" könnte also zum Beispiel so aussehen:
1
<!doctype html>
2
<html lang="de">
3
  <head>
4
    <title>Sonderangebote</title>
5
  </head>
6
  <body>
7
    <h1>Guckstu:</h1>
8
    {{ pandas_tabelle | safe }}
9
  </body>
10
</html>

Dein Dataframe würde dann als Variable "pandas_tabelle" übergeben, und 
die Template Engine würde Deinen df.to_html() dann dort ausgeben. Der 
Filter auf "| safe" würde Jinja2 sagen, daß Du sicheren HTML-Code 
angibst, < und > also nicht durch &lt; und &gt; ersetzt werden sollen -- 
ein Sicherheitsfeature. So eine Template-Engine ermöglicht es Dir Deine 
Codes sauber zu trennen, was der Übersichtlichkeit und der Separation of 
Concerns [6] hilft.

Wenn Du Fragen dazu hast, von denen Du denkst, daß sie für die 
Allgemeinheit des Forums eher nicht so interessant sind, helfe ich auch 
gerne per PM. Viel Spaß und Erfolg bei Deinem Projekt! :-)


[1] https://jinja.palletsprojects.com/en/stable/
[2] https://flask.palletsprojects.com/en/stable/
[3] https://www.djangoproject.com/
[4] https://jinja.palletsprojects.com/en/stable/templates/
[5] https://realpython.com/primer-on-jinja-templating/
[6] https://en.wikipedia.org/wiki/Separation_of_concerns

von Sheeva P. (sheevaplug)


Lesenswert?

Frank E. schrieb:
> Anstatt über einen (externen) Link <img src=yxz.com ... > kann man die
> Bilddaten auch als Base64-codierten Text direkt in die HTML-Datei
> einbetten.

Das hatte Ernst schon vorgeschlagen... Ich habe dabei über tradierte 
Bräuche wie Streckbank und Hexenverbrennung nachgedacht. :-/

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Sheeva P. schrieb:
> Frank E. schrieb:
>> Anstatt über einen (externen) Link <img src=yxz.com ... > kann man die
>> Bilddaten auch als Base64-codierten Text direkt in die HTML-Datei
>> einbetten.
>
> Das hatte Ernst schon vorgeschlagen... Ich habe dabei über tradierte
> Bräuche wie Streckbank und Hexenverbrennung nachgedacht. :-/

Ernsthaft: Warum? Bitte mal argumentieren

Wenn man auf diese Weise z.B. Exporte von Oszilloskopen oder sonstiger 
Analyse-Software macht, hat man tatsächlich alles in einer einzigen 
Datei und es genügt ein HTTP-Stream bzw. File. Oder in nicht sehr 
potenter Server-Umgebung wie Mikrocontroller als Webserver.

Für Fullscreen- bzw. HighEnd-Fototapete hatte ich es ja bereits 
eingeschränkt.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Εrnst B. schrieb:

> Das erzeugte HTML wird natürlich viel größer, funktioniert dann aber
> stand-alone ohne zusätzliche Bilddateien oder externe Verweise...

Naja, "viel" ist relativ: Faktor ca. 1,4.

von Εrnst B. (ernst)


Lesenswert?

Frank E. schrieb:
> Naja, "viel" ist relativ: Faktor ca. 1,4.

Mit Transfer-Encoding: gzip sogar weniger.
Allerdings hat der Webbrowser dann weniger Möglichkeit zum Cachen.

Der TE will wohl eine statische HTML-Datei auf die Festplatte schreiben, 
von einem Webserver war (seitens TE) nie die Rede, da macht das wenig 
Unterschied.

Muss er wissen, ob für seine Anwendung eine All-in-One HTML-Datei oder 
eine Datei mit einem Ordner Bilder nebenan oder eine Nicht-Offlinefähige 
Datei mit Bild-Verweisen zu den Ursprungswebservern besser passt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.