Firefox headless HTML to image

Sziasztok!

Adott egy szerver, PHP előállít HTML állományokat, van hozzá külön CSS, szépen néz ki minden böngészőben. A feladat az, hogy a HTML DOM bizonyos DIV-jeiből képet készítsek, amely képeket a PHP program majd előnézeti ábrához felhasználna.

Próbáltam a Phantomjs-t, de értékelhetetlenül csúnya végeredményt adott. Nézem most a Firefox-ot, elvileg tud screenhot-ot készíteni URL alapján, de nem hajlandó ugyanezt megtenni ha file:///home/xyz/test.html-lel próbálkozom.

Teljesen tanácstalan vagyok, mi legyen a megoldás az előnézeti képek generálására a szerver oldalon.

Barátkozom a Google-val, de nem a barátom. Nem hiszem, hogy csak nekem lenne szükségem ilyen dologra.

Nektek lenne ötletetek?

 

Cz

Hozzászólások

Ne selenium ide-t nézd! Az semmire nem való...

Rajk össze egy selenium grid-et.

https://github.com/eficode/Docker-Selenium-Example/blob/master/docker-compose.yml

codeception-on keresztül gyerekjáték elérni. Sőt itt még mozizni is tudsz egy vnc-n keresztül. Én sokszor használom népvakítása. 

https://codeception.com/docs/modules/WebDriver

Majd itt használd a makescrenshot-ot

A végén még a node.js is lehet befutó, de jelenleg ismerkedem a témával. Két nappal ezelőttig azt se tudtam, hogy létezik a webkit2gtk és csomó remek dolgot lehet vele művelni, leszámítva épp azt, ami a feladat lenne :-) Keresgélek, csapongok, próbálom megérteni a programok működését, igyekszem felderíteni a dokumentációból a lényeget.

> Nézem most a Firefox-ot, elvileg tud screenhot-ot készíteni URL alapján, de nem hajlandó ugyanezt megtenni ha file:///home/xyz/test.html-lel próbálkozom.

De localban miért nem egy a szerveredhez hasonló környezetben dolgozol, vagy legalább egy webszerveren? Aztán https://example.com.tld localban.

Szerk.:
Most esett le, hogy van ott PHP, stb. Miért a file:///home/xyz/test.html az útvonal?

Biztos, hogy nem én gonoszoltam le!

Csak próbálkozom, de van észszerűség abban, hogy http://-n keresztül érjem el a HTML állományokat. Jé. és tényleg :-)

No mármost, az a következő bajom, hogy a szerveren (Ubuntu 18.04) nincs grafikus környezet. Ha felteszem a firefox-ot, akkor magával húzza az ablakkezelőt is? Hogy kéne telepíteni a Firefox-ot, hogy minél kevesebb egyéb sallangot kelljen feltelepíteni?

Támadt némi hibaüzenetem:

Python executable: /usr/bin/python3
Python version:    3.6.9 (default, Nov  7 2019, 10:44:02)  [GCC 8.3.0]
Python venv:       not in venv
Python path:      
	/home/xyz
	/usr/lib/python36.zip
	/usr/lib/python3.6
	/usr/lib/python3.6/lib-dynload
	/usr/local/lib/python3.6/dist-packages
	/usr/lib/python3/dist-packages
Module gi path:    /usr/lib/python3/dist-packages/gi/__init__.py
Unable to init server: Could not connect: Connection refused
Unable to init server: Could not connect: Connection refused
Unable to init server: Could not connect: Connection refused
EGLDisplay Initialization failed: EGL_NOT_INITIALIZED
WaylandCompositor requires eglCreateImage and eglDestroyImage.
Nested Wayland compositor could not initialize EGL
Unable to init server: Could not connect: Connection refused

(htmlscr.py:8768): Gtk-WARNING **: 06:52:04.540: cannot open display: 

Hogyan lehet ezt meggyógyítani?

Ez jó tanács volt, egy kicsit tovább tudtam jutni:

 

Python executable: /usr/bin/python3
Python version:    3.6.9 (default, Nov  7 2019, 10:44:02)  [GCC 8.3.0]
Python venv:       not in venv
Python path:      
	/home/xyz
	/usr/lib/python36.zip
	/usr/lib/python3.6
	/usr/lib/python3.6/lib-dynload
	/usr/local/lib/python3.6/dist-packages
	/usr/lib/python3/dist-packages
Module gi path:    /usr/lib/python3/dist-packages/gi/__init__.py
Load-state: <enum WEBKIT_LOAD_STARTED of type WebKit2.LoadEvent>
Load-state: <enum WEBKIT_LOAD_REDIRECTED of type WebKit2.LoadEvent>
Load-state: <enum WEBKIT_LOAD_REDIRECTED of type WebKit2.LoadEvent>
Load-state: <enum WEBKIT_LOAD_COMMITTED of type WebKit2.LoadEvent>
failed to create drawable
Load-state: <enum WEBKIT_LOAD_FINISHED of type WebKit2.LoadEvent>
Taking snapshot ...
Snapshot ready
Traceback (most recent call last):
  File "htmlscr.py", line 72, in on_snapshot_finish
    cairo_surf = web_view.get_snapshot_finish(result)
TypeError: Couldn't find foreign struct converter for 'cairo.Surface'

^CGdk-Message: 15:04:08.880: htmlscr.py: Fatal IO error 11 (Resource temporarily unavailable) on X server :99.

Úgy néz ki, ezzel van most baja:

 

        cairo_surf = web_view.get_snapshot_finish(result)

Telepítettem egy python3-cairo csomagot, de semmi változás a kimenetben.

Mester, hogyan tovább? :-)

Akkor meg is válaszolnám a kérdésemet:

  • apt-get install python3-gi
  • apt-get install gir1.2-webkit2-4.0
  • apt-get install python3.6-gi
  • apt-get install xvfb
  • apt-get install python3-cairo
  • apt-get install python3-gi-cairo

Ezeket kellett telepíteni, majd így futtatni:

xvfb-run python3 htmlscr.py http://bme.hu bme.png 1200

Most már csak ez zavar, igaz, ne legyek telhetetlen :-)  :

failed to create drawable

Cz

Endi123 megoldását használom, de belefutottam egy jelenségbe: adott oldalon szerepel egy kép, amely nem közvetlen erőforrás hivatkozás, hanem egy képgeneráló URL, valami ilyesmi:

 <img src="http://XYZ.hu/artefact/file/download.php?file=8&amp;view=6&amp;time=1582494709" alt="Harvest-Bounty2-1024x1024.jpg" itemprop="contentURL" data-target="#configureblock" data-artefactid="8" data-blockid="27" title="Harvest-Bounty2-1024x1024.jpg">

A HTML állomány elmentett képén pedig csak az alt érték jelenik meg, nem tölti le a képet.

Mester, segíts :-)

A webkit másképp kezeli a képnek látszó hivatkozást és az egyéb src hivatkozást? Néhány ötlet:

  • Ha te írod a képgeneráló php kódot, akkor meg tudod változtatni úgy, hogy az src hivatkozást a böngésző kép kiterjesztésűnek lássa, a szerver pedig előállítja a kép neve alapján.
  • Az alt attr.-ról azt írják: "Omitting alt altogether indicates that the image is a key part of the content and no textual equivalent is available. Setting this attribute to an empty string (alt="") indicates that this image is not a key part of the content". Mindez nekem azt sejteti, hogy ha kihagyod az alt-ot, akkor a kép "key part"-á válik, és ez talán kényszeríti a böngészőt a betöltésre. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Img

Ha ezek nem jelentenek megoldást, akkor először is leellenőrízném hogy a webkit amúgy betölti az src képet ha hagyunk neki időt.

  • Nem-szerveren megteheted így: a python scriptben a "headless = False" legyen, és kommenteld ki a "self.close()" sort.
  • Szerveren például úgy tudod megtenni, hogy a python scriptben a WebKit2.LoadEvent.FINISHED után még vársz egy kicsit. Változtatások: GLib hozzáadása az importhoz:
    from gi.repository import Gtk, Gdk, GLib, WebKit2

    és lejjebb változik az on_load_changed(), és új az on_timeout() metódus:

        def on_load_changed(self, web_view, load_state):
            print("Load-state:", load_state)
            if load_state == WebKit2.LoadEvent.FINISHED and not hasattr(self, "timeout_count"):
                self.timeout_count = 4
                Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 1000, self.on_timeout)
        
        def on_timeout(self, *args):
            self.timeout_count -= 1
            if self.timeout_count > 0:
                print("Snapshot in ", self.timeout_count)
                return GLib.SOURCE_CONTINUE
            else:
                print("Taking snapshot ...")
                self.webview.get_snapshot(WebKit2.SnapshotRegion.FULL_DOCUMENT, snapshot_opts, cancellable=None, callback=self.on_snapshot_finish)
                return GLib.SOURCE_REMOVE

    ezek után a kimetet:

    Snapshot in 3
    Snapshot in 2
    Snapshot in 1
    Taking snapshot ...
    Snapshot ready

Ha az eddigiek nem jelentenek valamiféle megoldást, és ahogy írtad a webview settings beállításaival sem tudsz eredményt elérni, akkor javascript-ben lehetne a minden kép betöltődése eseményt elkapni (window.onload ?), és azt az eseményt pythonba továbbítani, de erről az irányról idő hiányában talán később.

Egyre kifinomultabb a programod, de sajnos még mindig nem tölti le a PHP által generált képet.

 

Python executable: /usr/bin/python3
Python version:    3.6.9 (default, Nov  7 2019, 10:44:02)  [GCC 8.3.0]
Python venv:       not in venv
Python path:      
	/var/www/html/mahara/HUNGLE
	/usr/lib/python36.zip
	/usr/lib/python3.6
	/usr/lib/python3.6/lib-dynload
	/usr/local/lib/python3.6/dist-packages
	/usr/lib/python3/dist-packages
Module gi path:    /usr/lib/python3/dist-packages/gi/__init__.py

Load-state: <enum WEBKIT_LOAD_STARTED of type WebKit2.LoadEvent> 

								(resources loading: 0, webview loading: True)
Resource load STARTED : http://xyz.hu/HUNGLE/27.html
								(resources loading: 1, webview loading: True)

Load-state: <enum WEBKIT_LOAD_COMMITTED of type WebKit2.LoadEvent> 

								(resources loading: 1, webview loading: True)
Resource load STARTED : http://xyz.hu/HUNGLE/style.ccs
								(resources loading: 2, webview loading: True)
Resource load STARTED : http://xyz.hu/artefact/file/download.php?file=8&view=6&time=1582701544
								(resources loading: 3, webview loading: True)
Resource load FINISHED: http://xyz.hu/HUNGLE/style.ccs
								(resources loading: 2, webview loading: True)
Resource load FINISHED: http://xyz.hu/HUNGLE/27.html
								(resources loading: 1, webview loading: True)
Resource load FINISHED: http://xyz.hu/artefact/file/download.php?file=8&view=6&time=1582701544&login
								(resources loading: 0, webview loading: True)

Load-state: <enum WEBKIT_LOAD_FINISHED of type WebKit2.LoadEvent> 

								(resources loading: 0, webview loading: False)
Resources loaded total: 3
No load activity for 500 ms, taking snapshot ...
Snapshot ready
Snapshot saved to 27.png

Láthatóan felismeri az erőforrást, de nem kezd vele semmit.

A korábban megfogalmazott ötleteidet végig jártam, de ott semmi változást nem tapasztaltam. Elkerülő úton sikerült megpatkolnom a szervert, hogy állomány szinten elérhető legyen a kép, de jobb lenne, ha sikerülne a webkit2gtk-val megoldani a nagy problémát.

Hogyan tovább?

Resource load STARTED : http://xyz.hu/artefact/file/download.php?file=8&view=6&time=1582701544 Resource load FINISHED: http://xyz.hu/artefact/file/download.php?file=8&view=6&time=1582701544&login

Összevetve a 2 kimeneti sort, látható hogy történt egy redirect. A fejlesztői gépen a böngésződben be lehetsz jelentkezve az oldaladra, ezért ott nem kapod a redirect-et a &login -ra. A képletöltő headless böngészők resource lekérése viszont át lesz irányítva, az img tag src hivatkozására egy html-t kapnak vissza, amivel helyesen nem kezd semmit.

A betöltési hibák kiszűrésére egy nagyon hasznos, mindenképpen ajánlott kiegészítés legutóbbi hsz-emhez, csak az on_resource_finished() változik - kiírja a választ:

    def on_resource_finished(self, resource):
        if resource.get_response() is None:
            resp_mime = "None"
        else:
            resp_mime = "status_code: " + str(resource.get_response().get_status_code()) + ", mime_type: " + str(resource.get_response().get_mime_type())
        print("Resource load FINISHED:", resource.get_uri())
        print("              RESPONSE:", resp_mime)
        self.resources_loading_set.discard(resource)
        self.restart_timer()

Így mostmár a kimeneten az is látszik, hogy pl a css-re 404-et kapok (el van írva a kiterjesztés?), az img hivatkozásra pedig html-t:

Resource load FINISHED: http://xyz.hu/HUNGLE/style.ccs
              RESPONSE: status_code: 404, mime_type: text/html
								(resources loading: 1, webview loading: True)
Resource load FINISHED: http://xyz.hu/artefact/file/download.php?file=8&view=6&time=1582702325&login
              RESPONSE: status_code: 200, mime_type: text/html
								(resources loading: 0, webview loading: True)

Bocs, hogy telesírom ezt a fórumot a bajaimmal, de egyrészt ez megnyugtat :-), másrészt hátha akad kolléga, akinek lenne segítő gondolata. A fenti téma Google-keresése közben egyszerűsödött a kívánságom:

Van-e valami CLI megoldás a dinamikusan generált weboldalak letöltésére (esetleg WebKit2GTK python kóddal :-) )? Azt olvastam, hogy a wget, curl ugyan nem beszél javascript-tül, de nekem egy PHP kód ad vissza egy képet, azt kéne megvárnom, de nem biztatnak sok jóval. A legtöbb helyen a phantomjs-t ajánlják, amit elég régóta nem fejlesztenek és amikor kipróbáltam, hááát, nem volt valami szép a végeredmény, ráadásul hiányos volt a generált tartalom.

A Selenium felmerült, Firefox alá telepítettem a Selenium IDE-t, jópofa, csak pont nincs benne képernyőmentés.

Eddig a WebKit2GTK tűnik a legjobb megoldásnak, bár egyelőre ennek is vérzik a torka.

Valahogy nehéz elhinni, hogy tényleg ilyen rétegigényt fogalmaztam meg, senkinek nem volt szüksége arra, amire nekem. Vagy megoldják máshogy?

 

Üdv, Cz

html2canvas jó lesz oda. Már használtam, nincs vele gond.