Python HTML Tabelle Text mit Bild ersetzen

OP #7771593
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.

Gast #7771595
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!

OP #7771605
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

Angehängte Dateien:
Gast #7771608
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.

OP #7771654
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 "<" bzw. ">" . 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

Angehängte Dateien:
Gast #7771727
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.

#7771739
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

#7771771
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...

OP #7771776
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.

(Firma: Q3) #7771801
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/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" />

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.

#7771829
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 < und > 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

(Firma: Q3) #7772103
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.

#7772144
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren