【问题标题】:JavaScript parser in Python [closed]Python中的JavaScript解析器[关闭]
【发布时间】:2010-09-28 07:21:07
【问题描述】:

至少在 C 和 Java(Mozilla)、JavaScript(又是 Mozilla)和 Ruby 中都有 JavaScript 解析器。目前有没有适用于 Python 的?

我不需要 JavaScript 解释器,我只需要一个符合 ECMA-262 标准的解析器。

快速的谷歌搜索没有立即得到答案,所以我询问了 SO 社区。​​p>

【问题讨论】:

标签: javascript python parsing compiler-construction


【解决方案1】:

如今,至少有一种更好的工具,称为slimit

SlimIt 是一个用 Python 编写的 JavaScript 压缩器。它编译 将 JavaScript 转换为更紧凑的代码,以便下载和运行 更快。

SlimIt 还提供了一个包含 JavaScript 解析器的库, 词法分析器、漂亮的打印机和树访问者。

演示:

假设我们有以下 javascript 代码:

$.ajax({
    type: "POST",
    url: 'http://www.example.com',
    data: {
        email: 'abc@g.com',
        phone: '9999999999',
        name: 'XYZ'
    }
});

现在我们需要从data 对象中获取emailphonename 值。

这里的想法是实例化一个slimit 解析器,访问所有节点,过滤所有分配并将它们放入字典中:

from slimit import ast
from slimit.parser import Parser
from slimit.visitors import nodevisitor


data = """
$.ajax({
    type: "POST",
    url: 'http://www.example.com',
    data: {
        email: 'abc@g.com',
        phone: '9999999999',
        name: 'XYZ'
    }
});
"""

parser = Parser()
tree = parser.parse(data)
fields = {getattr(node.left, 'value', ''): getattr(node.right, 'value', '')
          for node in nodevisitor.visit(tree)
          if isinstance(node, ast.Assign)}

print fields

打印出来:

{'name': "'XYZ'", 
 'url': "'http://www.example.com'", 
 'type': '"POST"', 
 'phone': "'9999999999'", 
 'data': '', 
 'email': "'abc@g.com'"}

【讨论】:

  • 这太棒了!您还可以将每个值作为字典访问,例如:print fields['url']
  • 这个 2013 年的库似乎无法解析 Javascript 类。
  • Slimit 不错,但只支持 ES5 :(
【解决方案2】:

ANTLR,另一种语言识别工具,是一种语言工具,它提供了一个框架,用于从包含各种目标语言动作的语法描述构建识别器、解释器、编译器和翻译器。

ANTLR 网站提供many grammars,包括one for JavaScript

碰巧有一个Python API 可用 - 所以你可以直接从 Python 调用从语法生成的词法分析器(识别器)(祝你好运)。

【讨论】:

  • ANTLR 4 似乎复活了。
【解决方案3】:

我已将esprima.js 翻译成 Python:

https://github.com/PiotrDabkowski/pyjsparser

>>> from pyjsparser import parse
>>> parse('var $ = "Hello!"')
{
"type": "Program",
"body": [
    {
        "type": "VariableDeclaration",
        "declarations": [
            {
                "type": "VariableDeclarator",
                "id": {
                    "type": "Identifier",
                    "name": "$"
                },
                "init": {
                    "type": "Literal",
                    "value": "Hello!",
                    "raw": '"Hello!"'
                }
            }
        ],
        "kind": "var"
    }
  ]
}

这是手动翻译,速度非常快,解析angular.js 文件大约需要 1 秒(因此每秒 100k 个字符)。它支持整个 ECMAScript 5.1 和版本 6 的部分内容 - 例如箭头函数、constlet

如果您需要对所有最新 JS6 功能的支持,您可以使用 Js2Py 即时翻译 esprima:

import js2py
esprima = js2py.require("esprima@4.0.1")
esprima.parse("a = () => {return 11};")
# {'body': [{'expression': {'left': {'name': 'a', 'type': 'Identifier'}, 'operator': '=', 'right': {'async': False, 'body': {'body': [{'argument': {'raw': '11', 'type': 'Literal', 'value': 11}, 'type': 'ReturnStatement'}], 'type': 'BlockStatement'}, 'expression': False, 'generator': False, 'id': None, 'params': [], 'type': 'ArrowFunctionExpression'}, 'type': 'AssignmentExpression'}, 'type': 'ExpressionStatement'}], 'sourceType': 'script', 'type': 'Program'}

【讨论】:

  • 经过测试并且运行良好。您可以使用它并从中重构例如一些由爬虫捕获的 JSON 数据。
  • 很好的文档,你也可以在线测试一些代码,esprima.org/demo/parse.html
【解决方案4】:

正如 pib 提到的,pynarcissus 是一个用 Python 编写的 Javascript 分词器。它似乎有一些粗糙的边缘,但到目前为止一直很好地完成了我想要完成的工作。

更新:对 pynarcissus 进行了另一次破解,以下是在访问者模式(如系统)中使用 PyNarcissus 的工作方向。不幸的是,我当前的客户购买了我的下一个实验版本,并决定不将其公开。以下代码的更简洁版本在 gist here

from pynarcissus import jsparser
from collections import defaultdict

class Visitor(object):

    CHILD_ATTRS = ['thenPart', 'elsePart', 'expression', 'body', 'initializer']

def __init__(self, filepath):
    self.filepath = filepath
    #List of functions by line # and set of names
    self.functions = defaultdict(set)
    with open(filepath) as myFile:
        self.source = myFile.read()

    self.root = jsparser.parse(self.source, self.filepath)
    self.visit(self.root)


def look4Childen(self, node):
    for attr in self.CHILD_ATTRS:
        child = getattr(node, attr, None)
        if child:
            self.visit(child)

def visit_NOOP(self, node):
    pass

def visit_FUNCTION(self, node):
    # Named functions
    if node.type == "FUNCTION" and getattr(node, "name", None):
        print str(node.lineno) + " | function " + node.name + " | " + self.source[node.start:node.end]


def visit_IDENTIFIER(self, node):
    # Anonymous functions declared with var name = function() {};
    try:
        if node.type == "IDENTIFIER" and hasattr(node, "initializer") and node.initializer.type == "FUNCTION":
            print str(node.lineno) + " | function " + node.name + " | " + self.source[node.start:node.initializer.end]
    except Exception as e:
        pass

def visit_PROPERTY_INIT(self, node):

    # Anonymous functions declared as a property of an object
    try:
        if node.type == "PROPERTY_INIT" and node[1].type == "FUNCTION":
            print str(node.lineno) + " | function " + node[0].value + " | " + self.source[node.start:node[1].end]
    except Exception as e:
        pass


def visit(self, root):

    call = lambda n: getattr(self, "visit_%s" % n.type, self.visit_NOOP)(n)
    call(root)
    self.look4Childen(root)
    for node in root:
        self.visit(node)

filepath = r"C:\Users\dward\Dropbox\juggernaut2\juggernaut\parser\test\data\jasmine.js"
outerspace = Visitor(filepath)

【讨论】:

  • 这不能从已解析的 AST 中发出 JavaScript,可以吗?我想修改 AST 并发出新的 JavaScript,但这似乎无法做到。
  • @gsingh2011 不,它在解析方面有点吃力,所以用另一种方式来做已经超出了它的能力。
【解决方案5】:

你可以试试python-spidermonkey 它是spidermonkey 的一个包装器,它是 Mozilla 的 javascript 的 C 实现的代号。

【讨论】:

  • 不幸的是蜘蛛猴在上游死了。
猜你喜欢
  • 1970-01-01
  • 2011-03-23
  • 2023-03-26
  • 2011-05-19
  • 2010-11-10
  • 1970-01-01
  • 1970-01-01
  • 2014-01-30
  • 2011-09-06
相关资源
最近更新 更多