如何使用python解析Javascript变量?

如何使用python解析Javascript变量?,javascript,python,web-scraping,beautifulsoup,Javascript,Python,Web Scraping,Beautifulsoup,问题是:我试图收集数据的网站使用Javascript生成图形。我希望能够提取图形中正在使用的数据,但我不确定从哪里开始。例如,数据可能如下所示: 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.5499992

问题是:我试图收集数据的网站使用Javascript生成图形。我希望能够提取图形中正在使用的数据,但我不确定从哪里开始。例如,数据可能如下所示:

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"]];
这是定价数据(日期、价格、数量)。我在这里发现了另一个问题——这建议我使用JSON和BeautifulSoup,但我不确定如何将其应用于这个特定问题,因为格式略有不同。事实上,在这个问题中,代码看起来更像python,而不是任何类型的JSON字典格式

我想我可以把它作为一个字符串读入,然后使用XPATH和一些时髦的字符串编辑来转换它,但是对于已经格式化为Javascript变量的东西来说,这似乎是太多的工作了


那么,在使用python时,如何从这个变量中提取这种类型的有组织数据呢?(我最熟悉python和BS4)

以下是一些假设,例如了解页面的格式,但是在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

假设您有一个带有javascript行/块的python变量作为字符串,如
“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

如果您的格式实际上只是一个或多个
var foo=[JSON数组或对象文字],您只需编写一个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的语句在代码的中间,它会引起问题。

请注意,如果数据可能包含不全是有效JSON的JavaScript文本,但都是有效的Python文本(这不太可能,但也不是不可能),则可以对其使用
ast.literal\u eval
,而不是
JSON.loads
。但是除非你知道是这样,否则我不会这么做。

好的,有几种方法可以做到,但我最终只是使用正则表达式来查找
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])
#以字符串形式读取页面数据
pageData=sock.read()
#将p设置为正则表达式

p=re.compile(“(?除了
=
之后的换行符和
var
关键字之外,其余的在pythonIs中是有效的,实际的代码?还是一个名为
line1
的变量,它是列表的列表?如果是后者,你可以
为第1行中的列表:做些什么(列表[0],列表[1],列表2]))
这是一个名为line1的变量,它是加载页面内容的一部分,是一个列表列表。因此,基本上它是一个javascript变量的字符串?不是一个实际的python变量。我担心您将不得不使用某种解析模块或去除字符串中所有不必要的文本和
exec()
它。使用
exec()之后
你可以用它做各种事情。我没办法让某个解析器抓取第1行的内容?这看起来应该很简单……我绝对不会在这里使用
eval
。如果你想处理任何有效的Python文本,请使用
ast.literal\u eval
。但你可能根本不想这样。Y你的想法基本上与我的想法相同,但我只是使用了一个正则表达式。虽然我最终使用了eval…但你并不需要去掉分号;它们在Python中是有效的语句分隔符,即使它们只是将一条语句从无到有分开。但更重要的是,
strip(“var;”)如果变量名可能是,比如说,
a_行
,那么将是危险的。更重要的是,试图将JS转换为Python并执行它是非常非常有黑客性和不安全的。它将在完全相同的情况下安全地工作,只需在右侧拆分并调用
ast.literal_eval
这样会更安全,并且允许您将变量存储在任何您想要的地方(例如,在dict中),而不是强制将它们存储到局部变量中(这几乎从来都不是您想要的).对。我认为我正在抓取的页面格式很好,但是如果有一个额外的行1=某处,那就真的会把事情搞砸。你是说有一个零散的
line1=
某处,还是说前面没有
var
line1=
?前者将无法匹配并被跳过,这很好。后者将d也无法匹配并被跳过,但可能不好。如果这是一个问题,你需要一个不同的正则表达式。真的,正则表达式不合适,除非你能清楚地定义你的输入格式;如果你只是猜测格式,你可能想要一些更松散、更详细的东西,也许是一个真正的解析器内置于
pyparsing
。看起来is项目的定义非常明确,这就是为什么正则表达式可以工作的原因。然而,我显然不知道我打算刮去的所有其他页面。我可能只需要使用正则表达式,直到出现问题为止。只要在一堆页面上运行是合适的,然后在第一个需要的页面上调试正则表达式
#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])