2013-08-21 19 views
9

Il problema: un sito Web che sto cercando di raccogliere i dati dagli usi Javascript per produrre un grafico. Mi piacerebbe essere in grado di estrarre i dati che vengono utilizzati nel grafico, ma non sono sicuro da dove iniziare. Ad esempio, i dati potrebbero essere i seguenti:Come posso analizzare le variabili Javascript usando python?

var line1= 
[["Wed, 12 Jun 2013 01:00:00 +0000",22.4916114807,"2 sold"], 
["Fri, 14 Jun 2013 01:00:00 +0000",27.4950008392,"2 sold"], 
["Sun, 16 Jun 2013 01:00:00 +0000",19.5499992371,"1 sold"], 
["Tue, 18 Jun 2013 01:00:00 +0000",17.25,"1 sold"], 
["Sun, 23 Jun 2013 01:00:00 +0000",15.5420341492,"2 sold"], 
["Thu, 27 Jun 2013 01:00:00 +0000",8.79045295715,"3 sold"], 
["Fri, 28 Jun 2013 01:00:00 +0000",10,"1 sold"]]; 

Dati di prezzo (Data, Prezzo, Volume). Ho trovato un'altra domanda qui - Parsing variable data out of a js tag using python - che suggerisce di utilizzare JSON e BeautifulSoup, ma non sono sicuro di come applicarlo a questo particolare problema perché la formattazione è leggermente diversa. In effetti, in questo problema il codice sembra più simile a qualsiasi tipo di formato di dizionario JSON.

Suppongo che potrei leggerlo come una stringa, quindi utilizzare XPATH e alcune stringhe funky per convertirlo, ma questo sembra troppo lavoro per qualcosa che è già formattato come variabile Javascript.

Quindi, cosa posso fare qui per estrarre questo tipo di dati organizzati da questa variabile durante l'utilizzo di python? (Mi è più familiare con pitone e BS4)

+0

tranne l'interruzione di riga dopo '=' e la parola chiave 'var', il resto è valida in python –

+0

è che il codice vero e proprio? o è una variabile chiamata 'line1' che è una lista di liste? Se è quest'ultimo, si può 'per la lista in linea1: do_something_with (lista [0], lista [1], lista2])' – IPDGino

+0

È una variabile chiamata linea1, che è parte del contenuto della pagina in caricamento, ed è una lista di liste. –

risposta

2

Okay, quindi ci sono alcuni modi per farlo, ma ho finito semplicemente usando un'espressione regolare per trovare tutto tra line1= e ;

#Read page data as a string 
pageData = sock.read() 
#set p as regular expression 
p = re.compile('(?<=line1=)(.*)(?=;)') 
#find all instances of regular expression in pageData 
parsed = p.findall(pageData) 
#evaluate list as python code => turn into list in python 
newParsed = eval(parsed[0]) 

Regex è bello quando si dispone di una buona codifica, ma è questo metodo migliore (EDIT: o peggio!) di qualsiasi altra risposta qui?

EDIT: alla fine ho usato il seguente:

#Read page data as a string 
pageData = sock.read() 
#set p as regular expression 
p = re.compile('(?<=line1=)(.*)(?=;)') 
#find all instances of regular expression in pageData 
parsed = p.findall(pageData) 
#load as JSON instead of using evaluate to prevent risky execution of unknown code 
newParsed = json.loads(parsed[0]) 
+1

'eval' è negativo per molte ragioni; è quasi sempre meglio usare qualcosa di più restrittivo come 'json.loads' o' ast.literal_eval'. Certo, potrebbe esserci un codice perfettamente sicuro e valido come Python che funziona anche se non è un valore letterale (come 'abc' + 'def'), ma hai almeno le probabilità di imbattersi in un'espressione questo significa qualcosa di diverso come Python o anche qualcosa di non sicuro.(Immagina cosa succede se qualcuno ti dà 'var line1 = __ import __ ('os'). System ('rm -rf ~ /')' ...) – abarnert

-1

Di seguito fa un paio di ipotesi, come sapere come la pagina viene formattata, ma un modo di ottenere il vostro esempio nella memoria su Python è come questo

# example data 
data = 'foo bar foo bar foo bar foo bar\r\nfoo bar foo bar foo bar foo bar \r\nvar line1=\r\n[["Wed, 12 Jun 2013 01:00:00 +0000",22.4916114807,"2 sold"],\r\n["Fri, 14 Jun 2013 01:00:00 +0000",27.4950008392,"2 sold"],\r\n["Sun, 16 Jun 2013 01:00:00 +0000",19.5499992371,"1 sold"],\r\n["Tue, 18 Jun 2013 01:00:00 +0000",17.25,"1 sold"],\r\n["Sun, 23 Jun 2013 01:00:00 +0000",15.5420341492,"2 sold"],\r\n["Thu, 27 Jun 2013 01:00:00 +0000",8.79045295715,"3 sold"],\r\n["Fri, 28 Jun 2013 01:00:00 +0000",10,"1 sold"]];\r\nfoo bar foo bar foo bar foo bar\r\nfoo bar foo bar foo bar foo bar' 
# find your variable's start and end 
x = data.find('line1=') + 6 
y = data.find(';', x) 
# so you can get just the relevant bit 
interesting = data[x:y].strip() 
# most dangerous step! don't do this on unknown sources 
parsed = eval(interesting) 
# maybe you'd want to use JSON instead, if the data has the right syntax 
from json import loads as JSON 
parsed = JSON(interesting) 
# now parsed is your data 
+2

Non userò sicuramente 'eval' qui. Se vuoi gestire qualsiasi cosa che sia un vero letterale Python, usa 'ast.literal_eval'. Ma probabilmente non lo vuoi nemmeno. – abarnert

+0

Fondamentalmente avevo la stessa cosa che avevo in mente, ma ho usato una regex. Anche se ho finito per usare eval ... –

-1

Supponendo di avere una variabile python con una riga/blocco javascript come una stringa come "var line1 = [[a,b,c], [d,e,f]];", è possibile utilizzare le seguenti righe di codice.

>>> code = """var line1 = [['a','b','c'], ['d','e','f'], ['g','h','i']];""" 
>>> python_readable_code = code.strip("var ;") 
>>> exec(python_readable_code) 
>>> print(line1) 
[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']] 

exec() Eseguirà il codice che è formattato come una stringa. In questo caso verrà impostata la variabile line1 in un elenco con elenchi.

E di quanto si potrebbe usare qualcosa di simile:

for list in line1: 
    print(list[0], list[1], list[2]) 
    # Or do something else with those values, like save them to a file 
+0

Non hai davvero bisogno di togliere il punto e virgola; sono separatori di istruzioni validi in Python, anche se separano solo un'istruzione dal nulla. Ma ancora più importante, 'strip (" var; ")' sarà pericoloso se i nomi delle variabili potrebbero essere, ad esempio, 'a_line' ... – abarnert

+0

E ancora più importante, provare a trasformare JS in Python ed eseguirlo è davvero, veramente hacky e poco sicuro. Funzionerà in modo sicuro negli stessi casi in cui basta scomporre e chiamare 'ast.literal_eval' sul lato destro, ma sarebbe molto più sicuro e ti permetterebbe di memorizzare le variabili ovunque tu voglia (ad esempio, in un ditt) invece di forzarli nella gente del posto (che non è quasi mai quello che vuoi). – abarnert

6

Se il formato è in realtà solo uno o più var foo = [JSON array or object literal];, si può semplicemente scrivere un regex dotall per estrarli, quindi analizzare ciascuno come JSON. Ad esempio:

>>> j = '''var line1= 
[["Wed, 12 Jun 2013 01:00:00 +0000",22.4916114807,"2 sold"], 
["Fri, 14 Jun 2013 01:00:00 +0000",27.4950008392,"2 sold"], 
["Sun, 16 Jun 2013 01:00:00 +0000",19.5499992371,"1 sold"], 
["Tue, 18 Jun 2013 01:00:00 +0000",17.25,"1 sold"], 
["Sun, 23 Jun 2013 01:00:00 +0000",15.5420341492,"2 sold"], 
["Thu, 27 Jun 2013 01:00:00 +0000",8.79045295715,"3 sold"], 
["Fri, 28 Jun 2013 01:00:00 +0000",10,"1 sold"]];\s*$''' 
>>> values = re.findall(r'var.*?=\s*(.*?);', j, re.DOTALL | re.MULTILINE) 
>>> for value in values: 
...  print(json.loads(value)) 
[[['Wed, 12 Jun 2013 01:00:00 +0000', 22.4916114807, '2 sold'], 
    ['Fri, 14 Jun 2013 01:00:00 +0000', 27.4950008392, '2 sold'], 
    ['Sun, 16 Jun 2013 01:00:00 +0000', 19.5499992371, '1 sold'], 
    ['Tue, 18 Jun 2013 01:00:00 +0000', 17.25, '1 sold'], 
    ['Sun, 23 Jun 2013 01:00:00 +0000', 15.5420341492, '2 sold'], 
    ['Thu, 27 Jun 2013 01:00:00 +0000', 8.79045295715, '3 sold'], 
    ['Fri, 28 Jun 2013 01:00:00 +0000', 10, '1 sold']]] 

Naturalmente questo rende alcune ipotesi:

  • Una virgola alla fine della riga deve essere un separatore effettiva istruzione, non mezzo di una stringa. Questo dovrebbe essere sicuro perché JS non ha stringhe multiline in stile Python.
  • Il codice contiene effettivamente punti e virgola alla fine di ogni istruzione, anche se sono facoltativi in ​​JS. La maggior parte del codice JS ha quel punto e virgola, ma ovviamente non è garantito.
  • I letterali degli array e degli oggetti sono davvero compatibili con JSON. Questo sicuramente non è garantito; ad esempio, JS può utilizzare stringhe con quotatura singola, ma JSON no. Ma funziona per il tuo esempio.
  • Il tuo formato è davvero ben definito. Ad esempio, se potrebbe esserci un'affermazione come var line2 = [[1]] + line1; nel mezzo del codice, causerà problemi.

Nota che se i dati potrebbero contenere letterali JavaScript che non sono tutti validi JSON, ma sono tutte letterali Python validi (che non è probabile, ma non è impossibile, o), è possibile utilizzare il ast.literal_eval invece di json.loads.Ma non lo farei a meno che tu non sappia che questo è il caso.

+0

Giusto. Penso che le pagine che sto scrivendo siano abbastanza ben formattate, ma avere una riga in più = in qualche modo potrebbe rovinare tutto. –

+0

Vuoi dire avere un 'line1 =' randagio da qualche parte, o vuoi dire 'line1 =' senza 'var' prima di esso? Il primo non sarebbe riuscito a eguagliare ed essere saltato, il che va bene. Anche quest'ultimo non riuscirebbe a corrispondere ed essere saltato, ma potrebbe non essere adatto. Se questo è un problema, hai bisogno di una regex diversa. In realtà, l'espressione regolare non è appropriata a meno che non sia possibile definire chiaramente il proprio formato di input; se stai solo indovinando il formato, probabilmente vorrai qualcosa di più sciolto e più dettagliato, magari un vero parser costruito in 'pyparsing'. – abarnert

+0

Sembra che questo progetto sia chiaramente definito, ed è per questo che ha funzionato regex. Tuttavia, ovviamente non so di tutte le altre pagine che intendo scrupolosamente. Probabilmente dovrò solo usare regex fino a quando qualcosa si rompe e vai da lì. –

Problemi correlati