2013-08-21 2 views
9

Проблема: веб-сайт, на котором я пытаюсь собрать данные, использует Javascript для создания графика. Я хотел бы получить данные, которые используются на графике, но я не уверен, с чего начать. Например, данные могут быть следующими:Как я могу анализировать переменные Javascript с помощью 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"]]; 

Это данные о ценах (дата, цена, объем). Я нашел здесь еще один вопрос: Parsing variable data out of a js tag using python - что говорит о том, что я использую JSON и BeautifulSoup, но я не уверен, как применить его к этой конкретной проблеме, потому что форматирование немного отличается. На самом деле, в этой проблеме код больше похож на python, чем на любой тип формата словаря JSON.

Я полагаю, что я мог бы прочитать его как строку, а затем использовать XPATH и некоторые фанки редактирования строки, чтобы преобразовать его, но это кажется слишком большой работой для того, что уже отформатировано как переменная Javascript.

Итак, что я могу сделать здесь, чтобы вытащить этот тип организованных данных из этой переменной при использовании python? (Я знаком с питоном и BS4)

+0

, кроме разрыва строки после '=' и 'var' ключевое слово, остальное действительно в питона –

+0

Это фактический код? или это переменная, называемая 'line1', которая является списком списков? Если это последний, вы можете 'для списка в строке1: do_something_with (list [0], list [1], list2])' – IPDGino

+0

Это переменная с именем line1, которая является частью содержимого страницы при загрузке и представляет собой список списков. –

ответ

2

Итак, есть несколько способов сделать это, но я в конечном итоге просто используя регулярное выражение, чтобы найти все, что между line1= и ;

#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 хорошо, когда у вас есть хорошее кодирование, но этот метод лучше (EDIT: или хуже!), чем любой из других ответов здесь?

EDIT: Я в конечном счете используется следующее:

#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' плохо по многим причинам; почти всегда лучше использовать что-то более ограничительное, например 'json.loads' или' ast.literal_eval'. Конечно, может существовать совершенно безопасный и действительный, как и код Python, который работает, даже если он не является литералом (например, «abc» + «def»), но вы, по крайней мере, как можно скорее столкнетесь с выражением это означает что-то другое, как Python, или даже делает что-то небезопасное.(Представьте, что произойдет, если кто-то даст вам «var line1 = __ import __ ('os'). System ('rm -rf ~ /')' ...) – abarnert

-1

Ниже делает несколько предположений, например, зная, как отформатирован страница, но способ получить ваш пример в память о Python, как это

# 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

Я бы определенно не использовал 'eval' здесь. Если вы хотите обрабатывать все, что является допустимым литералом Python, используйте 'ast.literal_eval'. Но вы, скорее всего, этого не хотите. – abarnert

+0

У вас в основном было то же самое, что я имел в виду, но вместо этого я использовал регулярное выражение. Хотя я в конечном итоге использовал eval ... –

-1

Предполагая, что у вас есть переменная python с строкой/блоком javascript в виде строки, такой как "var line1 = [[a,b,c], [d,e,f]];", вы можете использовать следующие несколько строк кода.

>>> 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() Запустит код, форматированный в виде строки. В этом случае он будет устанавливать переменную line1 в список со списками.

И чем вы могли бы использовать что-то вроде этого:

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

Вам не нужно снимать точки с запятой; они являются допустимыми разделителями операторов в Python, даже если они только отделяют оператор от ничего. Но что более важно, 'strip (" var; ")' будет опасным, если имена переменных могут быть, скажем, 'a_line' ... – abarnert

+0

И что еще более важно, попытка превратить JS в Python и выполнить его действительно, действительно взломанный и небезопасный. Он будет работать безопасно в тех же случаях, что и просто расщепление и вызов 'ast.literal_eval' с правой стороны, но это было бы намного безопаснее и позволяло бы хранить переменные везде, где вы хотите (например, в dict) вместо того, чтобы принуждать их к местным жителям (это почти никогда не то, что вы хотите). – abarnert

6

Если ваш формат действительно только один или более var foo = [JSON array or object literal];, вы можете просто написать dotall регулярное выражение для извлечения их, а затем разобрать каждый из них как JSON. Например:

>>> 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']]] 

Конечно, это делает несколько предположений:

  • точкой с запятой в конце строки должен быть фактическим разделитель операторов, а не в середине строки. Это должно быть безопасным, поскольку JS не имеет многострочных строк в стиле Python.
  • У кода на самом деле есть точки с запятой в конце каждого утверждения, даже если они не являются обязательными в JS. У большинства JS-кодов эти точки с запятой, но это, очевидно, не гарантируется.
  • Матричные и объектные литералы действительно совместимы с JSON. Это определенно не гарантируется; например, JS может использовать одиночные кавычки, но JSON не может. Но это работает для вашего примера.
  • Ваш формат действительно четко определен. Например, если в середине кода может быть такой оператор, как var line2 = [[1]] + line1;, это вызовет проблемы.

Обратите внимание, что если данные могут содержать литералы JavaScript, которые не являются корректными JSON, но являются все действительные литералы Python (который не является вероятным, но не невозможно, либо), вы можете использовать ast.literal_eval на вместо json.loads.Но я бы этого не сделал, если вы не знаете, что это так.

+0

Справа. Я думаю, что страницы, которые я очищаю, довольно хорошо отформатированы, но наличие дополнительной строки1 = где-то действительно повредило бы все. –

+0

Вы имеете в виду, что у вас есть паразитная строка 'line1 =', или вы имеете в виду наличие 'line1 =' без 'var' перед этим? Первый просто не смог бы соответствовать и был пропущен, и это нормально. Последний также не смог бы соответствовать и быть пропущен, но не мог бы быть хорошим. Если это проблема, вам нужно другое регулярное выражение. Действительно, регулярное выражение не подходит, если вы не можете четко определить свой формат ввода; если вы просто догадываетесь о формате, вы, вероятно, хотите, чтобы что-то было более рыхлым и более подробным, может быть, реальный парсер, построенный в «pyparsing». – abarnert

+0

Кажется, что этот проект довольно четко определен, поэтому регулярное выражение сработало. Тем не менее, я, очевидно, не знаю обо всех других страницах, которые я намерен очистить. Мне, вероятно, просто придется использовать регулярное выражение, пока что-то не сломается и не уйдет оттуда. –

Смежные вопросы