2013. december 26., csütörtök

Javascript futtatása Python kódból, PyV8 fordítása

Kellemes Ünnepeket mindenkinek!
Természetesen azért, mert megint nem írtam már hetek hónapok óta, még nem állt meg az élet. Mostanában web scraping-el foglalkozok, ami nagyon komplex egy feladat, persze csak ha az ember normálisan akarja megcsinálni.

Egy olyan problémába ütköztem, ami manapság egyre hétköznapibb, hogy a weboldal nagy részét Javascript kódból építik fel. Használhatnék amúgy erre egy valódi böngészőt is, ami futtatja a kapott kódot, és Python alól birizgálom, pl Selenium-al, de nekem csak a Javascript kódban tárolt adatokra van szükségem. Párperces keresgélés után nem találtam normális parsert a nyelvhez, így aztán úgy döntöttem, hogy felrakom a Google V8 engine-jét, a hozzá tartozó Python-os bindinggel, a PyV8-al, betápolom neki a kódot, és dolgozok a visszakapott értékekkel.

Szerencsére a kis Odroid-X2-n szépen -lassan- lefordul maga az engine, és a binding is. A dokumentációban nem nagyon van részletes leírás a fordítás menetéről, így ezt magamnak kellett kitalálnom. Szerencsére apt-ből mindent össze lehet szedni. Lássuk a parancsokat!
sudo apt-get install libboost-python-dev libboost-thread-dev libboost-system-dev
Ennyi az összes függőség a fordításhoz. Ha nincs svn kliens feltelepítve, akkor erre még szükség lehet:
sudo apt-get install subversion
Ezek után egyszerűen pip-el telepítjük
sudo pip install pyv8

Rövid példácska

Rendbe, ha már ilyen szépen feltelepült a lib, akkor azt is leírom hogy mire kellett.
Tehát, a feldolgozandó kódban, egyes HTML tag-ek onmouseover eventjén van az alábbihoz hasonló kód:
jQuery("#1337").html("Leet");
Azaz rámutatáskor ad 1337 id-ű tag tartalma legyen a Leet string. Ilyen egyszerű.

Nem akartam az egész jQuery-t betölteni a memóriába, ezért inkább írtam egy kis kamu függvényt, amivel a fenti sor futtatható. Így néz ki:
var jQuery = function (unused){
    html = function(data){
        return data
    }
    return this;
}
A jQuery függvény visszatér a this-el, ami úgy mellékesen a window. Maga a függvény nem csinál semmit se a paraméterével. A függvény törzsében definiálunk egy html nevű függvényt. Ez globális lesz, így a window.html-en keresztül is elérhető.
Tehát amikor a jQuery visszatér a this-el, ott lesz már a window.html is, így működni fog method chaining (igazából nem fog működni, ahhoz megint csak this-el kellett volna visszatérni, de nekem most így is megfelel a kód).

Rakjuk össze az egészet és próbáljuk ki:

import PyV8
ctx = PyV8.JSContext()
ctx.enter()
ctx.eval('var jQuery=function(unused){html=function(data){return data};return this;}')
ctx.eval("jQuery('#1337').html('Leet')")
#>>>'Leet'

Először létrehozunk egy új contextet és bele is lépünk. Ez után betöltjük a függvényt, aztán kipróbáljuk hogy visszakapjuk-e a html-nek adott stringet, természetesen a Python interpreterben.
Az utolsó sorban látszik hogy ez megtörtént, úgyhogy nagy az öröm!

-slp