Hallo wie kann ich verschiedene Javascript Arrays (var a=new Array(...), etc...) aus einer Webseite (mit mechanize.Browser() geladen) in Python parsen?
Was ist da die eleganteste Methode?
Javascript Array aus Webseite parsen
Wenn denen allen gemeinsam ist, dass die Daten zwischen 'Array(' und ')' und in den Daten selber keine ')' vorkommen, dann könnte man in einem ersten Schritt diese Informationen heraus ziehen. Dann kommt es darauf an, wie der Inhalt des Arrays aussieht.
hey danke für eure antworten.
werde die 3-4 arrays jetzt wohl irgendwie manuell parsen.
Das mit pypy wär ne gute idee wenn ich mehr javascript zu verarbeiten hätte.
btw, 2. platz bei google, das ging aber schnell
http://www.google.de/search?q=python+javascript+parsen
werde die 3-4 arrays jetzt wohl irgendwie manuell parsen.
Das mit pypy wär ne gute idee wenn ich mehr javascript zu verarbeiten hätte.
btw, 2. platz bei google, das ging aber schnell

http://www.google.de/search?q=python+javascript+parsen
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Du kannst entweder eine kleine EBNF aufstellen fürs Array oder so Lösungen wie Pyparsing verwenden. Das sollte recht einfach zu machen sein.fuku hat geschrieben:werde die 3-4 arrays jetzt wohl irgendwie manuell parsen.
Das mit pypy wär ne gute idee wenn ich mehr javascript zu verarbeiten hätte.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
ok, ich probiers mal mit pyparsing, eine grammatik für arrays zu erstellen ist nicht schwer.
jedoch habe ich so meine probleme das in pyparsing umzusetzen.
soweit bin ich bisher gekommen, jedoch bricht das program beim variablennamen des arrays ab (pyparsing.ParseException: Expected W:(ABCD...,abcd...) (at char 4), (line:1, col:5))
Was ist denn da falsch gelaufen. "name" gehört eigentlich zu word.
Nunja ich geh jetzt schlafen. Gute Nacht =)
jedoch habe ich so meine probleme das in pyparsing umzusetzen.
soweit bin ich bisher gekommen, jedoch bricht das program beim variablennamen des arrays ab (pyparsing.ParseException: Expected W:(ABCD...,abcd...) (at char 4), (line:1, col:5))
Was ist denn da falsch gelaufen. "name" gehört eigentlich zu word.
Nunja ich geh jetzt schlafen. Gute Nacht =)
Code: Alles auswählen
#!/usr/bin/python
from pyparsing import *
exp = "var name=new Array('testtest', 3345434343,34543543543,'test',3232323);"
word = Word(string.uppercase, string.lowercase)
string = Literal("'") + word + Literal("'")
digit = Word("0123456789")
var = string | digit
bracket = Literal("(") + var + Optional(OneOrMore(Literal(",")+var))
array = Literal("var ") + word + Literal("=new Array") + bracket + Literal(";")
print array.parseString(exp)
@fuku: So wie Du `word` definiert hast muss es mit einem Grossbuchstaben beginnen und darf danach nur noch Kleinbuchstaben enthalten. Und das trifft auf 'name' nunmal nicht zu.
`pyparsing` kümmert sich im Normalfall selbst um Leerzeichen, die sollte man also in der Grammatik nicht angeben.
Dann hast Du die schliessende Klammer vor dem Semikolon in der Grammatik vergessen.
`pyparsing` kümmert sich im Normalfall selbst um Leerzeichen, die sollte man also in der Grammatik nicht angeben.
Dann hast Du die schliessende Klammer vor dem Semikolon in der Grammatik vergessen.
Code: Alles auswählen
from pyparsing import (alphas, delimitedList, Group, Literal, nums, OneOrMore,
Optional, removeQuotes, sglQuotedString, Suppress, Word)
def main():
source = ("var name=new Array('testtest',"
"3345434343,34543543543,'test',3232323);\n"
"var foo = new Array ( 'answer', 42 ) ;")
name = Word(alphas)
string = sglQuotedString
digit = Word(nums)
value = string | digit
bracketed = Suppress('(') + Group(delimitedList(value)) + Suppress(')')
array = (Suppress('var') + name
+ Suppress(Literal('=') + 'new' + 'Array')
+ bracketed + Suppress(';'))
print array.searchString(source)
Es muss in Zeile 5
lauten. Außerdem fehlt dir in Zeile 9 noch ein
Du hast mit diesem Ansatz aber noch einen weiten Weg vor dir, denn jedes Element des Array-Konstruktors kann ja beliebig komplex sein. Willst du vielleicht einfach nur JSON parsen? Dafür gibt es ein Beispiel für Pyparsing.
Ansonsten, ein JavaScript-Parser in Python, der einen AST bauen kann, wäre schon eine schicke Sache. Du könntest ihn dann ja vielleicht als weiteres Beispiel für Pyparsing einreichen. Ich fand bislang die Doku von Pyparsing nur so schlecht und den Quelltext sehr unübersichtlich, sodass ich nicht abschätzen kann, was für ein Aufwand das wäre.
Ansonsten? Kann nicht auch ANTLR Python-Code erzeugen? Da gibt es schon eine fertige Grammatik für EMCAScript 3. Vielleicht gibt es ja noch einen anderen Python-basierten Parsergenerator, der JavaScript als Beispiel mitbringt.
Ganz nett sieht Aperiot, insbesondere gefällt mir, dass dieser Parsergenerator (auf wenn's für deinen Fall unwichtig ist) indent/dedent-Token synthetisieren kann.
Ansonsten, CoCo/R ist ein netter kleiner Parsergenerator für LL(1)-Parser, für den es offenbar auch einen Python-Port gibt. Da könnte man diese etwas ältere JavaScript LL(1)-Grammatik einfüttern, die allerdings nicht konform zur aktuellen Spec ist, weil sie nicht richtig mit ";" umgeht. LL(1)-Grammatiken einfach genug, dass man dafür auch manuell einen rekursiv absteigenden Parser bauen kann, das fummeln mit CoCo/R zu umständlich ist.
Stefan
Code: Alles auswählen
word = Word(string.uppercase + string.lowercase)
Code: Alles auswählen
+ Literal(")")
Ansonsten, ein JavaScript-Parser in Python, der einen AST bauen kann, wäre schon eine schicke Sache. Du könntest ihn dann ja vielleicht als weiteres Beispiel für Pyparsing einreichen. Ich fand bislang die Doku von Pyparsing nur so schlecht und den Quelltext sehr unübersichtlich, sodass ich nicht abschätzen kann, was für ein Aufwand das wäre.
Ansonsten? Kann nicht auch ANTLR Python-Code erzeugen? Da gibt es schon eine fertige Grammatik für EMCAScript 3. Vielleicht gibt es ja noch einen anderen Python-basierten Parsergenerator, der JavaScript als Beispiel mitbringt.
Ganz nett sieht Aperiot, insbesondere gefällt mir, dass dieser Parsergenerator (auf wenn's für deinen Fall unwichtig ist) indent/dedent-Token synthetisieren kann.
Ansonsten, CoCo/R ist ein netter kleiner Parsergenerator für LL(1)-Parser, für den es offenbar auch einen Python-Port gibt. Da könnte man diese etwas ältere JavaScript LL(1)-Grammatik einfüttern, die allerdings nicht konform zur aktuellen Spec ist, weil sie nicht richtig mit ";" umgeht. LL(1)-Grammatiken einfach genug, dass man dafür auch manuell einen rekursiv absteigenden Parser bauen kann, das fummeln mit CoCo/R zu umständlich ist.
Stefan
Hallo Leute,
der Code funktioniert, danke.
Momentan sieht die Function folgendermaßen aus:
die correctList function habe ich eingebaut, da die list die mir diese Funktion geliefert hat jeweils Indexnamen und Werte zusammen in einer Liste angezeigt hat und Integer als Strings angezeigt wurden.
Außerdem muss ich die Korrekturfunktion so erweitern, dass von den Strings die Anführungszeichen gelöscht werden.
Ist das so ok oder kann ich das mittels pyparsing eleganter hinbekommen?
der Code funktioniert, danke.
Momentan sieht die Function folgendermaßen aus:
Code: Alles auswählen
def parse(self, source):
def correctList(lst):
cor = {}
for item in lst:
cor[item[0]] = list(conv(elem) for elem in item[1])
return cor
def conv(x):
try:
return int(x)
except ValueError:
return x
name = Word(alphas)
string = quotedString
digit = Word(nums)
value = string | digit
squareBracket = Suppress('[') + Group(delimitedList(value)) + Suppress(']')
value = string | digit | squareBracket
bracketed = Suppress('(') + Group(delimitedList(value)) + Suppress(')')
array = (Suppress('var') + name
+ Suppress(Literal('=') + 'new' + 'Array')
+ bracketed + Suppress(';'))
return correctList(array.searchString(source))
Außerdem muss ich die Korrekturfunktion so erweitern, dass von den Strings die Anführungszeichen gelöscht werden.
Ist das so ok oder kann ich das mittels pyparsing eleganter hinbekommen?
"correctList" könntest du noch etwas vereinfachen:
Code: Alles auswählen
def correct_list(lst):
return dict((k, map(conv, v)) for (k, v) in lst)
Mit `parseAction()` kann man die Konvertierungen gleich in den Parser einbauen.
Warum gibt's bei Dir ein `self`-Argument, das gar nicht verwendet wird?
Ausserdem ist `value` unschön. Du definierst das zweimal, d.h. es hat vor der zweiten Zusweisung eine andere Bedeutung als nachher.
Code: Alles auswählen
def parse(source):
name = Word(alphas)
string = quotedString.setParseAction(removeQuotes)
digit = Word(nums).setParseAction(lambda toks: int(toks[0]))
value = string | digit
squareBracket = Suppress('[') + Group(delimitedList(value)) + Suppress(']')
value = string | digit | squareBracket
bracketed = Suppress('(') + Group(delimitedList(value)) + Suppress(')')
array = (Suppress('var') + name
+ Suppress(Literal('=') + 'new' + 'Array')
+ bracketed + Suppress(';'))
return dict(array.searchString(source).asList())
Ausserdem ist `value` unschön. Du definierst das zweimal, d.h. es hat vor der zweiten Zusweisung eine andere Bedeutung als nachher.
Danke Jungs für die Verbesserungen.
@BlackJack:
Das self-Argument ist bei den Parametern weil die Funktion Teil einer Klasse ist.
Die doppelte value Zuweisung habe ich eingebaut, damit ich auf verschachtelte Arrays zugreifen kann, jedoch gefällt mir das selbst nicht.
Optimal ist es auch nicht, da es momentan nur auf eine Tiefe zugreifen kann funktioniert nicht
mit der for Schleife kann ich die Tiefe zwar einstellen, so richtig elegant ist das aber nicht.
Nochwas: Falls eines der Sub-Arrays leer ist funktioniert das Parsen nicht, da delimitedList mind. einen Treffer erwartet.
Gibt es eine passende Funktion die auch bei einem leeren Inhalt parst oder muss ich irgendwas mit ZeroOrMore basteln?
@BlackJack:
Das self-Argument ist bei den Parametern weil die Funktion Teil einer Klasse ist.
Die doppelte value Zuweisung habe ich eingebaut, damit ich auf verschachtelte Arrays zugreifen kann, jedoch gefällt mir das selbst nicht.
Optimal ist es auch nicht, da es momentan nur auf eine Tiefe zugreifen kann
Code: Alles auswählen
var name = new array('bla', [1, 2, 3])
Code: Alles auswählen
var name = new array('foo', [1, 2, ['bar', 'bar']])
Code: Alles auswählen
class Parse:
def parse(self, source):
name = Word(alphas)
string = quotedString.setParseAction(removeQuotes)
digit = Word(nums).setParseAction(lambda toks: int(toks[0]))
value = string | digit
for i in range(2):
squareBracket = Suppress('[') + Group(delimitedList(value)) + Suppress(']')
value = string | digit | squareBracket
bracketed = Suppress('(') + Group(delimitedList(value)) + Suppress(')')
array = (Suppress('var') + name + Suppress(Literal('=') + 'new' + 'Array')
+ bracketed + Suppress(';'))
return dict(array.searchString(source).asList())
Nochwas: Falls eines der Sub-Arrays leer ist funktioniert das Parsen nicht, da delimitedList mind. einen Treffer erwartet.
Gibt es eine passende Funktion die auch bei einem leeren Inhalt parst oder muss ich irgendwas mit ZeroOrMore basteln?
@fuku: Nächste Frage: Warum ist die Funktion teil einer Klasse, wenn gar nicht auf das Objekt zugegriffen wird? Ist oft ein Zeichen, dass man Java oder C# in Python programmiert.
Beliebig tief verschachteln kann man, indem man die Grammatik rekursiv angibt. Dazu benutzt man für ein Element der Grammatik erst einmal einen Platzhalter (`Forward`) und legt die eigentliche Grammatikregel erst fest (mit ``<<``), wenn alle nötigen Einzelteile mit dem Platzhalter definiert sind.
Leere Arrays und Unter-Arrays würde ich mit `Optional` realisieren.
Beliebig tief verschachteln kann man, indem man die Grammatik rekursiv angibt. Dazu benutzt man für ein Element der Grammatik erst einmal einen Platzhalter (`Forward`) und legt die eigentliche Grammatikregel erst fest (mit ``<<``), wenn alle nötigen Einzelteile mit dem Platzhalter definiert sind.
Leere Arrays und Unter-Arrays würde ich mit `Optional` realisieren.
Code: Alles auswählen
def parse(source):
name = Word(alphas)
string = quotedString.setParseAction(removeQuotes)
digit = Word(nums).setParseAction(lambda toks: int(toks[0]))
value = Forward()
values = Group(Optional(delimitedList(value)))
bracketed = Suppress('[') + values + Suppress(']')
value << (string | digit | bracketed)
paranthesed = Suppress('(') + values + Suppress(')')
array = (Suppress('var') + name
+ Suppress(Literal('=') + 'new' + 'Array')
+ paranthesed + Suppress(';'))
return dict(array.searchString(source).asList())
Hey BlackJack, danke für deine Hilfe.
Zu deiner Frage: Welches Objekt meinst du?
Meinst du, dass diese Funktion auf keine anderen Werte und Funktionen der Instanz zugreift und deswegen nichts in einer Klasse zu suchen hat?
Ich muss zugeben, dass ich im Bezug auf OOP noch Aufholbedarf habe.
Deswegen wäre es cool, wenn du mir sagen würdest, wie ich das besser machen kann (Soll die Funktion aus der Klasse ausgekapselt und seperat aufgerufen werden?).
Zu deiner Frage: Welches Objekt meinst du?
Meinst du, dass diese Funktion auf keine anderen Werte und Funktionen der Instanz zugreift und deswegen nichts in einer Klasse zu suchen hat?
Ich muss zugeben, dass ich im Bezug auf OOP noch Aufholbedarf habe.
Deswegen wäre es cool, wenn du mir sagen würdest, wie ich das besser machen kann (Soll die Funktion aus der Klasse ausgekapselt und seperat aufgerufen werden?).
@fuku: Das meinte ich. Wobei es trotzdem Gründe geben kann, so etwas in eine Klasse zu stecken. Dann würde ich aber eine `staticmethod` daraus machen, um dem Leser deutlicher zu machen, dass es eigentlich eine Funktion ist, die da im Namensraum einer Klasse steckt.