标签归档:javascript

WebDriver click()与JavaScript click()

问题:WebDriver click()与JavaScript click()

故事:

在StackOverflow上,我看到用户报告他们无法通过selenium WebDriver“单击”命令单击元素,并且可以通过执行脚本来解决JavaScript单击问题。

Python中的示例:

element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)

WebDriverJS /量角器中的示例:

var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());

问题:

为什么在常规WebDriver单击不起作用时单击“通过JavaScript”有效?这到底是什么时候发生的,这种解决方法(如果有)的缺点是什么?

我个人使用此变通办法时并未完全理解为什么必须这样做以及它可能导致什么问题。

The Story:

Here on StackOverflow, I’ve seen users reporting that they cannot click an element via selenium WebDriver “click” command and can work around it with a JavaScript click by executing a script.

Example in Python:

element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)

Example in WebDriverJS/Protractor:

var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());

The Question:

Why is clicking “via JavaScript” works when a regular WebDriver click does not? When exactly is this happening and what is the downside of this workaround (if any)?

I personally used this workaround without fully understanding why I have to do it and what problems it can lead to.


回答 0

当前接受的答案所暗示的相反,关于PhantomJS并没有特定于WebDriver单击和使用JavaScript的区别。

区别

两种方法之间的本质区别是所有浏览器都共有的,可以很简单地解释一下:

  • WebDriver:当WebDriver进行单击时,它会尽可能地尝试模拟真实用户使用浏览器时发生的情况。假设您有一个元素A,它是一个表示“单击我”的按钮,一个元素B是一个div透明的元素,但具有其尺寸和zIndex设置,使其完全覆盖A。然后,您告诉WebDriver单击A。WebDriver将模拟点击,使B 首先获得点击。为什么?因为B涵盖了A,并且如果用户尝试单击A,则B将首先获得该事件。A是否最终会获得click事件取决于B如何处理该事件。无论如何,在这种情况下,WebDriver的行为与真实用户尝试单击A时的行为相同。

  • JavaScript:现在,假设您使用JavaScript来做A.click()这种单击方法不能重现用户尝试单击A时实际发生的情况。JavaScript将click事件直接发送给A,而B将不会获得任何事件。

为什么在WebDriver单击不起作用时JavaScript单击有效?

正如我上面提到的,WebDriver会尽可能地模拟真实用户使用浏览器时发生的情况。事实是,DOM可以包含用户无法与之交互的元素,并且WebDriver不允许您单击这些元素。除了我提到的重叠情况之外,这还意味着无法单击不可见元素。我在“堆栈溢出”问题中看到的一个常见案例是某人试图与DOM中已经存在的GUI元素进行交互,但只有在处理了其他某些元素后才可见。有时在下拉菜单中会发生这种情况:您必须先单击按钮,然后弹出菜单,然后才能选择菜单项。如果有人尝试在菜单可见之前单击菜单项,如果此人随后尝试使用JavaScript进行操作,那么它将起作用,因为事件是直接传递给元素的,而与可见性无关。

什么时候应该使用JavaScript进行点击?

如果您使用Selenium来测试应用程序,那么我对这个问题的回答是“几乎不会”。总的来说,您的Selenium测试应该重现用户对浏览器的操作。以下拉菜单为例:测试应单击首先显示下拉菜单的按钮,然后单击菜单项。如果由于按钮不可见而导致GUI出现问题,或者按钮无法显示菜单项或类似内容,则测试将失败并且您将检测到该错误。如果使用JavaScript单击鼠标,则将无法通过自动测试检测到这些错误。

我说“几乎从不”是因为使用JavaScript可能会有exceptions。但是,它们应该很少见。

如果您使用Selenium 抓取站点,则尝试重现用户行为并不那么重要。因此,使用JavaScript绕过GUI并不是什么大问题。

Contrarily to what the currently accepted answer suggests, there’s nothing specific to PhantomJS when it comes to the difference between having WebDriver do a click and doing it in JavaScript.

The Difference

The essential difference between the two methods is common to all browsers and can be explained pretty simply:

  • WebDriver: When WebDriver does the click, it attempts as best as it can to simulate what happens when a real user uses the browser. Suppose you have an element A which is a button that says “Click me” and an element B which is a div element which is transparent but has its dimensions and zIndex set so that it completely covers A. Then you tell WebDriver to click A. WebDriver will simulate the click so that B receives the click first. Why? Because B covers A, and if a user were to try to click on A, then B would get the event first. Whether or not A would eventually get the click event depends on how B handles the event. At any rate, the behavior with WebDriver in this case is the same as when a real user tries to click on A.

  • JavaScript: Now, suppose you use JavaScript to do A.click(). This method of clicking does not reproduce what really happens when the user tries to click A. JavaScript sends the click event directly to A, and B will not get any event.

Why a JavaScript Click Works When a WebDriver Click Does Not?

As I mentioned above WebDriver will try to simulate as best it can what happens when a real user is using a browser. The fact of the matter is that the DOM can contain elements that a user cannot interact with, and WebDriver won’t allow you to click on these element. Besides the overlapping case I mentioned, this also entails that invisible elements cannot be clicked. A common case I see in Stack Overflow questions is someone who is trying to interact with a GUI element that already exists in the DOM but becomes visible only when some other element has been manipulated. This sometimes happens with dropdown menus: you have to first click on the button the brings up the dropdown before a menu item can be selected. If someone tries to click the menu item before the menu is visible, WebDriver will balk and say that the element cannot be manipulated. If the person then tries to do it with JavaScript, it will work because the event is delivered directly to the element, irrespective of visibility.

When Should You Use JavaScript for Clicking?

If you are using Selenium for testing an application, my answer to this question is “almost never”. By and large, your Selenium test should reproduce what a user would do with the browser. Taking the example of the drop down menu: a test should click on the button that brings up the drop down first, and then click on the menu item. If there is a problem with the GUI because the button is invisible, or the button fails to show the menu items, or something similar, then your test will fail and you’ll have detected the bug. If you use JavaScript to click around, you won’t be able to detect these bugs through automated testing.

I say “almost never” because there may be exceptions where it makes sense to use JavaScript. They should be very rare, though.

If you are using Selenium for scraping sites, then it is not as critical to attempt to reproduce user behavior. So using JavaScript to bypass the GUI is less of an issue.


回答 1

驱动程序执行的单击尝试在JavaScript HTMLElement.click()click事件执行默认操作时,即使元素不可交互,也尽可能模拟真实用户的行为。

不同之处在于:

  • 驱动程序通过将元素滚动到视图中来确保该元素可见,并检查该元素是否可交互

    驱动程序将引发错误:

    • 当点击坐标顶部的元素不是目标元素或后代时
    • 当元素没有正尺寸或完全透明时
    • 当元素是禁用的输入或按​​钮(属性/属性disabledtrue)时
    • 当元素的鼠标指针被禁用(CSS pointer-eventsnone)时


    HTMLElement.click()如果元素被禁用, JavaScript 将始终执行默认操作,或者至多只会静默失败。

  • 如果元素是可聚焦的,则希望驾驶员将其聚焦。

    JavaScript HTMLElement.click()不会。

  • 希望驱动程序像真实用户一样发出所有事件(mousemove,mousedown,mouseup,click等)。

    JavaScript HTMLElement.click()仅发出click事件。该页面可能依赖于这些额外的事件,并且如果未发出这些事件,它们的行为可能会有所不同。

    这些是驱动程序发出的单击Chrome的事件:

    mouseover {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousemove {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousedown {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mouseup {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    click {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }

    这是JavaScript注入发出的事件:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • JavaScript发出的事件.click() 不受信任,并且默认操作不能被调用:

    https://developer.mozilla.org/zh/docs/Web/API/Event/isTrusted
    https://googlechrome.github.io/samples/event-istrusted/index.html

    请注意,某些驱动程序仍在生成不受信任的事件。从2.1版开始,PhantomJS就是这种情况。

  • JavaScript发出的事件.click() 没有click的坐标

    属性clientX, clientY, screenX, screenY, layerX, layerY设置为0。该页面可能依赖于它们,并且可能会有所不同。


可以使用JavaScript .click()抓取一些数据,但这不是在测试环境中。由于它无法模拟用户的行为,因此无法达到测试的目的。因此,如果来自驱动程序的单击失败,则实际用户很可能也将在相同条件下无法执行相同的单击。


是什么使驱动程序在我们期望成功时无法单击元素?

  • 由于延迟或过渡效果,目标元素尚不可见/不可交互。

    一些例子 :

    https://developer.mozilla.org/fr/docs/Web(下拉导航菜单) http://materializecss.com/side-nav.html(下拉侧栏)

    工作环境:

    添加服务员以等待可见性,最小尺寸或稳定位置:

    // wait visible
    browser.wait(ExpectedConditions.visibilityOf(elem), 5000);
    
    // wait visible and not disabled
    browser.wait(ExpectedConditions.elementToBeClickable(elem), 5000);
    
    // wait for minimum width
    browser.wait(function minimumWidth() {
        return elem.getSize().then(size => size.width > 50);
    }, 5000);

    重试单击直到成功:

    browser.wait(function clickSuccessful() {
        return elem.click().then(() => true, (ex) => false);
    }, 5000);

    添加与动画/过渡的持续时间匹配的延迟:

    browser.sleep(250);


  • 目标元素最终被浮动元素覆盖一旦滚动到视图中:

    驱动程序自动将元素滚动到视图中以使其可见。如果页面包含浮动/粘滞元素(菜单,广告,页脚,通知,Cookie策略..),则该元素可能最终被覆盖,将不再可见/不可交互。

    示例:https//twitter.com/?lang = zh-CN

    解决方法:

    将窗口的大小设置为较大的大小,以避免滚动或浮动元素。

    Y将鼠标移到具有负偏移量的元素上,然后单击它:

      browser.actions()
         .mouseMove(elem, {x: 0, y: -250})
         .click()
         .perform();

    单击之前,将元素滚动到窗口的中心:

    browser.executeScript(function scrollCenter(elem) {
      var win = elem.ownerDocument.defaultView || window,
        box = elem.getBoundingClientRect(),
        dy = box.top - (win.innerHeight - box.height) / 2;
      win.scrollTo(win.pageXOffset, win.pageYOffset + dy);
    }, element);
    
    element.click();

    如果无法避免,请隐藏浮动元素:

    browser.executeScript(function scrollCenter(elem) {
      elem.style.display = 'none';
    }, element);

The click executed by the driver tries to simulate the behavior of a real user as close as possible while the JavaScript HTMLElement.click() performs the default action for the click event, even if the element is not interactable.

The differences are:

  • The driver ensures that the element is visible by scrolling it into the view and checks that the element is interactable.

    The driver will raise an error:

    • when the element on top at the coordinates of the click is not the targeted element or a descendant
    • when the element doesn’t have a positive size or if it is fully transparent
    • when the element is a disabled input or button (attribute/property disabled is true)
    • when the element has the mouse pointer disabled (CSS pointer-events is none)


    A JavaScript HTMLElement.click() will always perform the default action or will at best silently fail if the element is a disabled.

  • The driver is expected to bring the element into focus if it is focusable.

    A JavaScript HTMLElement.click() won’t.

  • The driver is expected to emit all the events (mousemove, mousedown, mouseup, click, …) just like like a real user.

    A JavaScript HTMLElement.click() emits only the click event. The page might rely on these extra events and might behave differently if they are not emitted.

    These are the events emitted by the driver for a click with Chrome:

    mouseover {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousemove {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousedown {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mouseup {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    click {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    

    And this is the event emitted with a JavaScript injection:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
    
  • The event emitted by a JavaScript .click() is not trusted and the default action may not be invoked:

    https://developer.mozilla.org/en/docs/Web/API/Event/isTrusted
    https://googlechrome.github.io/samples/event-istrusted/index.html

    Note that some of the drivers are still generating untrusted events. This is the case with PhantomJS as of version 2.1.

  • The event emitted by a JavaScript .click() doesn’t have the coordinates of the click.

    The properties clientX, clientY, screenX, screenY, layerX, layerY are set to 0. The page might rely on them and might behave differently.


It may be ok to use a JavaScript .click() to scrap some data, but it is not in a testing context. It defeats the purpose of the test since it doesn’t simulate the behavior of a user. So, if the click from the driver fails, then a real user will most likely also fail to perform the same click in the same conditions.


What makes the driver fail to click an element when we expect it to succeed?

  • The targeted element is not yet visible/interactable due to a delay or a transition effect.

    Some examples :

    https://developer.mozilla.org/fr/docs/Web (dropdown navigation menu) http://materializecss.com/side-nav.html (dropdown side bar)

    Workarrounds:

    Add a waiter to wait for the visibility, a minimum size or a steady position :

    // wait visible
    browser.wait(ExpectedConditions.visibilityOf(elem), 5000);
    
    // wait visible and not disabled
    browser.wait(ExpectedConditions.elementToBeClickable(elem), 5000);
    
    // wait for minimum width
    browser.wait(function minimumWidth() {
        return elem.getSize().then(size => size.width > 50);
    }, 5000);
    

    Retry to click until it succeeds :

    browser.wait(function clickSuccessful() {
        return elem.click().then(() => true, (ex) => false);
    }, 5000);
    

    Add a delay matching the duration of the animation/transition :

    browser.sleep(250);
    


  • The targeted element ends-up covered by a floating element once scrolled into the view:

    The driver automatically scrolls the element into the view to make it visible. If the page contains a floating/sticky element (menu, ads, footer, notification, cookie policy..), the element may end-up covered and will no longer be visible/interactable.

    Example: https://twitter.com/?lang=en

    Workarounds:

    Set the size of the window to a larger one to avoid the scrolling or the floating element.

    Mover over the element with a negative Y offset and then click it:

      browser.actions()
         .mouseMove(elem, {x: 0, y: -250})
         .click()
         .perform();
    

    Scroll the element to the center of the window before the click:

    browser.executeScript(function scrollCenter(elem) {
      var win = elem.ownerDocument.defaultView || window,
        box = elem.getBoundingClientRect(),
        dy = box.top - (win.innerHeight - box.height) / 2;
      win.scrollTo(win.pageXOffset, win.pageYOffset + dy);
    }, element);
    
    element.click();
    

    Hide the floating element if it can’t be avoided:

    browser.executeScript(function scrollCenter(elem) {
      elem.style.display = 'none';
    }, element);
    

回答 2

注意:我们将“点击”称为最终用户点击。“ js click”是通过JS点击的

为什么在常规WebDriver单击不起作用时单击“通过JavaScript”有效?

有两种情况会发生这种情况:

I. 如果您正在使用PhamtomJS

这是的最常见的已知行为PhantomJS。例如,某些元素有时不可单击<div>。这是因为PhantomJS最初是用来模拟浏览器引擎的(例如初始HTML + CSS->计算CSS->呈现)。但这并不意味着要以最终用户的方式进行交互(查看,单击,拖动)。因此PhamtomJS,最终用户交互仅部分支持。

JS为什么点击工作?至于任一点击,它们都是平均点击。它就像带有1个枪管2个扳机的枪。视口中的一个,JS中的一个。由于可以PhamtomJS很好地模拟浏览器的引擎,因此JS单击应该可以很好地工作。

二。“ click”的事件处理程序必须在糟糕的时间内绑定。

例如,我们得到了一个 <div>

  • ->我们做一些计算

  • ->然后将click事件绑定到<div>

  • ->加上一些错误的角度编码(例如,不能正确处理示波器的周期)

我们可能最终得到相同的结果。单击将不起作用,因为WebdriverJS在没有单击事件处理程序的情况下会尝试单击该元素。

JS为什么点击工作?JS单击就像将JS直接注入浏览器。可能有2种方式,

拳头是通过devtools控制台(是的,WebdriverJS确实与devtools的控制台进行通信)。

其次是将<script>标记直接注入HTML。

对于每个浏览器,其行为将有所不同。但是无论如何,这些方法比单击按钮更复杂。Click正在使用已经存在的内容(最终用户单击),js click正在通过后门。

对于js,单击将显示为异步任务。这与一个复杂的主题“ 浏览器异步任务和CPU任务调度 ”相关(一会儿再读也找不到文章)。简而言之,这主要是因为js click需要等待CPU任务调度周期,并且在click事件绑定后运行速度会变慢。 (当您发现元素有时可单击,有时却不可单击时,您可能会知道这种情况。)

这到底是什么时候发生的,这种解决方法(如果有)的缺点是什么?

=>如上所述,两者都意味着一个目的,但是关于使用哪个入口:

  • 点击:使用默认情况下提供的浏览器。
  • JS click:正在通过后门。

=>对于性能,很难说,因为它依赖于浏览器。但通常:

  • 单击:并不意味着更快,而只是在CPU执行任务的计划列表中签名更高的位置。
  • JS单击:并不意味着速度较慢,而仅是它登录到CPU任务计划列表的最后位置。

=>缺点:

  • 点击:除了您使用PhamtomJS之外,似乎没有其他缺点。
  • JS单击:对健康非常不利。您可能不小心单击了视图中没有的内容。使用此功能时,请确保该元素在那里并且可供查看,然后单击以作为最终用户的观点。

PS,如果您正在寻找解决方案。

  • 使用PhantomJS?我建议改用无头的Chrome。是的,您可以在Ubuntu上无头设置Chrome。事物的运行方式与Chrome一样,但它没有视图,并且像PhantomJS一样没有虫子。
  • 不使用PhamtomJS但仍然有问题吗?我建议使用带有browser.wait()(分度器的ExpectedCondition)(检查此以获得更多信息

(我想简短一点,但结局很糟。与理论有关的任何事情都难以解释…)

NOTE: let’s call ‘click’ is end-user click. ‘js click’ is click via JS

Why is clicking “via JavaScript” works when a regular WebDriver click does not?

There are 2 cases for this to happen:

I. If you are using PhamtomJS

Then this is the most common known behavior of PhantomJS . Some elements are sometimes not clickable, for example <div>. This is because PhantomJS was original made for simulating the engine of browsers (like initial HTML + CSS -> computing CSS -> rendering). But it does not mean to be interacted with as an end user’s way (viewing, clicking, dragging). Therefore PhamtomJS is only partially supported with end-users interaction.

WHY DOES JS CLICK WORK? As for either click, they are all mean click. It is like a gun with 1 barrel and 2 triggers. One from the viewport, one from JS. Since PhamtomJS great in simulating browser’s engine, a JS click should work perfectly.

II. The event handler of “click” got to bind in the bad period of time.

For example, we got a <div>

  • -> We do some calculation

  • -> then we bind event of click to the <div>.

  • -> Plus with some bad coding of angular (e.g. not handling scope’s cycle properly)

We may end up with the same result. Click won’t work, because WebdriverJS trying to click on the element when it has no click event handler.

WHY DOES JS CLICK WORK? Js click is like injecting js directly into the browser. Possible with 2 ways,

Fist is through devtools console (yes, WebdriverJS does communicate with devtools’ console).

Second is inject a <script> tag directly into HTML.

For each browser, the behavior will be different. But regardless, these methods are more complicating than clicking on the button. Click is using what already there (end-users click), js click is going through backdoor.

And for js click will appear to be an asynchronous task. This is related a with a kinda complex topic of ‘browser asynchronous task and CPU task scheduling‘ (read it a while back can’t find the article again). For short this will mostly result as js click will need to wait for a cycle of task scheduling of CPU and it will be ran a bit slower after the binding of the click event. (You could know this case when you found the element sometimes clickable, sometimes not. )

When exactly is this happening and what is the downside of this workaround (if any)?

=> As mention above, both mean for one purpose, but about using which entrance:

  • Click: is using what providing by default of browser.
  • JS click: is going through backdoor.

=> For performance, it is hard to say because it relies on browsers. But generally:

  • Click: doesn’t mean faster but only signed higher position in schedule list of CPU execution task.
  • JS click: doesn’t mean slower but only it signed into the last position of schedule list of CPU task.

=> Downsides:

  • Click: doesn’t seem to have any downside except you are using PhamtomJS.
  • JS click: very bad for health. You may accidentally click on something that doesn’t there on the view. When you use this, make sure the element is there and available to view and click as the point of view of end-user.

P.S. if you are looking for a solution.

  • Using PhantomJS? I will suggest using Chrome headless instead. Yes, you can set up Chrome headless on Ubuntu. Thing runs just like Chrome but it only does not have a view and less buggy like PhantomJS.
  • Not using PhamtomJS but still having problems? I will suggest using ExpectedCondition of Protractor with browser.wait() (check this for more information)

(I want to make it short, but ended up badly. Anything related with theory is complicated to explain…)


如何在模板中将数据从Flask传递到JavaScript?

问题:如何在模板中将数据从Flask传递到JavaScript?

我的应用程序调用返回字典的API。我想将信息从此字典传递到视图中的JavaScript。具体来说,我在JS中使用Google Maps API,因此我希望向其传递一个包含长/短信息的元组列表。我知道render_template会将这些变量传递给视图,以便可以在HTML中使用它们,但是如何将它们传递给模板中的JavaScript?

from flask import Flask
from flask import render_template

app = Flask(__name__)

import foo_api

api = foo_api.API('API KEY')

@app.route('/')
def get_data():
    events = api.call(get_event, arg0, arg1)
    geocode = event['latitude'], event['longitude']
    return render_template('get_data.html', geocode=geocode)

My app makes a call to an API that returns a dictionary. I want to pass information from this dict to JavaScript in the view. I am using the Google Maps API in the JS, specifically, so I’d like to pass it a list of tuples with the long/lat information. I know that render_template will pass these variables to the view so they can be used in HTML, but how could I pass them to JavaScript in the template?

from flask import Flask
from flask import render_template

app = Flask(__name__)

import foo_api

api = foo_api.API('API KEY')

@app.route('/')
def get_data():
    events = api.call(get_event, arg0, arg1)
    geocode = event['latitude'], event['longitude']
    return render_template('get_data.html', geocode=geocode)

回答 0

您可以{{ variable }}在模板的任何地方使用,而不仅限于HTML部分。所以这应该工作:

<html>
<head>
  <script>
    var someJavaScriptVar = '{{ geocode[1] }}';
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: {{ geocode[0] }} ' + someJavaScriptVar)" />
</body>
</html>

可以将其视为两个阶段的过程:首先,Jinja(Flask使用的模板引擎)生成文本输出。这将发送给执行他所看到的JavaScript的用户。如果希望Flask变量作为数组在JavaScript中可用,则必须在输出中生成数组定义:

<html>
  <head>
    <script>
      var myGeocode = ['{{ geocode[0] }}', '{{ geocode[1] }}'];
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Jinja还提供了来自Python的更高级的构造,因此您可以将其缩短为:

<html>
<head>
  <script>
    var myGeocode = [{{ ', '.join(geocode) }}];
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
</body>
</html>

您还可以使用for循环,if语句等,请参阅Jinja2文档以获取更多信息。

另外,看看福特的答案,谁指出了tojson过滤器,它是对Jinja2标准过滤器集的补充。

编辑2018年11月:tojson现在已包含在Jinja2的标准过滤器集中。

You can use {{ variable }} anywhere in your template, not just in the HTML part. So this should work:

<html>
<head>
  <script>
    var someJavaScriptVar = '{{ geocode[1] }}';
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: {{ geocode[0] }} ' + someJavaScriptVar)" />
</body>
</html>

Think of it as a two-stage process: First, Jinja (the template engine Flask uses) generates your text output. This gets sent to the user who executes the JavaScript he sees. If you want your Flask variable to be available in JavaScript as an array, you have to generate an array definition in your output:

<html>
  <head>
    <script>
      var myGeocode = ['{{ geocode[0] }}', '{{ geocode[1] }}'];
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Jinja also offers more advanced constructs from Python, so you can shorten it to:

<html>
<head>
  <script>
    var myGeocode = [{{ ', '.join(geocode) }}];
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
</body>
</html>

You can also use for loops, if statements and many more, see the Jinja2 documentation for more.

Also, have a look at Ford’s answer who points out the tojson filter which is an addition to Jinja2’s standard set of filters.

Edit Nov 2018: tojson is now included in Jinja2’s standard set of filters.


回答 1

将几乎所有Python对象都转换为JavaScript对象的理想方法是使用JSON。JSON非常适合作为系统之间传输的格式,但有时我们会忘记它代表JavaScript Object Notation。这意味着将JSON注入模板与注入描述对象的JavaScript代码相同。

Flask为此提供了一个Jinja过滤器:tojson将结构转储到JSON字符串并标记为安全,以便Jinja不会自动转义它。

<html>
  <head>
    <script>
      var myGeocode = {{ geocode|tojson }};
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

这适用于JSON可序列化的任何Python结构:

python_data = {
    'some_list': [4, 5, 6],
    'nested_dict': {'foo': 7, 'bar': 'a string'}
}
var data = {{ python_data|tojson }};
alert('Data: ' + data.some_list[1] + ' ' + data.nested_dict.foo + 
      ' ' + data.nested_dict.bar);

The ideal way to go about getting pretty much any Python object into a JavaScript object is to use JSON. JSON is great as a format for transfer between systems, but sometimes we forget that it stands for JavaScript Object Notation. This means that injecting JSON into the template is the same as injecting JavaScript code that describes the object.

Flask provides a Jinja filter for this: tojson dumps the structure to a JSON string and marks it safe so that Jinja does not autoescape it.

<html>
  <head>
    <script>
      var myGeocode = {{ geocode|tojson }};
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

This works for any Python structure that is JSON serializable:

python_data = {
    'some_list': [4, 5, 6],
    'nested_dict': {'foo': 7, 'bar': 'a string'}
}
var data = {{ python_data|tojson }};
alert('Data: ' + data.some_list[1] + ' ' + data.nested_dict.foo + 
      ' ' + data.nested_dict.bar);

回答 2

在HTML元素上使用data属性可以避免使用内联脚本,这反过来意味着您可以使用更严格的CSP规则来提高安全性。

指定数据属性,如下所示:

<div id="mydiv" data-geocode='{{ geocode|tojson }}'>...</div>

然后像下面这样在静态JavaScript文件中进行访问:

// Raw JavaScript
var geocode = JSON.parse(document.getElementById("mydiv").dataset.geocode);

// jQuery
var geocode = JSON.parse($("#mydiv").data("geocode"));

Using a data attribute on an HTML element avoids having to use inline scripting, which in turn means you can use stricter CSP rules for increased security.

Specify a data attribute like so:

<div id="mydiv" data-geocode='{{ geocode|tojson }}'>...</div>

Then access it in a static JavaScript file like so:

// Raw JavaScript
var geocode = JSON.parse(document.getElementById("mydiv").dataset.geocode);

// jQuery
var geocode = JSON.parse($("#mydiv").data("geocode"));

回答 3

另外,您可以添加一个端点以返回变量:

@app.route("/api/geocode")
def geo_code():
    return jsonify(geocode)

然后执行XHR检索它:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })

Alternatively you could add an endpoint to return your variable:

@app.route("/api/geocode")
def geo_code():
    return jsonify(geocode)

Then do an XHR to retrieve it:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })

回答 4

对于那些想要将变量传递给使用flask来源的脚本的人来说,这是另一种替代解决方案,我只能通过在外部定义变量,然后按如下所示调用脚本来设法使它起作用:

    <script>
    var myfileuri = "/static/my_csv.csv"
    var mytableid = 'mytable';
    </script>
    <script type="text/javascript" src="/static/test123.js"></script>

如果我在test123.js其中输入jinja变量不起作用,则会收到错误消息。

Just another alternative solution for those who want to pass variables to a script which is sourced using flask, I only managed to get this working by defining the variables outside and then calling the script as follows:

    <script>
    var myfileuri = "/static/my_csv.csv"
    var mytableid = 'mytable';
    </script>
    <script type="text/javascript" src="/static/test123.js"></script>

If I input jinja variables in test123.js it doesn’t work and you will get an error.


回答 5

已经给出了有效的答案,但是我想添加一个检查,以在烧瓶变量不可用的情况下充当故障保险。使用时:

var myVariable = {{ flaskvar | tojson }};

如果存在导致变量不存在的错误,则导致的错误可能会产生意外的结果。为避免这种情况:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}

Working answers are already given but I want to add a check that acts as a fail-safe in case the flask variable is not available. When you use:

var myVariable = {{ flaskvar | tojson }};

if there is an error that causes the variable to be non existent, resulting errors may produce unexpected results. To avoid this:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}

回答 6

<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

这使用jinja2将地理编码元组转换为json字符串,然后javascript JSON.parse将其转换为javascript数组。

<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

This uses jinja2 to turn the geocode tuple into a json string, and then the javascript JSON.parse turns that into a javascript array.


回答 7

好吧,我有一个棘手的方法来完成这项工作。这个想法如下

<label>, <p>, <input>在HTML主体中制作一些不可见的HTML标记(如etc),并在标记ID中创建模式,例如,在标记ID中使用列表索引,在标记类名称中使用列表值。

在这里,我有两个长度相同的清单maintenance_next []和maintenance_block_time []。我想使用烧瓶将这两个列表的数据传递给javascript。因此,我采取了一些不可见的标签标记并将其标记名称设置为列表索引的模式,并将其类名称设置为index的值。

{% for i in range(maintenance_next|length): %}
<label id="maintenance_next_{{i}}" name="{{maintenance_next[i]}}" style="display: none;"></label>
<label id="maintenance_block_time_{{i}}" name="{{maintenance_block_time[i]}}" style="display: none;"></label>
{% endfor%}

之后,我使用一些简单的javascript操作检索javascript中的数据。

<script>
var total_len = {{ total_len }};

for (var i = 0; i < total_len; i++) {
    var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
    var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
    
    //Do what you need to do with tm1 and tm2.
    
    console.log(tm1);
    console.log(tm2);
}
</script>

Well, I have a tricky method for this job. The idea is as follow-

Make some invisible HTML tags like <label>, <p>, <input> etc. in HTML body and make a pattern in tag id, for example, use list index in tag id and list value at tag class name.

Here I have two lists maintenance_next[] and maintenance_block_time[] of the same length. I want to pass these two list’s data to javascript using the flask. So I take some invisible label tag and set its tag name is a pattern of list index and set its class name as value at index.

{% for i in range(maintenance_next|length): %}
<label id="maintenance_next_{{i}}" name="{{maintenance_next[i]}}" style="display: none;"></label>
<label id="maintenance_block_time_{{i}}" name="{{maintenance_block_time[i]}}" style="display: none;"></label>
{% endfor%}

After this, I retrieve the data in javascript using some simple javascript operation.

<script>
var total_len = {{ total_len }};

for (var i = 0; i < total_len; i++) {
    var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
    var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
    
    //Do what you need to do with tm1 and tm2.
    
    console.log(tm1);
    console.log(tm2);
}
</script>

回答 8

一些js文件来自网络或库,它们不是您自己编写的。他们获得的代码如下所示:

var queryString = document.location.search.substring(1);
var params = PDFViewerApplication.parseQueryString(queryString);
var file = 'file' in params ? params.file : DEFAULT_URL;

此方法使js文件保持不变(保持独立性),并正确传递变量!

Some js files come from the web or library, they are not written by yourself. The code they get variable like this:

var queryString = document.location.search.substring(1);
var params = PDFViewerApplication.parseQueryString(queryString);
var file = 'file' in params ? params.file : DEFAULT_URL;

This method makes js files unchanged(keep independence), and pass variable correctly!


像python这样的javascript中有字典吗?

问题:像python这样的javascript中有字典吗?

我需要像这样用javascript制作字典

我不记得确切的符号了,但是有点像:

states_dictionary={ CT=[alex,harry], AK=[liza,alex], TX=[fred, harry] ........ }

javascript中有这样的事情吗?

i need to make a dictionary in javascript like this

i dont remember the exact notation, but it was something like:

states_dictionary={ CT=[alex,harry], AK=[liza,alex], TX=[fred, harry] ........ }

is there such a thing in javascript?


回答 0

这是一篇旧文章,但我认为无论如何我都应该提供一个图解的答案。

使用javascript的对象符号。像这样:

states_dictionary={ 
     "CT":["alex","harry"], 
     "AK":["liza","alex"], 
     "TX":["fred", "harry"]
};

并访问值:

states_dictionary.AK[0] //which is liza

或者您可以使用javascript文字对象表示法,其中键不需要用引号引起来:

states_dictionary={ 
     CT:["alex","harry"], 
     AK:["liza","alex"], 
     TX:["fred", "harry"]
};

This is an old post, but I thought I should provide an illustrated answer anyway.

Use javascript’s object notation. Like so:

states_dictionary={ 
     "CT":["alex","harry"], 
     "AK":["liza","alex"], 
     "TX":["fred", "harry"]
};

And to access the values:

states_dictionary.AK[0] //which is liza

or you can use javascript literal object notation, whereby the keys not require to be in quotes:

states_dictionary={ 
     CT:["alex","harry"], 
     AK:["liza","alex"], 
     TX:["fred", "harry"]
};

回答 1

直到2015年(ECMAScript 6发行版),Javascript中都没有真正的关联数组。从那时起,您可以将Map对象用作Robocat状态。在MDN中查找详细信息。例:

let map = new Map();
map.set('key', {'value1', 'value2'});
let values = map.get('key');

如果不支持ES6,则可以尝试使用对象:

var x = new Object();
x["Key"] = "Value";

但是,对于对象,无法使用典型的数组属性或方法,例如array.length。至少可以在for-in-loop中访问“对象数组”。

There were no real associative arrays in Javascript until 2015 (release of ECMAScript 6). Since then you can use the Map object as Robocat states. Look up the details in MDN. Example:

let map = new Map();
map.set('key', {'value1', 'value2'});
let values = map.get('key');

Without support for ES6 you can try using objects:

var x = new Object();
x["Key"] = "Value";

However with objects it is not possible to use typical array properties or methods like array.length. At least it is possible to access the “object-array” in a for-in-loop.


回答 2

我意识到这是一个古老的问题,但是当您搜索“ javascript词典”时,它会在Google中弹出,因此我想在上述答案中加上ECMAScript 6中Map引入的官方对象,它是字典实施:

var dict = new Map();
dict.set("foo", "bar");

//returns "bar"
dict.get("foo");

与javascript的普通对象不同,它允许任何对象作为键:

var foo = {};
var bar = {};
var dict = new Map();
dict.set(foo, "Foo");
dict.set(bar, "Bar");

//returns "Bar"
dict.get(bar);

//returns "Foo"
dict.get(foo);

//returns undefined, as {} !== foo and {} !== bar
dict.get({});

I realize this is an old question, but it pops up in Google when you search for ‘javascript dictionaries’, so I’d like to add to the above answers that in ECMAScript 6, the official Map object has been introduced, which is a dictionary implementation:

var dict = new Map();
dict.set("foo", "bar");

//returns "bar"
dict.get("foo");

Unlike javascript’s normal objects, it allows any object as a key:

var foo = {};
var bar = {};
var dict = new Map();
dict.set(foo, "Foo");
dict.set(bar, "Bar");

//returns "Bar"
dict.get(bar);

//returns "Foo"
dict.get(foo);

//returns undefined, as {} !== foo and {} !== bar
dict.get({});

回答 3

在JS中创建了一个简单的字典:

function JSdict() {
    this.Keys = [];
    this.Values = [];
}

// Check if dictionary extensions aren't implemented yet.
// Returns value of a key
if (!JSdict.prototype.getVal) {
    JSdict.prototype.getVal = function (key) {
        if (key == null) {
            return "Key cannot be null";
        }
        for (var i = 0; i < this.Keys.length; i++) {
            if (this.Keys[i] == key) {
                return this.Values[i];
            }
        }
        return "Key not found!";
    }
}


// Check if dictionary extensions aren't implemented yet.
// Updates value of a key
if (!JSdict.prototype.update) {
    JSdict.prototype.update = function (key, val) {
        if (key == null || val == null) {
            return "Key or Value cannot be null";
        }
        // Verify dict integrity before each operation
        if (keysLength != valsLength) {
            return "Dictionary inconsistent. Keys length don't match values!";
        }
        var keysLength = this.Keys.length;
        var valsLength = this.Values.length;
        var flag = false;
        for (var i = 0; i < keysLength; i++) {
            if (this.Keys[i] == key) {
                this.Values[i] = val;
                flag = true;
                break;
            }
        }
        if (!flag) {
            return "Key does not exist";
        }
    }
}



// Check if dictionary extensions aren't implemented yet.
// Adds a unique key value pair
if (!JSdict.prototype.add) {
    JSdict.prototype.add = function (key, val) {
        // Allow only strings or numbers as keys
        if (typeof (key) == "number" || typeof (key) == "string") {
            if (key == null || val == null) {
                return "Key or Value cannot be null";
            }
            if (keysLength != valsLength) {
                return "Dictionary inconsistent. Keys length don't match values!";
            }
            var keysLength = this.Keys.length;
            var valsLength = this.Values.length;
            for (var i = 0; i < keysLength; i++) {
                if (this.Keys[i] == key) {
                    return "Duplicate keys not allowed!";
                }
            }
            this.Keys.push(key);
            this.Values.push(val);
        }
        else {
            return "Only number or string can be key!";
        }
    }
}

// Check if dictionary extensions aren't implemented yet.
// Removes a key value pair
if (!JSdict.prototype.remove) {
    JSdict.prototype.remove = function (key) {
        if (key == null) {
            return "Key cannot be null";
        }
        if (keysLength != valsLength) {
            return "Dictionary inconsistent. Keys length don't match values!";
        }
        var keysLength = this.Keys.length;
        var valsLength = this.Values.length;
        var flag = false;
        for (var i = 0; i < keysLength; i++) {
            if (this.Keys[i] == key) {
                this.Keys.shift(key);
                this.Values.shift(this.Values[i]);
                flag = true;
                break;
            }
        }
        if (!flag) {
            return "Key does not exist";
        }
    }
}

上面的实现现在可以用于模拟字典,如下所示:

var dict = new JSdict();

dict.add(1, "one")

dict.add(1, "one more")
"Duplicate keys not allowed!"

dict.getVal(1)
"one"

dict.update(1, "onne")

dict.getVal(1)
"onne"

dict.remove(1)

dict.getVal(1)
"Key not found!"

这只是一个基本的模拟。可以通过实施更好的运行时间算法以使其至少达到O(nlogn)时间复杂度甚至更低的复杂度来对其进行进一步优化。像对数组进行合并/快速排序,然后对查询进行一些B搜索。我没有尝试或搜索过有关在JS中映射哈希函数的信息。

另外,可以将JSdict obj的键和值转换为私有变量,以防止偷偷摸摸。

希望这可以帮助!

编辑>>完成上述操作后,我个人使用了JS对象作为现成可用的关联数组。

但是,我想特别提到两种方法,它们实际上被证明有助于使它成为方便的哈希表体验。

即:dict.hasOwnProperty(key) 删除dict [key]

阅读这篇文章,作为有关此实现/用法的好资源。 在JavaScript关联数组中动态创建键

谢谢!

Have created a simple dictionary in JS here:

function JSdict() {
    this.Keys = [];
    this.Values = [];
}

// Check if dictionary extensions aren't implemented yet.
// Returns value of a key
if (!JSdict.prototype.getVal) {
    JSdict.prototype.getVal = function (key) {
        if (key == null) {
            return "Key cannot be null";
        }
        for (var i = 0; i < this.Keys.length; i++) {
            if (this.Keys[i] == key) {
                return this.Values[i];
            }
        }
        return "Key not found!";
    }
}


// Check if dictionary extensions aren't implemented yet.
// Updates value of a key
if (!JSdict.prototype.update) {
    JSdict.prototype.update = function (key, val) {
        if (key == null || val == null) {
            return "Key or Value cannot be null";
        }
        // Verify dict integrity before each operation
        if (keysLength != valsLength) {
            return "Dictionary inconsistent. Keys length don't match values!";
        }
        var keysLength = this.Keys.length;
        var valsLength = this.Values.length;
        var flag = false;
        for (var i = 0; i < keysLength; i++) {
            if (this.Keys[i] == key) {
                this.Values[i] = val;
                flag = true;
                break;
            }
        }
        if (!flag) {
            return "Key does not exist";
        }
    }
}



// Check if dictionary extensions aren't implemented yet.
// Adds a unique key value pair
if (!JSdict.prototype.add) {
    JSdict.prototype.add = function (key, val) {
        // Allow only strings or numbers as keys
        if (typeof (key) == "number" || typeof (key) == "string") {
            if (key == null || val == null) {
                return "Key or Value cannot be null";
            }
            if (keysLength != valsLength) {
                return "Dictionary inconsistent. Keys length don't match values!";
            }
            var keysLength = this.Keys.length;
            var valsLength = this.Values.length;
            for (var i = 0; i < keysLength; i++) {
                if (this.Keys[i] == key) {
                    return "Duplicate keys not allowed!";
                }
            }
            this.Keys.push(key);
            this.Values.push(val);
        }
        else {
            return "Only number or string can be key!";
        }
    }
}

// Check if dictionary extensions aren't implemented yet.
// Removes a key value pair
if (!JSdict.prototype.remove) {
    JSdict.prototype.remove = function (key) {
        if (key == null) {
            return "Key cannot be null";
        }
        if (keysLength != valsLength) {
            return "Dictionary inconsistent. Keys length don't match values!";
        }
        var keysLength = this.Keys.length;
        var valsLength = this.Values.length;
        var flag = false;
        for (var i = 0; i < keysLength; i++) {
            if (this.Keys[i] == key) {
                this.Keys.shift(key);
                this.Values.shift(this.Values[i]);
                flag = true;
                break;
            }
        }
        if (!flag) {
            return "Key does not exist";
        }
    }
}

The above implementation can now be used to simulate a dictionary as:

var dict = new JSdict();

dict.add(1, "one")

dict.add(1, "one more")
"Duplicate keys not allowed!"

dict.getVal(1)
"one"

dict.update(1, "onne")

dict.getVal(1)
"onne"

dict.remove(1)

dict.getVal(1)
"Key not found!"

This is just a basic simulation. It can be further optimized by implementing a better running time algorithm to work in atleast O(nlogn) time complexity or even less. Like merge/quick sort on arrays and then some B-search for lookups. I Didn’t give a try or searched about mapping a hash function in JS.

Also, Key and Value for the JSdict obj can be turned into private variables to be sneaky.

Hope this helps!

EDIT >> After implementing the above, I personally used the JS objects as associative arrays that are available out-of-the-box.

However, I would like to make a special mention about two methods that actually proved helpful to make it a convenient hashtable experience.

Viz: dict.hasOwnProperty(key) and delete dict[key]

Read this post as a good resource on this implementation/usage. Dynamically creating keys in JavaScript associative array

THanks!


回答 4

使用JavaScript对象。您可以访问它们的属性,例如字典中的键。这是JSON的基础。语法类似于Python字典。请参阅:JSON.org

Use JavaScript objects. You can access their properties like keys in a dictionary. This is the foundation of JSON. The syntax is similar to Python dictionaries. See: JSON.org


回答 5

一个老问题,但是最近我需要做一个AS3> JS端口,为了提高速度,我为JS写了一个简单的AS3样式的Dictionary对象:

http://jsfiddle.net/MickMalone1983/VEpFf/2/

如果您不知道,AS3词典允许您使用任何对象作为键,而不仅仅是字符串。一旦您发现它们的用途,它们就会非常方便。

它的速度不如本地对象快,但是在这方面,我还没有发现任何重大问题。

API:

//Constructor
var dict = new Dict(overwrite:Boolean);

//If overwrite, allows over-writing of duplicate keys,
//otherwise, will not add duplicate keys to dictionary.

dict.put(key, value);//Add a pair
dict.get(key);//Get value from key
dict.remove(key);//Remove pair by key
dict.clearAll(value);//Remove all pairs with this value
dict.iterate(function(key, value){//Send all pairs as arguments to this function:
    console.log(key+' is key for '+value);
});


dict.get(key);//Get value from key

An old question but I recently needed to do an AS3>JS port, and for the sake of speed I wrote a simple AS3-style Dictionary object for JS:

http://jsfiddle.net/MickMalone1983/VEpFf/2/

If you didn’t know, the AS3 dictionary allows you to use any object as the key, as opposed to just strings. They come in very handy once you’ve found a use for them.

It’s not as fast as a native object would be, but I’ve not found any significant problems with it in that respect.

API:

//Constructor
var dict = new Dict(overwrite:Boolean);

//If overwrite, allows over-writing of duplicate keys,
//otherwise, will not add duplicate keys to dictionary.

dict.put(key, value);//Add a pair
dict.get(key);//Get value from key
dict.remove(key);//Remove pair by key
dict.clearAll(value);//Remove all pairs with this value
dict.iterate(function(key, value){//Send all pairs as arguments to this function:
    console.log(key+' is key for '+value);
});


dict.get(key);//Get value from key

回答 6

Firefox 13+提供了map类似于dictpython中对象的实验性实现。规格在这里

它仅在firefox中可用,但看起来比使用a的属性更好new Object()。来自文档的引用:

  • 对象具有原型,因此地图中包含默认键。但是,可以使用绕过此操作map = Object.create(null)
  • 的键ObjectStrings,其中它们可以是的任何值Map
  • Map当您必须手动跟踪的大小时,您可以轻松获得大小Object

Firefox 13+ provides an experimental implementation of the map object similar to the dict object in python. Specifications here.

It’s only avaible in firefox, but it looks better than using attributes of a new Object(). Citation from the documentation :

  • An Object has a prototype, so there are default keys in the map. However, this can be bypassed using map = Object.create(null).
  • The keys of an Object are Strings, where they can be any value for a Map.
  • You can get the size of a Map easily while you have to manually keep track of size for an Object.

JavaScript函数类似于Python range()

问题:JavaScript函数类似于Python range()

JavaScript中是否有类似于Python的函数range()

我认为应该有比每次编写以下几行更好的方法:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}

Is there a function in JavaScript similar to Python’s range()?

I think there should be a better way than to write the following lines every time:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}

回答 0

,没有,但您可以制造一个

JavaScript的Python实现 range()

尝试模拟它在Python中的工作方式,我将创建类似于以下的函数:

function range(start, stop, step) {
    if (typeof stop == 'undefined') {
        // one param defined
        stop = start;
        start = 0;
    }

    if (typeof step == 'undefined') {
        step = 1;
    }

    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
        return [];
    }

    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
        result.push(i);
    }

    return result;
};

请参阅此jsfiddle以获取证明。

range()JavaScript和Python之间的比较

它以以下方式工作:

  • range(4)返回[0, 1, 2, 3]
  • range(3,6)返回[3, 4, 5]
  • range(0,10,2)返回[0, 2, 4, 6, 8]
  • range(10,0,-1)返回[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
  • range(8,2,-2)返回[8, 6, 4]
  • range(8,2)返回[]
  • range(8,2,2)返回[]
  • range(1,5,-1)返回[]
  • range(1,5,-2)返回[]

及其Python对应物的工作方式完全相同(至少在上述情况下):

>>> range(4)
[0, 1, 2, 3]
>>> range(3,6)
[3, 4, 5]
>>> range(0,10,2)
[0, 2, 4, 6, 8]
>>> range(10,0,-1)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> range(8,2,-2)
[8, 6, 4]
>>> range(8,2)
[]
>>> range(8,2,2)
[]
>>> range(1,5,-1)
[]
>>> range(1,5,-2)
[]

因此,如果您需要一个功能类似于Python的功能range(),则可以使用上述解决方案。

No, there is none, but you can make one.

JavaScript’s implementation of Python’s range()

Trying to emulate how it works in Python, I would create function similar to this:

function range(start, stop, step) {
    if (typeof stop == 'undefined') {
        // one param defined
        stop = start;
        start = 0;
    }

    if (typeof step == 'undefined') {
        step = 1;
    }

    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
        return [];
    }

    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
        result.push(i);
    }

    return result;
};

See this jsfiddle for a proof.

Comparison between range() in JavaScript and Python

It works in the following way:

  • range(4) returns [0, 1, 2, 3],
  • range(3,6) returns [3, 4, 5],
  • range(0,10,2) returns [0, 2, 4, 6, 8],
  • range(10,0,-1) returns [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
  • range(8,2,-2) returns [8, 6, 4],
  • range(8,2) returns [],
  • range(8,2,2) returns [],
  • range(1,5,-1) returns [],
  • range(1,5,-2) returns [],

and its Python counterpart works exactly the same way (at least in the mentioned cases):

>>> range(4)
[0, 1, 2, 3]
>>> range(3,6)
[3, 4, 5]
>>> range(0,10,2)
[0, 2, 4, 6, 8]
>>> range(10,0,-1)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> range(8,2,-2)
[8, 6, 4]
>>> range(8,2)
[]
>>> range(8,2,2)
[]
>>> range(1,5,-1)
[]
>>> range(1,5,-2)
[]

So if you need a function to work similarly to Python’s range(), you can use above mentioned solution.


回答 1

对于ES6中非常简单的范围:

let range = n => Array.from(Array(n).keys())

根据bigOmega的评论,可以使用Spread语法将其缩短:

let range = n => [...Array(n).keys()]

For a very simple range in ES6:

let range = n => Array.from(Array(n).keys())

From bigOmega’s comment, this can be shortened using Spread syntax:

let range = n => [...Array(n).keys()]

回答 2

2018年:这个答案一直在投票,所以这里有一个更新。下面的代码已经过时,但是幸运的是ES6标准化的生成器和yield关键字,并且它们在各个平台上得到普遍支持。可以在这里找到延迟range()使用的示例。 yield


除了已经说过的内容外,Javascript 1.7+还提供了对迭代器和生成器的支持,这些迭代器和生成器可用于创建懒惰的,内存有效的range,类似于xrangePython2:

function range(low, high) {  
    return {
        __iterator__: function() {
            return {  
                next: function() {
                    if (low > high)
                        throw StopIteration;  
                    return low++;
                }
            }
        }
    }
}

for (var i in range(3, 5))  
  console.log(i); // 3,4,5

2018: this answer keeps getting upvotes, so here’s an update. The code below is obsolete, but luckily ES6 standardized generators and the yield keyword, and they are universally supported across platforms. An example of the lazy range() using yield can be found here.


In addition to what’s already said, Javascript 1.7+ provides support for iterators and generators which can be used to create a lazy, memory-efficient version of range, simlar to xrange in Python2:

function range(low, high) {  
    return {
        __iterator__: function() {
            return {  
                next: function() {
                    if (low > high)
                        throw StopIteration;  
                    return low++;
                }
            }
        }
    }
}

for (var i in range(3, 5))  
  console.log(i); // 3,4,5

回答 3

将来自@Tadeck@georg的两个答案融合在一起,我想到了:

function* range(start, stop, step = 1) {
    if (stop == null) {
        // one param defined
        stop = start;
        start = 0;
    }

    for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
        yield i;
    }
}

要在for循环中使用它,您需要ES6 / JS1.7 for-of循环:

for (let i of range(5)) {
    console.log(i);
}
// Outputs => 0 1 2 3 4

for (let i of range(0, 10, 2)) {
    console.log(i);
}
// Outputs => 0 2 4 6 8

for (let i of range(10, 0, -2)) {
    console.log(i);
}
// Outputs => 10 8 6 4 2

Fusing together both answers from @Tadeck and @georg, I came up with this:

function* range(start, stop, step = 1) {
    if (stop == null) {
        // one param defined
        stop = start;
        start = 0;
    }

    for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
        yield i;
    }
}

To use it in a for loop you need the ES6/JS1.7 for-of loop:

for (let i of range(5)) {
    console.log(i);
}
// Outputs => 0 1 2 3 4

for (let i of range(0, 10, 2)) {
    console.log(i);
}
// Outputs => 0 2 4 6 8

for (let i of range(10, 0, -2)) {
    console.log(i);
}
// Outputs => 10 8 6 4 2

回答 4

underscore.jslodash实用程序库(以及许多其他有用的工具)range提供了Python 2中的函数端口。从下划线文档复制的示例:

_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []

A port of the range function from Python 2 is provided by the underscore.js and lodash utility libraries (along with many other useful tools). Examples copied from the underscore docs:

_.range(10);
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11);
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5);
=> [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1);
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0);
=> []

回答 5

可以通过将迭代器附加到Number原型来实现

  Number.prototype[Symbol.iterator] = function* () { 
     for (var i = 0; i <= this; i++) {
       yield i
     } 
  }

[...5] // will result in [0,1,2,3,4,5]

摘自Kyle Simpson的类Rethinking Asynchronous JavaScript

Can be achieved by attaching an iterator to the Number prototype

  Number.prototype[Symbol.iterator] = function* () { 
     for (var i = 0; i <= this; i++) {
       yield i
     } 
  }

[...5] // will result in [0,1,2,3,4,5]

Taken from Kyle Simpson’s course Rethinking Asynchronous JavaScript


回答 6

如果需要同时指定范围的开始位置和结束位置,以下是其中一个答案的小扩展名:

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);

Here’s a small extension for one of the answers in case you need to specify both starting and ending position of the range:

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);

回答 7

干得好。

这将用索引号写入(或覆盖)每个索引的值。

Array.prototype.writeIndices = function( n ) {
    for( var i = 0; i < (n || this.length); ++i ) this[i] = i;
    return this;
};

如果不提供数字,它将使用数组的当前长度。

像这样使用它:

var array = [].writeIndices(10);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Here you go.

This will write (or overwrite) the value of each index with the index number.

Array.prototype.writeIndices = function( n ) {
    for( var i = 0; i < (n || this.length); ++i ) this[i] = i;
    return this;
};

If you don’t provide a number, it will use the current length of the Array.

Use it like this:

var array = [].writeIndices(10);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

回答 8

为了获得大小数组x,这里是不使用任何库的单线

var range = n => Array(n + 1).join(1).split('').map((x, i) => i)

作为

> range(4)
[0, 1, 2, 3]

For getting an array of size x, here’s an one-liner without using any library

var range = n => Array(n + 1).join(1).split('').map((x, i) => i)

works as

> range(4)
[0, 1, 2, 3]

回答 9

以下是Python的range()函数对JavaScript的自然适应:

// Generate range from start (inclusive) to stop (exclusive):
function* range(start, stop, step = 1) {
   if (stop === undefined) [start, stop] = [0, start];
   if (step > 0) while (start < stop) yield start, start += step;
   else if (step < 0) while (start > stop) yield start, start += step;
   else throw new RangeError('range() step argument invalid');
} 

// Examples:
console.log([...range(3)]);       // [0, 1, 2]
console.log([...range(0, 3)]);    // [0, 1, 2]
console.log([...range(0, 3, -1)]);// []
console.log([...range(0, 0)]);    // []
console.log([...range(-3)]);      // []
console.log([...range(-3, 0)]);   // [-3, -2, -1]

它支持哪些可以比任何参数0stop并且可被递增step。当数字不超过时,其行为与Python版本相同Number.MAX_SAFE_INTEGER

请注意以下情况:

[...range(0, 0, 0)];        // RangeError: range() step argument invalid
[...range(Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2)];  // []
[...range(Number.MAX_SAFE_INTEGER + 2, Number.MAX_SAFE_INTEGER + 3)];  // Infinite loop
[...range(0.7, 0.8, 0.1)];  // [0.7, 0.7999999999999999]
[...range('1', '11')];      // ['1']
[...range('2', '22')];      // Infinite loop

@Tadeck@ Volv@ janka102的答案相反[]undefinedstep0or求值时返回或进入无限循环NaN,此生成器函数将引发类似于Python行为的异常。

The following is a natural adaption of Python’s range() function to JavaScript:

// Generate range from start (inclusive) to stop (exclusive):
function* range(start, stop, step = 1) {
   if (stop === undefined) [start, stop] = [0, start];
   if (step > 0) while (start < stop) yield start, start += step;
   else if (step < 0) while (start > stop) yield start, start += step;
   else throw new RangeError('range() step argument invalid');
} 

// Examples:
console.log([...range(3)]);       // [0, 1, 2]
console.log([...range(0, 3)]);    // [0, 1, 2]
console.log([...range(0, 3, -1)]);// []
console.log([...range(0, 0)]);    // []
console.log([...range(-3)]);      // []
console.log([...range(-3, 0)]);   // [-3, -2, -1]

It supports any argument which can be compared to 0 and stop and can be incremented by step. It behaves identical to the Python version when used with numbers not exceeding Number.MAX_SAFE_INTEGER.

Please note the following corner cases:

[...range(0, 0, 0)];        // RangeError: range() step argument invalid
[...range(Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2)];  // []
[...range(Number.MAX_SAFE_INTEGER + 2, Number.MAX_SAFE_INTEGER + 3)];  // Infinite loop
[...range(0.7, 0.8, 0.1)];  // [0.7, 0.7999999999999999]
[...range('1', '11')];      // ['1']
[...range('2', '22')];      // Infinite loop

In contrast to @Tadeck’s, @Volv’s and @janka102’s answer which return [], undefined or enter an infinite loop when step evaluates to 0 or NaN, this generator function throws an exception similar to Python’s behavior.


回答 10

使用ES6默认参数进一步完善。

let range = function*(start = 0, stop, step = 1) {
  let cur = (stop === undefined) ? 0 : start;
  let max = (stop === undefined) ? start : stop;
  for (let i = cur; step < 0 ? i > max : i < max; i += step)
    yield i
}

Further refined with ES6 default parameters.

let range = function*(start = 0, stop, step = 1) {
  let cur = (stop === undefined) ? 0 : start;
  let max = (stop === undefined) ? start : stop;
  for (let i = cur; step < 0 ? i > max : i < max; i += step)
    yield i
}

回答 11

pythonicrange使用JS的generators(yield)尽可能地模仿Python行为,同时支持range(stop)range(start, stop, step)用例。此外,pythonicrange函式会传回一个Iterator与Python相似的物件,map并支援和filter,因此您可以执行像以下这样的单一程式:

import {range} from 'pythonic';
// ...
const results = range(5).map(wouldBeInvokedFiveTimes);
// `results` is now an array containing elements from
// 5 calls to wouldBeInvokedFiveTimes

使用安装npm

npm install --save pythonic

披露我是Pythonic的作者和维护者

pythonic mimics the Python range behaviour best it can using JS’ generators (yield), supporting both the range(stop) and range(start, stop, step) use cases. In addition, pythonic‘s range function returns an Iterator object similar to Python that supports map and filter, so one could do fancy one-liners like:

import {range} from 'pythonic';
// ...
const results = range(5).map(wouldBeInvokedFiveTimes);
// `results` is now an array containing elements from
// 5 calls to wouldBeInvokedFiveTimes

Install using npm:

npm install --save pythonic

Disclosure I’m author and maintainer of Pythonic


回答 12

MDN建议采用以下方法:序列生成器(范围)

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
console.log("range(0, 4, 1):", range(0, 4, 1));
// [0, 1, 2, 3, 4] 

// Generate numbers range 1..10 with step of 2 
console.log("\nrange(1, 10, 2):", range(1, 10, 2));
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
console.log("\nrange('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))", range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

MDN recommends this approach: Sequence generator (range)

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
console.log("range(0, 4, 1):", range(0, 4, 1));
// [0, 1, 2, 3, 4] 

// Generate numbers range 1..10 with step of 2 
console.log("\nrange(1, 10, 2):", range(1, 10, 2));
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
console.log("\nrange('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))", range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

回答 13

您可以使用下划线库。它包含许多有用的函数,可用于处理数组等。

You may use underscore library. It contains dozens of useful functions for working with arrays and many more.


回答 14

JavaScript中是否有类似于Python的range()的函数?

此处的所有解决方案均指的是Python 2的范围(可能是由于您提供的代码示例)。但是在Python 3中,range()方法返回一个迭代器。JavaScript还具有迭代器,并且迭代器比生成整个数组并将其存储在内存中更节省空间。

因此,Python 3range(n)函数的更准确表示是Array(n).keys()

例如:

for (let i of Array(n).keys()) {
  console.log(i) // 0, 1, 2, 3, ..., n
}

另一个例子(其他答案中已经介绍过)。将迭代器转换为数组(ES6):

let ary = [...Array(n).keys()];
// ary = [0, 1, 2, 3, ..., n]

Is there a function in JavaScript similar to Python’s range()?

All of the solutions here are referring to Python 2’s range (probably because of the code example you gave). However in Python 3, the range() method returns an iterator. JavaScript also has iterators and they’re more space efficient than generating the whole array and storing it in memory.

So the more accurate representation of Python 3’s range(n) function is Array(n).keys().

For example:

for (let i of Array(n).keys()) {
  console.log(i) // 0, 1, 2, 3, ..., n
}

One more example (which has already been covered in the other answers). Converting the iterator to an array (ES6):

let ary = [...Array(n).keys()];
// ary = [0, 1, 2, 3, ..., n]

回答 15

仍然没有等效于的内置函数range(),但是使用最新版本-E​​S2015-您可以构建自己的实现。这是它的限定版。受限,因为它不考虑step参数。最小,最大。

const range = (min = null, max = null) =>
  Array.from({length:max ? max - min : min}, (v,k) => max ? k + min : k)

这是通过Array.from能够从具有length属性的任何对象构建数组的方法来完成的。因此,仅传递带有length属性的简单对象将创建一个ArrayIterator,它将产生length许多对象。

Still no built-in function that is equivalent to range(), but with the most recent version – ES2015 – you can build your own implementation. Here’s a limited version of it. Limited because it doesn’t take into account the step parameter. Just min, max.

const range = (min = null, max = null) =>
  Array.from({length:max ? max - min : min}, (v,k) => max ? k + min : k)

This is accomplished by the Array.from method able to build an array from any object that has a length property. So passing in a simple object with just the length property will create an ArrayIterator that will yield length number of objects.


回答 16

这是我的首选方式。它允许您像在Python中一样指定一个或两个输入。

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}

This is my preferred way. It allows you to specify one or two inputs like in Python.

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}

回答 17

这是es6该范围的另一个实现

// range :: (from, to, step?) -> [Number]
const range = (from, to, step = 1) => {
  //swap values if necesery
  [from, to] = from > to ? [to, from] : [from, to]
  //create range array
  return [...Array(Math.round((to - from) / step))]
    .map((_, index) => {
      const negative = from < 0 ? Math.abs(from) : 0
      return index < negative ? 
        from + index * step  :
        (index - negative + 1) * step
    })
}  

range(-20, 0, 5)
  .forEach(val => console.log(val))

for(const val of range(5, 1)){
   console.log(`value ${val}`)
}

Here is another es6 implementation of the range

// range :: (from, to, step?) -> [Number]
const range = (from, to, step = 1) => {
  //swap values if necesery
  [from, to] = from > to ? [to, from] : [from, to]
  //create range array
  return [...Array(Math.round((to - from) / step))]
    .map((_, index) => {
      const negative = from < 0 ? Math.abs(from) : 0
      return index < negative ? 
        from + index * step  :
        (index - negative + 1) * step
    })
}  

range(-20, 0, 5)
  .forEach(val => console.log(val))

for(const val of range(5, 1)){
   console.log(`value ${val}`)
}

回答 18

不,没有,但是您可以制造一个。

我偏爱范围的Python3行为。您将在以下JavaScript的Python range()的实现中找到:

function* range(start=0, end=undefined, step=1) {    
    if(arguments.length === 1) {end = start, start = 0}    
    
    [...arguments].forEach(arg => {    
        if( typeof arg !== 'number') {throw new TypeError("Invalid argument")}                               
    })    
    if(arguments.length === 0) {throw new TypeError("More arguments neede")}    
        
    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(start + step, end, step)    
}    
         
// Use Cases
console.log([...range(5)])

console.log([...range(2, 5)])

console.log([...range(2, 5, 2)])
console.log([...range(2,3)])
// You can, of course, iterate through the range instance.

No, there is none, but you can make one.

I’m partial to Python3 behavior of range. You will find below JavaScript’s implementation of Python’s range():

function* range(start=0, end=undefined, step=1) {    
    if(arguments.length === 1) {end = start, start = 0}    
    
    [...arguments].forEach(arg => {    
        if( typeof arg !== 'number') {throw new TypeError("Invalid argument")}                               
    })    
    if(arguments.length === 0) {throw new TypeError("More arguments neede")}    
        
    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(start + step, end, step)    
}    
         
// Use Cases
console.log([...range(5)])

console.log([...range(2, 5)])

console.log([...range(2, 5, 2)])
console.log([...range(2,3)])
// You can, of course, iterate through the range instance.

回答 19

假设您需要一个简单的范围,只需一步即可:

let range = (start, end)=> {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}

其他

let range = (start, end, step)=> {
    if(start === end) return [start];
    return [start, ...range(start + step, end)];
}

有关更多信息,请参见此处

Assuming you need a simple range with a single step:

let range = (start, end)=> {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}

else

let range = (start, end, step)=> {
    if(start === end) return [start];
    return [start, ...range(start + step, end)];
}

refer to here for more.


回答 20

JavaScript中是否有类似于Python的range()的函数?

如之前的回答:,没有。但是你可以自己做。我相信这对于ES6是一种有趣的方法。它的工作原理与Python 2.7非常相似range(),但它的动态性更高。

function range(start, stop, step = 1) 
{
    // This will make the function behave as range(stop)
    if(arguments.length === 1)
    {
        return [...Array(arguments[0]).keys()]
    }

    // Adjusts step to go towards the stop value
    if((start > stop && !(step < 0)) ||
       (start < stop && !(step > 0)))
    {
        step *= -1
    }

    let returnArray = []
    // Checks if i is in the interval between start and stop no matter if stop
    // is lower than start or vice-versa
    for(let i = start; (i-start)*(i-stop) <= 0; i += step)
    {
        returnArray.push(i)
    }
    return returnArray
}

该函数可以三种不同的方式运行(就像Python的range()一样):

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

这些例子:

console.log(range(5))
console.log(range(-2, 2))
console.log(range(2, -2))
console.log(range(10, 20, 2))

将为您提供以下输出:

[ 0, 1, 2, 3, 4 ]
[ -2, -1, 0, 1, 2 ]
[ 2, 1, 0, -1, -2 ]
[ 10, 12, 14, 16, 18, 20 ]

请注意in,您不必使用运算符(如python)遍历数组,而必须使用of。因此,i变量采用数组元素的值而不是索引。

for(let i of range(5))
{
    // do something with i...
}

Is there a function in JavaScript similar to Python’s range()?

As answered before: no, there’s not. But you can make your own. I believe this is an interesting approach for ES6. It works very similar to Python 2.7 range(), but it’s much more dynamic.

function range(start, stop, step = 1) 
{
    // This will make the function behave as range(stop)
    if(arguments.length === 1)
    {
        return [...Array(arguments[0]).keys()]
    }

    // Adjusts step to go towards the stop value
    if((start > stop && !(step < 0)) ||
       (start < stop && !(step > 0)))
    {
        step *= -1
    }

    let returnArray = []
    // Checks if i is in the interval between start and stop no matter if stop
    // is lower than start or vice-versa
    for(let i = start; (i-start)*(i-stop) <= 0; i += step)
    {
        returnArray.push(i)
    }
    return returnArray
}

This function can behave in three different ways (just like Python’s range()):

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

These examples:

console.log(range(5))
console.log(range(-2, 2))
console.log(range(2, -2))
console.log(range(10, 20, 2))

Will give you the following output:

[ 0, 1, 2, 3, 4 ]
[ -2, -1, 0, 1, 2 ]
[ 2, 1, 0, -1, -2 ]
[ 10, 12, 14, 16, 18, 20 ]

Note that instead of iterating over the array with the in operator (like python), you have to use of. Thus the i variable assumes the value, and not the index, of the array’s element.

for(let i of range(5))
{
    // do something with i...
}

回答 21

NodeJ的一个选项是使用缓冲区:

[...Buffer.alloc(5).keys()]
// [ 0, 1, 2, 3, 4 ]

很好的是,您可以直接在缓冲区上进行迭代:

Buffer.alloc(5).forEach((_, index) => console.log(index))
// 0
// 1
// 2
// 3
// 4

您不能使用未初始化的数组来做到这一点:

Array(5).forEach((_, index) => console.log(index))
// undefined

但是,谁在他们的右脑中使用Buffer这样的目的;)

An option for NodeJs is to use a Buffer:

[...Buffer.alloc(5).keys()]
// [ 0, 1, 2, 3, 4 ]

What’s nice is that you can iterate directly on the buffer:

Buffer.alloc(5).forEach((_, index) => console.log(index))
// 0
// 1
// 2
// 3
// 4

You can’t do that with an uninitialized Array:

Array(5).forEach((_, index) => console.log(index))
// undefined

But, who in their right mind uses a Buffer for a purpose like this ;)


回答 22

这是我的方法

let n = 5 
[...Array(n).keys()].map(x=>{console.log(x)})

输出

0
1
2
3
4

Here’s how i do it

let n = 5 
[...Array(n).keys()].map(x=>{console.log(x)})

output

0
1
2
3
4

获取Selenium中Javascript代码的返回值

问题:获取Selenium中Javascript代码的返回值

我正在使用Selenium2对我的网站进行一些自动化测试,并且希望能够获得一些Javascript代码的返回值。如果我的foobar()网页中有Javascript函数,并且想调用该函数并将返回值获取到我的Python代码中,该怎么做?

I’m using Selenium2 for some automated tests of my website, and I’d like to be able to get the return value of some Javascript code. If I have a foobar() Javascript function in my webpage and I want to call that and get the return value into my Python code, what can I call to do that?


回答 0

要返回值,只需return在传递给execute_script()方法的字符串中使用JavaScript关键字,例如

>>> from selenium import webdriver
>>> wd = webdriver.Firefox()
>>> wd.get("http://localhost/foo/bar")
>>> wd.execute_script("return 5")
5
>>> wd.execute_script("return true")
True
>>> wd.execute_script("return {foo: 'bar'}")
{u'foo': u'bar'}
>>> wd.execute_script("return foobar()")
u'eli'

To return a value, simply use the return JavaScript keyword in the string passed to the execute_script() method, e.g.

>>> from selenium import webdriver
>>> wd = webdriver.Firefox()
>>> wd.get("http://localhost/foo/bar")
>>> wd.execute_script("return 5")
5
>>> wd.execute_script("return true")
True
>>> wd.execute_script("return {foo: 'bar'}")
{u'foo': u'bar'}
>>> wd.execute_script("return foobar()")
u'eli'

回答 1

即使您没有像下面的示例代码中那样将代码的片段作为函数编写,也可以通过return var;在最后添加var是要返回的变量的方式来返回值。

result = driver.execute_script('''cells = document.querySelectorAll('a');
URLs = []
console.log(cells);
[].forEach.call(cells, function (el) {
    if(el.text.indexOf("download") !== -1){
    //el.click();
    console.log(el.href)
    //window.open(el.href, '_blank');
    URLs.push(el.href)
    }
});
return URLs''')

result将包含在URLs这种情况下的数组。

You can return values even if you don’t have your snipped of code written as a function like in the below example code, by just adding return var; at the end where var is the variable you want to return.

result = driver.execute_script('''
cells = document.querySelectorAll('a');
URLs = [];
[].forEach.call(cells, function (el) {
    URLs.push(el.href)
});
return URLs
''')

result will contain the array that is in URLs this case.


Antlr4-ANTLR 是一个功能强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件

ANTLR(另一个用于语言识别的工具)是一个功能强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。它被广泛用于构建语言、工具和框架。根据语法,ANTLR生成一个解析器,该解析器可以构建解析树,还可以生成一个侦听器接口(或访问器),从而可以轻松地响应感兴趣的短语的识别

考虑到日间工作的限制,我在这个项目上的工作时间有限,因此我必须首先专注于修复bug,而不是更改/改进功能集。很可能我每隔几个月就会突然做一次。如果您的bug或Pull请求没有产生响应,请不要生气!–parrt

作者和主要贡献者

有用的信息

您可能还会发现以下页面很有用,特别是当您想要使用各种目标语言时

权威的ANTLR 4参考

程序员总是遇到解析问题。无论是JSON这样的数据格式,SMTP这样的网络协议,Apache的服务器配置文件,PostScript/PDF文件,还是简单的电子表格宏语言-ANTLR v4,本书都将揭开这个过程的神秘面纱。ANTLRv4已经从头开始重写,使得构建解析器和构建在其上的语言应用程序比以往任何时候都更加容易。这本完全改写的新版畅销ANTLR权威参考向您展示了如何利用这些新功能

你可以买这本书The Definitive ANTLR 4 Reference在亚马逊或electronic version at the publisher’s site

您会发现Book source code有用的

附加语法

This repository是不带动作的语法集合,其中根目录名是语法分析的语言的全小写名称。例如,java、cpp、cSharp、c等

可以使用scrapy从使用AJAX的网站中抓取动态内容吗?

问题:可以使用scrapy从使用AJAX的网站中抓取动态内容吗?

我最近一直在学习Python,并全力以赴来构建网络抓取工具。一点都不花哨。其唯一目的是从博彩网站上获取数据,并将此数据放入Excel。

大多数问题都是可以解决的,我周围有些混乱。但是,我在一个问题上遇到了巨大的障碍。如果站点加载一张马表并列出当前的投注价格,则此信息不在任何源文件中。线索是这些数据有时是实时的,而数字显然是从某个远程服务器上更新的。我PC上的HTML只是有一个漏洞,他们的服务器正在推送我需要的所有有趣数据。

现在我对动态Web内容的经验很低,所以这件事使我难以理解。

我认为Java或Javascript是关键,这经常弹出。

刮板只是赔率比较引擎。有些网站有API,但对于那些没有的API则需要。我正在使用python 2.7的scrapy库

如果这个问题过于开放,我深表歉意。简而言之,我的问题是:如何使用scrapy来抓取此动态数据,以便可以使用它?这样我就可以实时抓取该赔率赔率数据?

I have recently been learning Python and am dipping my hand into building a web-scraper. It’s nothing fancy at all; its only purpose is to get the data off of a betting website and have this data put into Excel.

Most of the issues are solvable and I’m having a good little mess around. However I’m hitting a massive hurdle over one issue. If a site loads a table of horses and lists current betting prices this information is not in any source file. The clue is that this data is live sometimes, with the numbers being updated obviously from some remote server. The HTML on my PC simply has a hole where their servers are pushing through all the interesting data that I need.

Now my experience with dynamic web content is low, so this thing is something I’m having trouble getting my head around.

I think Java or Javascript is a key, this pops up often.

The scraper is simply a odds comparison engine. Some sites have APIs but I need this for those that don’t. I’m using the scrapy library with Python 2.7

I do apologize if this question is too open-ended. In short, my question is: how can scrapy be used to scrape this dynamic data so that I can use it? So that I can scrape this betting odds data in real-time?


回答 0

基于Webkit的浏览器(例如Google Chrome或Safari)具有内置的开发人员工具。在Chrome中,您可以将其打开Menu->Tools->Developer Tools。该Network选项卡使您可以查看有关每个请求和响应的所有信息:

在图片的底部,您可以看到我已将请求过滤为XHR-这些是由javascript代码发出的请求。

提示:每次加载页面时都会清除日志,在图片底部,黑点按钮将保留日志。

分析请求和响应后,您可以模拟来自网络爬虫的请求并提取有价值的数据。在许多情况下,获取数据比解析HTML更容易,因为该数据不包含表示逻辑,并且其格式设置为可以由javascript代码访问。

Firefox具有类似的扩展名,称为firebug。有人会说萤火虫功能更强大,但我喜欢webkit的简单性。

Webkit based browsers (like Google Chrome or Safari) has built-in developer tools. In Chrome you can open it Menu->Tools->Developer Tools. The Network tab allows you to see all information about every request and response:

In the bottom of the picture you can see that I’ve filtered request down to XHR – these are requests made by javascript code.

Tip: log is cleared every time you load a page, at the bottom of the picture, the black dot button will preserve log.

After analyzing requests and responses you can simulate these requests from your web-crawler and extract valuable data. In many cases it will be easier to get your data than parsing HTML, because that data does not contain presentation logic and is formatted to be accessed by javascript code.

Firefox has similar extension, it is called firebug. Some will argue that firebug is even more powerful but I like the simplicity of webkit.


回答 1

这是一个scrapy带有AJAX请求的简单示例 。让我们看看网站rubin-kazan.ru

所有消息都加载了AJAX请求。我的目标是获取这些消息及其所有属性(作者,日期等):

当我分析页面的源代码时,因为网页使用AJAX技术,所以看不到所有这些消息。但是我可以使用Mozilla Firefox中的Firebug(或其他浏览器中的等效工具)来分析HTTP请求,该请求会在网页上生成消息:

它不会重新加载整个页面,而是仅重新加载页面中包含消息的部分。为此,我单击底部任意数量的页面:

我观察到负责邮件正文的HTTP请求:

完成后,我分析请求的标头(我必须引用将从var部分的源页面中提取的该URL,请参见下面的代码):

和请求的表单数据内容(HTTP方法为“ Post”):

以及响应的内容,它是一个JSON文件:

其中显示了我正在寻找的所有信息。

从现在开始,我必须抓紧实施所有这些知识。为此,我们定义蜘蛛:

class spider(BaseSpider):
    name = 'RubiGuesst'
    start_urls = ['http://www.rubin-kazan.ru/guestbook.html']

    def parse(self, response):
        url_list_gb_messages = re.search(r'url_list_gb_messages="(.*)"', response.body).group(1)
        yield FormRequest('http://www.rubin-kazan.ru' + url_list_gb_messages, callback=self.RubiGuessItem,
                          formdata={'page': str(page + 1), 'uid': ''})

    def RubiGuessItem(self, response):
        json_file = response.body

parse函数中,我有第一个请求的响应。在RubiGuessItem我有所有信息的JSON文件。

Here is a simple example of scrapy with an AJAX request. Let see the site rubin-kazan.ru.

All messages are loaded with an AJAX request. My goal is to fetch these messages with all their attributes (author, date, …):

When I analyze the source code of the page I can’t see all these messages because the web page uses AJAX technology. But I can with Firebug from Mozilla Firefox (or an equivalent tool in other browsers) to analyze the HTTP request that generate the messages on the web page:

It doesn’t reload the whole page but only the parts of the page that contain messages. For this purpose I click an arbitrary number of page on the bottom:

And I observe the HTTP request that is responsible for message body:

After finish, I analyze the headers of the request (I must quote that this URL I’ll extract from source page from var section, see the code below):

And the form data content of the request (the HTTP method is “Post”):

And the content of response, which is a JSON file:

Which presents all the information I’m looking for.

From now, I must implement all this knowledge in scrapy. Let’s define the spider for this purpose:

class spider(BaseSpider):
    name = 'RubiGuesst'
    start_urls = ['http://www.rubin-kazan.ru/guestbook.html']

    def parse(self, response):
        url_list_gb_messages = re.search(r'url_list_gb_messages="(.*)"', response.body).group(1)
        yield FormRequest('http://www.rubin-kazan.ru' + url_list_gb_messages, callback=self.RubiGuessItem,
                          formdata={'page': str(page + 1), 'uid': ''})

    def RubiGuessItem(self, response):
        json_file = response.body

In parse function I have the response for first request. In RubiGuessItem I have the JSON file with all information.


回答 2

进行爬网时,很多时候我们会遇到这样的问题:页面上呈现的内容是使用Javascript生成的,因此scrapy无法为其进行爬网(例如ajax请求,jQuery疯狂)。

但是,如果您将Scrapy与Web测试框架Selenium一起使用,则我们能够对普通Web浏览器中显示的内容进行爬网。

注意事项:

  • 您必须安装Python版本的Selenium RC才能正常工作,并且必须正确设置Selenium。这也只是模板搜寻器。您可能会更疯狂,更先进,但是我只是想展示基本思想。如代码所示,您将对任何给定的URL进行两个请求。Scrapy提出了一个请求,Selenium提出了另一个请求。我确信有办法解决这个问题,这样您就可以让Selenium做一个唯一的请求,但是我没有理会实现这一点,通过执行两个请求,您也可以使用Scrapy抓取页面。

  • 这非常强大,因为现在您可以抓取整个呈现的DOM,并且仍然可以使用Scrapy中所有不错的抓取功能。当然,这将使爬网速度变慢,但是取决于您需要呈现的DOM的多少,可能值得等待。

    from scrapy.contrib.spiders import CrawlSpider, Rule
    from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
    from scrapy.selector import HtmlXPathSelector
    from scrapy.http import Request
    
    from selenium import selenium
    
    class SeleniumSpider(CrawlSpider):
        name = "SeleniumSpider"
        start_urls = ["http://www.domain.com"]
    
        rules = (
            Rule(SgmlLinkExtractor(allow=('\.html', )), callback='parse_page',follow=True),
        )
    
        def __init__(self):
            CrawlSpider.__init__(self)
            self.verificationErrors = []
            self.selenium = selenium("localhost", 4444, "*chrome", "http://www.domain.com")
            self.selenium.start()
    
        def __del__(self):
            self.selenium.stop()
            print self.verificationErrors
            CrawlSpider.__del__(self)
    
        def parse_page(self, response):
            item = Item()
    
            hxs = HtmlXPathSelector(response)
            #Do some XPath selection with Scrapy
            hxs.select('//div').extract()
    
            sel = self.selenium
            sel.open(response.url)
    
            #Wait for javscript to load in Selenium
            time.sleep(2.5)
    
            #Do some crawling of javascript created content with Selenium
            sel.get_text("//div")
            yield item
    
    # Snippet imported from snippets.scrapy.org (which no longer works)
    # author: wynbennett
    # date  : Jun 21, 2011

参考:http//snipplr.com/view/66998/

Many times when crawling we run into problems where content that is rendered on the page is generated with Javascript and therefore scrapy is unable to crawl for it (eg. ajax requests, jQuery craziness).

However, if you use Scrapy along with the web testing framework Selenium then we are able to crawl anything displayed in a normal web browser.

Some things to note:

  • You must have the Python version of Selenium RC installed for this to work, and you must have set up Selenium properly. Also this is just a template crawler. You could get much crazier and more advanced with things but I just wanted to show the basic idea. As the code stands now you will be doing two requests for any given url. One request is made by Scrapy and the other is made by Selenium. I am sure there are ways around this so that you could possibly just make Selenium do the one and only request but I did not bother to implement that and by doing two requests you get to crawl the page with Scrapy too.

  • This is quite powerful because now you have the entire rendered DOM available for you to crawl and you can still use all the nice crawling features in Scrapy. This will make for slower crawling of course but depending on how much you need the rendered DOM it might be worth the wait.

    from scrapy.contrib.spiders import CrawlSpider, Rule
    from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
    from scrapy.selector import HtmlXPathSelector
    from scrapy.http import Request
    
    from selenium import selenium
    
    class SeleniumSpider(CrawlSpider):
        name = "SeleniumSpider"
        start_urls = ["http://www.domain.com"]
    
        rules = (
            Rule(SgmlLinkExtractor(allow=('\.html', )), callback='parse_page',follow=True),
        )
    
        def __init__(self):
            CrawlSpider.__init__(self)
            self.verificationErrors = []
            self.selenium = selenium("localhost", 4444, "*chrome", "http://www.domain.com")
            self.selenium.start()
    
        def __del__(self):
            self.selenium.stop()
            print self.verificationErrors
            CrawlSpider.__del__(self)
    
        def parse_page(self, response):
            item = Item()
    
            hxs = HtmlXPathSelector(response)
            #Do some XPath selection with Scrapy
            hxs.select('//div').extract()
    
            sel = self.selenium
            sel.open(response.url)
    
            #Wait for javscript to load in Selenium
            time.sleep(2.5)
    
            #Do some crawling of javascript created content with Selenium
            sel.get_text("//div")
            yield item
    
    # Snippet imported from snippets.scrapy.org (which no longer works)
    # author: wynbennett
    # date  : Jun 21, 2011
    

Reference: http://snipplr.com/view/66998/


回答 3

另一个解决方案是实现下载处理程序或下载处理程序中间件。(有关下载器中间件的更多信息,请参见scrapy docs)以下是将硒与无头phantomjs webdriver一起使用的示例类:

1)middlewares.py脚本中定义类。

from selenium import webdriver
from scrapy.http import HtmlResponse

class JsDownload(object):

    @check_spider_middleware
    def process_request(self, request, spider):
        driver = webdriver.PhantomJS(executable_path='D:\phantomjs.exe')
        driver.get(request.url)
        return HtmlResponse(request.url, encoding='utf-8', body=driver.page_source.encode('utf-8'))

2)JsDownload()在变量DOWNLOADER_MIDDLEWARE内添加类settings.py

DOWNLOADER_MIDDLEWARES = {'MyProj.middleware.MiddleWareModule.MiddleWareClass': 500}

3)整合HTMLResponse内部your_spider.py。解码响应主体将为您提供所需的输出。

class Spider(CrawlSpider):
    # define unique name of spider
    name = "spider"

    start_urls = ["https://www.url.de"] 

    def parse(self, response):
        # initialize items
        item = CrawlerItem()

        # store data as items
        item["js_enabled"] = response.body.decode("utf-8") 

可选的插件:
我希望能够告诉不同的Spider使用哪个中间件,因此我实现了此包装器:

def check_spider_middleware(method):
@functools.wraps(method)
def wrapper(self, request, spider):
    msg = '%%s %s middleware step' % (self.__class__.__name__,)
    if self.__class__ in spider.middleware:
        spider.log(msg % 'executing', level=log.DEBUG)
        return method(self, request, spider)
    else:
        spider.log(msg % 'skipping', level=log.DEBUG)
        return None

return wrapper

为了使包装工作,所有蜘蛛必须至少具有:

middleware = set([])

包括中间件:

middleware = set([MyProj.middleware.ModuleName.ClassName])

优点:
以这种方式而不是在蜘蛛网中实现它的主要优点是,您最终只会提出一个请求。例如,在AT解决方案中:下载处理程序处理请求,然后将响应传递给蜘蛛。然后,蜘蛛程序将在其parse_page函数中提出一个全新的请求-这是对相同内容的两个请求。

Another solution would be to implement a download handler or download handler middleware. (see scrapy docs for more information on downloader middleware) The following is an example class using selenium with headless phantomjs webdriver:

1) Define class within the middlewares.py script.

from selenium import webdriver
from scrapy.http import HtmlResponse

class JsDownload(object):

    @check_spider_middleware
    def process_request(self, request, spider):
        driver = webdriver.PhantomJS(executable_path='D:\phantomjs.exe')
        driver.get(request.url)
        return HtmlResponse(request.url, encoding='utf-8', body=driver.page_source.encode('utf-8'))

2) Add JsDownload() class to variable DOWNLOADER_MIDDLEWARE within settings.py:

DOWNLOADER_MIDDLEWARES = {'MyProj.middleware.MiddleWareModule.MiddleWareClass': 500}

3) Integrate the HTMLResponse within your_spider.py. Decoding the response body will get you the desired output.

class Spider(CrawlSpider):
    # define unique name of spider
    name = "spider"

    start_urls = ["https://www.url.de"] 

    def parse(self, response):
        # initialize items
        item = CrawlerItem()

        # store data as items
        item["js_enabled"] = response.body.decode("utf-8") 

Optional Addon:
I wanted the ability to tell different spiders which middleware to use so I implemented this wrapper:

def check_spider_middleware(method):
@functools.wraps(method)
def wrapper(self, request, spider):
    msg = '%%s %s middleware step' % (self.__class__.__name__,)
    if self.__class__ in spider.middleware:
        spider.log(msg % 'executing', level=log.DEBUG)
        return method(self, request, spider)
    else:
        spider.log(msg % 'skipping', level=log.DEBUG)
        return None

return wrapper

for wrapper to work all spiders must have at minimum:

middleware = set([])

to include a middleware:

middleware = set([MyProj.middleware.ModuleName.ClassName])

Advantage:
The main advantage to implementing it this way rather than in the spider is that you only end up making one request. In A T’s solution for example: The download handler processes the request and then hands off the response to the spider. The spider then makes a brand new request in it’s parse_page function — That’s two requests for the same content.


回答 4

我当时使用的是自定义下载器中间件,但对此并不满意,因为我没有设法使缓存与之配合使用。

更好的方法是实现自定义下载处理程序。

有一个工作示例这里。看起来像这样:

# encoding: utf-8
from __future__ import unicode_literals

from scrapy import signals
from scrapy.signalmanager import SignalManager
from scrapy.responsetypes import responsetypes
from scrapy.xlib.pydispatch import dispatcher
from selenium import webdriver
from six.moves import queue
from twisted.internet import defer, threads
from twisted.python.failure import Failure


class PhantomJSDownloadHandler(object):

    def __init__(self, settings):
        self.options = settings.get('PHANTOMJS_OPTIONS', {})

        max_run = settings.get('PHANTOMJS_MAXRUN', 10)
        self.sem = defer.DeferredSemaphore(max_run)
        self.queue = queue.LifoQueue(max_run)

        SignalManager(dispatcher.Any).connect(self._close, signal=signals.spider_closed)

    def download_request(self, request, spider):
        """use semaphore to guard a phantomjs pool"""
        return self.sem.run(self._wait_request, request, spider)

    def _wait_request(self, request, spider):
        try:
            driver = self.queue.get_nowait()
        except queue.Empty:
            driver = webdriver.PhantomJS(**self.options)

        driver.get(request.url)
        # ghostdriver won't response when switch window until page is loaded
        dfd = threads.deferToThread(lambda: driver.switch_to.window(driver.current_window_handle))
        dfd.addCallback(self._response, driver, spider)
        return dfd

    def _response(self, _, driver, spider):
        body = driver.execute_script("return document.documentElement.innerHTML")
        if body.startswith("<head></head>"):  # cannot access response header in Selenium
            body = driver.execute_script("return document.documentElement.textContent")
        url = driver.current_url
        respcls = responsetypes.from_args(url=url, body=body[:100].encode('utf8'))
        resp = respcls(url=url, body=body, encoding="utf-8")

        response_failed = getattr(spider, "response_failed", None)
        if response_failed and callable(response_failed) and response_failed(resp, driver):
            driver.close()
            return defer.fail(Failure())
        else:
            self.queue.put(driver)
            return defer.succeed(resp)

    def _close(self):
        while not self.queue.empty():
            driver = self.queue.get_nowait()
            driver.close()

假设您的刮板称为“刮板”。如果您将提到的代码放在“ scraper”文件夹根目录下的名为handlers.py的文件中,则可以将其添加到settings.py中:

DOWNLOAD_HANDLERS = {
    'http': 'scraper.handlers.PhantomJSDownloadHandler',
    'https': 'scraper.handlers.PhantomJSDownloadHandler',
}

还有JS解析DOM的voilà,其中包含抓取的缓存,重试等。

I was using a custom downloader middleware, but wasn’t very happy with it, as I didn’t manage to make the cache work with it.

A better approach was to implement a custom download handler.

There is a working example here. It looks like this:

# encoding: utf-8
from __future__ import unicode_literals

from scrapy import signals
from scrapy.signalmanager import SignalManager
from scrapy.responsetypes import responsetypes
from scrapy.xlib.pydispatch import dispatcher
from selenium import webdriver
from six.moves import queue
from twisted.internet import defer, threads
from twisted.python.failure import Failure


class PhantomJSDownloadHandler(object):

    def __init__(self, settings):
        self.options = settings.get('PHANTOMJS_OPTIONS', {})

        max_run = settings.get('PHANTOMJS_MAXRUN', 10)
        self.sem = defer.DeferredSemaphore(max_run)
        self.queue = queue.LifoQueue(max_run)

        SignalManager(dispatcher.Any).connect(self._close, signal=signals.spider_closed)

    def download_request(self, request, spider):
        """use semaphore to guard a phantomjs pool"""
        return self.sem.run(self._wait_request, request, spider)

    def _wait_request(self, request, spider):
        try:
            driver = self.queue.get_nowait()
        except queue.Empty:
            driver = webdriver.PhantomJS(**self.options)

        driver.get(request.url)
        # ghostdriver won't response when switch window until page is loaded
        dfd = threads.deferToThread(lambda: driver.switch_to.window(driver.current_window_handle))
        dfd.addCallback(self._response, driver, spider)
        return dfd

    def _response(self, _, driver, spider):
        body = driver.execute_script("return document.documentElement.innerHTML")
        if body.startswith("<head></head>"):  # cannot access response header in Selenium
            body = driver.execute_script("return document.documentElement.textContent")
        url = driver.current_url
        respcls = responsetypes.from_args(url=url, body=body[:100].encode('utf8'))
        resp = respcls(url=url, body=body, encoding="utf-8")

        response_failed = getattr(spider, "response_failed", None)
        if response_failed and callable(response_failed) and response_failed(resp, driver):
            driver.close()
            return defer.fail(Failure())
        else:
            self.queue.put(driver)
            return defer.succeed(resp)

    def _close(self):
        while not self.queue.empty():
            driver = self.queue.get_nowait()
            driver.close()

Suppose your scraper is called “scraper”. If you put the mentioned code inside a file called handlers.py on the root of the “scraper” folder, then you could add to your settings.py:

DOWNLOAD_HANDLERS = {
    'http': 'scraper.handlers.PhantomJSDownloadHandler',
    'https': 'scraper.handlers.PhantomJSDownloadHandler',
}

And voilà, the JS parsed DOM, with scrapy cache, retries, etc.


回答 5

如何使用scrapy来抓取此动态数据,以便可以使用它?

我想知道为什么没有人仅使用Scrapy发布解决方案。

查阅Scrapy小组SCRAPING INFINITE SCROLLING PAGES 的博客文章。该示例抓取了使用无限滚动的http://spidyquotes.herokuapp.com/scroll网站。

这个想法是使用浏览器的开发人员工具并注意AJAX请求,然后根据该信息创建对Scrapy的请求

import json
import scrapy


class SpidyQuotesSpider(scrapy.Spider):
    name = 'spidyquotes'
    quotes_base_url = 'http://spidyquotes.herokuapp.com/api/quotes?page=%s'
    start_urls = [quotes_base_url % 1]
    download_delay = 1.5

    def parse(self, response):
        data = json.loads(response.body)
        for item in data.get('quotes', []):
            yield {
                'text': item.get('text'),
                'author': item.get('author', {}).get('name'),
                'tags': item.get('tags'),
            }
        if data['has_next']:
            next_page = data['page'] + 1
            yield scrapy.Request(self.quotes_base_url % next_page)

how can scrapy be used to scrape this dynamic data so that I can use it?

I wonder why no one has posted the solution using Scrapy only.

Check out the blog post from Scrapy team SCRAPING INFINITE SCROLLING PAGES . The example scraps http://spidyquotes.herokuapp.com/scroll website which uses infinite scrolling.

The idea is to use Developer Tools of your browser and notice the AJAX requests, then based on that information create the requests for Scrapy.

import json
import scrapy


class SpidyQuotesSpider(scrapy.Spider):
    name = 'spidyquotes'
    quotes_base_url = 'http://spidyquotes.herokuapp.com/api/quotes?page=%s'
    start_urls = [quotes_base_url % 1]
    download_delay = 1.5

    def parse(self, response):
        data = json.loads(response.body)
        for item in data.get('quotes', []):
            yield {
                'text': item.get('text'),
                'author': item.get('author', {}).get('name'),
                'tags': item.get('tags'),
            }
        if data['has_next']:
            next_page = data['page'] + 1
            yield scrapy.Request(self.quotes_base_url % next_page)

回答 6

是的,Scrapy可以废弃动态网站,即通过javaScript呈现的网站。

抓取此类网站有两种方法。

第一,

您可以splash用来呈现Javascript代码,然后解析呈现的HTML。你可以在这里找到文档和项目Scrapy splash,git

第二,

就像每个人都在说的那样,通过监视network calls,可以的,您可以找到获取数据的api调用,并在您的scrapy spider中模拟该调用可能会帮助您获取所需的数据。

yes, Scrapy can scrap dynamic websites, website that are rendered through javaScript.

There are Two approaches to scrapy these kind of websites.

First,

you can use splash to render Javascript code and then parse the rendered HTML. you can find the doc and project here Scrapy splash, git

Second,

As everyone is stating, by monitoring the network calls, yes, you can find the api call that fetch the data and mock that call in your scrapy spider might help you to get desired data.


回答 7

我通过使用Selenium和Firefox Web驱动程序来处理ajax请求。如果需要使用爬虫作为守护程序,速度不是很快,但是比任何手动解决方案要好得多。我在这里写了一个简短的教程供参考

I handle the ajax request by using Selenium and the Firefox web driver. It is not that fast if you need the crawler as a daemon, but much better than any manual solution. I wrote a short tutorial here for reference


Django模板变量和Javascript

问题:Django模板变量和Javascript

当我使用Django模板渲染器渲染页面时,可以传入包含各种值的字典变量,以使用来在页面中对其进行操作{{ myVar }}

有没有办法在Javascript中访问相同的变量(也许使用DOM,我不知道Django如何使变量可访问)?我希望能够基于传入的变量中包含的值使用AJAX查找来查找详细信息。

When I render a page using the Django template renderer, I can pass in a dictionary variable containing various values to manipulate them in the page using {{ myVar }}.

Is there a way to access the same variable in Javascript (perhaps using the DOM, I don’t know how Django makes the variables accessible)? I want to be able to lookup details using an AJAX lookup based on the values contained in the variables passed in.


回答 0

{{variable}}直接替换为HTML。查看资料;它不是“变量”或类似的变量。它只是渲染的文本。

话虽如此,您可以将这种替换形式添加到JavaScript中。

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}";
</script>

这为您提供了“动态” javascript。

The {{variable}} is substituted directly into the HTML. Do a view source; it isn’t a “variable” or anything like it. It’s just rendered text.

Having said that, you can put this kind of substitution into your JavaScript.

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}";
</script>

This gives you “dynamic” javascript.


回答 1

注意检查票证#17419,以获取有关将类似的标签添加到Django核心中的讨论以及通过将此模板标签与用户生成的数据一起使用而引入的可能的XSS漏洞。amacneil的评论讨论了故障单中提出的大多数问题。


我认为最灵活,方便的方法是为要在JS代码中使用的变量定义模板过滤器。这样可以确保您的数据已正确转义,并且可以将其用于复杂的数据结构,例如dictlist。这就是为什么我写这个答案的原因,尽管有一个被广泛接受的答案。

这是模板过滤器的示例:

// myapp/templatetags/js.py

from django.utils.safestring import mark_safe
from django.template import Library

import json


register = Library()


@register.filter(is_safe=True)
def js(obj):
    return mark_safe(json.dumps(obj))

此模板过滤器将变量转换为JSON字符串。您可以这样使用它:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>

CAUTION Check ticket #17419 for discussion on adding similar tag into Django core and possible XSS vulnerabilities introduced by using this template tag with user generated data. Comment from amacneil discusses most of the concerns raised in the ticket.


I think the most flexible and handy way of doing this is to define a template filter for variables you want to use in JS code. This allows you to ensure, that your data is properly escaped and you can use it with complex data structures, such as dict and list. That’s why I write this answer despite there is an accepted answer with a lot of upvotes.

Here is an example of template filter:

// myapp/templatetags/js.py

from django.utils.safestring import mark_safe
from django.template import Library

import json


register = Library()


@register.filter(is_safe=True)
def js(obj):
    return mark_safe(json.dumps(obj))

This template filters converts variable to JSON string. You can use it like so:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>

回答 2

对我有用的解决方案是使用模板中的隐藏输入字段

<input type="hidden" id="myVar" name="variable" value="{{ variable }}">

然后以这种方式在javascript中获取值,

var myVar = document.getElementById("myVar").value;

A solution that worked for me is using the hidden input field in the template

<input type="hidden" id="myVar" name="variable" value="{{ variable }}">

Then getting the value in javascript this way,

var myVar = document.getElementById("myVar").value;

回答 3

从Django 2.1开始,专门针对此用例引入了一个新的内置模板标记:json_script

https://docs.djangoproject.com/zh-CN/3.0/ref/templates/builtins/#json-script

新标签将安全地序列化模板值并防止XSS。

Django文档摘录:

安全地将Python对象作为JSON输出,包装在标记中,可以与JavaScript一起使用。

As of Django 2.1, a new built in template tag has been introduced specifically for this use case: json_script.

https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script

The new tag will safely serialize template values and protects against XSS.

Django docs excerpt:

Safely outputs a Python object as JSON, wrapped in a tag, ready for use with JavaScript.


回答 4

这是我很容易做的事情:我为模板修改了base.html文件,并将其放在底部:

{% if DJdata %}
    <script type="text/javascript">
        (function () {window.DJdata = {{DJdata|safe}};})();
    </script>
{% endif %}

然后,当我想在javascript文件中使用变量时,我创建了一个DJdata字典,并通过json将其添加到上下文中: context['DJdata'] = json.dumps(DJdata)

希望能帮助到你!

Here is what I’m doing very easily: I modified my base.html file for my template and put that at the bottom:

{% if DJdata %}
    <script type="text/javascript">
        (function () {window.DJdata = {{DJdata|safe}};})();
    </script>
{% endif %}

then when I want to use a variable in the javascript files, I create a DJdata dictionary and I add it to the context by a json : context['DJdata'] = json.dumps(DJdata)

Hope it helps!


回答 5

对于字典,最好先编码为JSON。您可以使用simplejson.dumps(),或者如果要从App Engine中的数据模型进行转换,则可以使用GQLEncoder库中的encode()。

For a dictionary, you’re best of encoding to JSON first. You can use simplejson.dumps() or if you want to convert from a data model in App Engine, you could use encode() from the GQLEncoder library.


回答 6

我面临着类似的问题,S.Lott建议的答案为我工作。

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}"
</script>

但是,我想在这里指出主要的实现限制。如果您打算将javascript代码放在其他文件中,然后将该文件包含在模板中。这行不通。

仅当主模板和javascript代码位于同一文件中时,此方法才有效。也许django小组可以解决这个限制。

I was facing simillar issue and answer suggested by S.Lott worked for me.

<script type="text/javascript"> 
   var a = "{{someDjangoVariable}}"
</script>

However I would like to point out major implementation limitation here. If you are planning to put your javascript code in different file and include that file in your template. This won’t work.

This works only when you main template and javascript code is in same file. Probably django team can address this limitation.


回答 7

我也一直在为此苦苦挣扎。从表面上看,上述解决方案应该可行。但是,django架构要求每个html文件都有其自己的呈现变量(即{{contact}}呈现为contact.html,而呈现{{posts}}给eg index.html等)。另一方面,<script>标记出现{%endblock%}base.htmlfrom的后面contact.htmlindex.html继承。这基本上意味着任何解决方案,包括

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

必然会失败,因为变量和脚本不能共存于同一文件中。

我最终想出并为我工作的一个简单解决方案是,简单地用带有id的标签包装变量,然后在js文件中引用它,如下所示:

// index.html
<div id="myvar">{{ myVar }}</div>

然后:

// somecode.js
var someVar = document.getElementById("myvar").innerHTML;

并且只包含<script src="static/js/somecode.js"></script>base.html照常进行。当然,这只是关于获取内容。关于安全性,只需遵循其他答案即可。

I’ve been struggling with this too. On the surface it seems that the above solutions should work. However, the django architecture requires that each html file has its own rendered variables (that is, {{contact}} is rendered to contact.html, while {{posts}} goes to e.g. index.html and so on). On the other hand, <script> tags appear after the {%endblock%} in base.html from which contact.html and index.html inherit. This basically means that any solution including

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

is bound to fail, because the variable and the script cannot co-exist in the same file.

The simple solution I eventually came up with, and worked for me, was to simply wrap the variable with a tag with id and later refer to it in the js file, like so:

// index.html
<div id="myvar">{{ myVar }}</div>

and then:

// somecode.js
var someVar = document.getElementById("myvar").innerHTML;

and just include <script src="static/js/somecode.js"></script> in base.html as usual. Of course this is only about getting the content. Regarding security, just follow the other answers.


回答 8

对于以文本形式存储在Django字段中的JavaScript对象,它需要再次成为动态插入页面脚本中的JavaScript对象,您需要同时使用escapejsJSON.parse()

var CropOpts = JSON.parse("{{ profile.last_crop_coords|escapejs }}");

Django的escapejs句柄正确处理了引号,JSON.parse()并将字符串转换回JS对象。

For a JavaScript object stored in a Django field as text, which needs to again become a JavaScript object dynamically inserted into on-page script, you need to use both escapejs and JSON.parse():

var CropOpts = JSON.parse("{{ profile.last_crop_coords|escapejs }}");

Django’s escapejs handles the quoting properly, and JSON.parse() converts the string back into a JS object.


回答 9

请注意,如果要将变量传递给外部.js脚本,则需要在脚本标签之前加上另一个声明全局变量的脚本标签。

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

<script type="text/javascript" src="{% static "scripts/my_script.js" %}"></script>

data 在视图中照常定义 get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context

Note, that if you want to pass a variable to an external .js script then you need to precede your script tag with another script tag that declares a global variable.

<script type="text/javascript">
    var myVar = "{{ myVar }}"
</script>

<script type="text/javascript" src="{% static "scripts/my_script.js" %}"></script>

data is defined in the view as usual in the get_context_data

def get_context_data(self, *args, **kwargs):
    context['myVar'] = True
    return context

回答 10

我在Django 2.1中使用这种方式并为我工作,这种方式很安全(参考)

Django方面:

def age(request):
    mydata = {'age':12}
    return render(request, 'test.html', context={"mydata_json": json.dumps(mydata)})

HTML方面:

<script type='text/javascript'>
     const  mydata = {{ mydata_json|safe }};
console.log(mydata)
 </script>

I use this way in Django 2.1 and work for me and this way is secure (reference):

Django side:

def age(request):
    mydata = {'age':12}
    return render(request, 'test.html', context={"mydata_json": json.dumps(mydata)})

Html side:

<script type='text/javascript'>
     const  mydata = {{ mydata_json|safe }};
console.log(mydata)
 </script>

回答 11

您可以在字符串中声明数组变量的地方组装整个脚本,如下所示,

views.py

    aaa = [41, 56, 25, 48, 72, 34, 12]
    prueba = "<script>var data2 =["
    for a in aaa:
        aa = str(a)
        prueba = prueba + "'" + aa + "',"
    prueba = prueba + "];</script>"

将生成如下字符串

prueba = "<script>var data2 =['41','56','25','48','72','34','12'];</script>"

拥有此字符串后,必须将其发送到模板

views.py

return render(request, 'example.html', {"prueba": prueba})

在模板中,您会收到它,并以书面形式将其解释为htm代码,例如您需要的javascript代码之前

模板

{{ prueba|safe  }}

在代码的其余部分下面,请记住,在示例中使用的变量是data2

<script>
 console.log(data2);
</script>

这样,您将保留数据类型,在这种情况下,这是一种安排

you can assemble the entire script where your array variable is declared in a string, as follows,

views.py

    aaa = [41, 56, 25, 48, 72, 34, 12]
    prueba = "<script>var data2 =["
    for a in aaa:
        aa = str(a)
        prueba = prueba + "'" + aa + "',"
    prueba = prueba + "];</script>"

that will generate a string as follows

prueba = "<script>var data2 =['41','56','25','48','72','34','12'];</script>"

after having this string, you must send it to the template

views.py

return render(request, 'example.html', {"prueba": prueba})

in the template you receive it and interpret it in a literary way as htm code, just before the javascript code where you need it, for example

template

{{ prueba|safe  }}

and below that is the rest of your code, keep in mind that the variable to use in the example is data2

<script>
 console.log(data2);
</script>

that way you will keep the type of data, which in this case is an arrangement


回答 12

Javascript中有两件事对我有用:

'{{context_variable|escapejs }}'

其他:在views.py中

from json import dumps as jdumps

def func(request):
    context={'message': jdumps('hello there')}
    return render(request,'index.html',context)

并在html中:

{{ message|safe }}

There are two things that worked for me inside Javascript:

'{{context_variable|escapejs }}'

and other: In views.py

from json import dumps as jdumps

def func(request):
    context={'message': jdumps('hello there')}
    return render(request,'index.html',context)

and in the html:

{{ message|safe }}

Botframework-sdk-BOT框架为构建对话应用程序提供了最全面的体验

BOT框架为构建对话应用程序提供了最全面的体验

使用Bot Framework SDK,开发人员可以构建与自由格式或具有引导交互的机器人进行交互,包括使用包含文本、图像和操作按钮的简单文本或富卡

开发人员可以使用他们喜欢的编程语言(包括C#、JS、Python和Java)建模和构建复杂的对话,也可以使用Bot Framework Composer,这是一个开源的可视化创作画布,供开发人员和多学科团队设计和构建对话体验,包括语言理解、QNA Maker和BOT回复的复杂组合(Language Generation)

签出 Bot Framework ecosystem部分,了解与Bot Framework SDK相关的其他工具和服务的更多信息

快速链接

|Bot Framework Composer|C# Repo|JS Repo|Python Repo|Java Repo|BF CLI|

BOT框架SDK v4

Bot Framework SDK v4是一个open source SDK使开发人员能够使用他们最喜欢的编程语言建模和构建复杂的对话

C# JS python Java语言
稳定释放 packages packages packages packages
文档 docs docs docs docs
样本 .NET CoreWebAPI Node.jsTypeScriptes6 Python Java

通道和适配器

有两种方式可以将你的机器人连接到客户端体验:

  • Azure Bot服务频道-通过Azure Bot服务提供语言和SDK独立支持
  • BOT框架SDK适配器-每种语言适配器组件
客户端 蔚蓝通道 C#适配器 JS适配器 Python适配器
微软团队 Azure
直达线路 Azure
网络聊天 Azure Botkit
Skype Azure
电子邮件 Azure
Facebook Azure SDK Botkit
松弛 Azure SDK Botkit SDK
Kik Azure
电报 Azure
线路 Azure
GroupMe Azure
Twilio(短信) Azure SDK Botkit
Alexa技能 Community Community
谷歌行动 Community Community
Google Hangout Botkit
WebEx SDK Botkit
WhatsApp(Infobip) Community
缩放 Community
RingCentral Community
科尔塔纳 Azure
控制台 Community

社区开源项目

以下开放源码社区提供了各种组件来扩展您的bot应用程序,包括适配器、识别器、对话框和中间件

C# JavaScript python Java语言
Bot Framework Community C# JavaScript Python Java
Botkit JavaScript

问题和帮助

如果您对Bot Framework SDK或使用Azure Bot服务有任何疑问,我们鼓励您联系社区和Azure Bot服务开发团队寻求帮助

查看所有可用的支持选项here

问题和功能请求

我们在不同的位置跟踪Bot Framework SDK、工具和Azure Bot服务的功能问题和功能需求。如果您发现问题或有功能请求,请将问题提交到以下存储库

项目 描述 链接
SDK v4.NET 用于.NET、连接器、中间件、对话框、提示、Luis和QNA的核心bot运行时 File an issue
SDK v4 JavaScript 用于TypeScript/Javascript、连接器、中间件、对话框、提示符、Luis和QNA的核心bot运行时 File an issue
SDK v4 Python Python、连接器、中间件、对话框、提示、Luis和QNA的核心bot运行时 File an issue
SDK v4 Java 面向Java、连接器、中间件、对话框、提示、Luis和QNA的核心bot运行时 File an issue
BOT框架组合器 BOT框架合成器电子和Web应用程序 File an issue
BOT框架CLI BOT框架cli工具 File an issue
网络聊天 BOT框架网络聊天工具 File an issue

以前的版本

BOT框架生态系统

BOT框架组合器

Bot Framework Composer是一个集成开发工具,供开发人员和多学科团队使用Microsoft Bot Framework构建机器人和对话体验。在此工具中,您将找到构建复杂对话体验所需的一切

博基特

Botkit是一个开发人员工具和SDK,用于为主要消息传递平台构建聊天机器人、应用程序和自定义集成。僵尸机器人hear()触发器,ask()问题和say()回复。开发人员可以使用此语法构建对话框-现在可与最新版本的Bot Framework SDK交叉兼容

此外,botkit还附带了6个平台适配器,允许Javascript bot应用程序直接与消息传递平台通信:SlackWebex TeamsGoogle HangoutsFacebook MessengerTwilio,以及Web chat

botkit是Microsoft Bot Framework的一部分,在MIT Open Source license

BOT框架虚拟助手解决方案加速器

这个Bot Framework Solutions repository是世界上最大的Virtual Assistant Solution Accelerator,它提供了一组模板、解决方案加速器和技能,以帮助构建复杂的对话体验

  • Virtual Assistant.客户和合作伙伴非常需要为他们的品牌量身定做一个对话助手,为他们的用户量身定做,并且在各种画布和设备上都可以使用。

    这汇集了所有支持组件,并极大地简化了新BOT项目的创建,包括:基本对话意图、调度集成、QNA Maker、Application Insight和自动化部署

  • Skills.一个可重复使用的会话技能构建块库,使您可以向Bot添加功能。我们目前提供:日历,电子邮件,任务,兴趣点,汽车,天气和新闻技能。技能包括以源代码形式交付的Luis模型、对话框和集成代码,以便根据需要进行自定义和扩展
  • Analytics.使用Bot Framework Analytics解决方案获得对您的机器人的健康状况和行为的重要见解,其中包括:示例Application Insights查询和Power BI仪表板,以全面了解您的机器人与用户的对话

Azure Bot服务

Azure Bot服务使您能够托管完全拥有和控制您的数据的企业级智能机器人。开发者可以在Skype、微软团队、Cortana、网络聊天等平台上注册机器人并将其连接到用户。[Docs]

  • 直通JS客户端:如果您希望在Azure Bot服务中使用Direct Line频道,并且没有使用Webchat客户端,则可以在您的自定义应用程序中使用Direct Line JS客户端。[Readme]

  • 直接线路语音信道:我们正在将Bot框架和Microsoft的语音服务结合在一起,以提供一个通道,支持从客户端到BOT应用程序的双向流式语音和文本。若要注册,请向您的Azure Bot服务添加“Direct Line Speech”频道
  • 为您的Bot-Direct Line App服务扩展提供更好的隔离:Direct Line App Service Extension可以作为VNET的一部分进行部署,使IT管理员能够更好地控制会话流量,并由于减少了跳数而改善了会话延迟。单击此处开始使用Direct Line App Service Extension。VNET允许您在Azure中创建自己的私有空间,并且对您的云网络至关重要,因为它提供隔离、分段和其他主要优势

BOT框架仿真器

这个Bot Framework Emulator是一个跨平台的桌面应用程序,允许bot开发人员测试和调试使用Bot Framework SDK构建的bot。可以使用Bot Framework Emulator测试在计算机上本地运行的Bot或连接到远程运行的Bot。[Download latest|Docs]

BOT框架网络聊天

机器人框架Web Chat是Azure Bot服务的一个高度可自定义的基于Web的客户端聊天控件,它为用户提供了在网页中直接与你的机器人交互的功能。[Stable release|Docs|Samples]

BOT框架CLI

Bot Framework CLI工具托管open source跨平台Bot Framework CLI工具,旨在支持构建强大的端到端开发工作流。Bot Framework CLI工具取代了legacy standalone tools用于管理僵尸程序和相关服务。BF CLI将跨平台工具集合聚合到一个紧密一致的界面中

相关服务

语言理解

一种基于机器学习的服务,用于构建自然语言体验。快速创建可持续改进的企业就绪型定制模型。语言理解服务(Language Underming Service,Luis)允许您的应用程序用他们自己的话来理解他们想要的东西。[Docs|Add language understanding to your bot]

QNA制造商

QnA Maker是一种基于云的API服务,可在您的数据上创建会话问答层。使用QNA Maker,您可以在几分钟内根据FAQ URL、结构化文档、产品手册或编辑内容构建、培训和发布一个简单的问答机器人。[Docs|Add qnamaker to your bot]

派单

调度工具允许您构建语言模型,允许您在不同的组件(如QNA、Luis和自定义代码)之间进行调度。[Readme]

语音服务

语音服务将音频转换为文本,通过统一的语音服务进行语音翻译和文本到语音的转换。有了语音服务,你可以将语音集成到你的机器人中,创建自定义唤醒词,并用多种语言创作。[Docs]

适配卡

Adaptive Cards是一个开放标准,供开发人员以通用且一致的方式交换卡内容,并被Bot Framework开发人员用来创建出色的跨通道转换体验

  • 开放式框架,原生性能-简单的开放式卡格式支持共享工具的生态系统、应用程序之间的无缝集成以及任何设备上的本机跨平台性能
  • 从第一天起启用语音-我们生活在一个令人兴奋的时代,在这个时代,用户可以与他们的设备交谈。适配卡拥抱了这一新世界,并从头开始设计以支持这些新体验

贡献

请参阅我们的contributing guidelines

报告安全问题

安全问题和错误应通过电子邮件私下报告给Microsoft安全响应中心(MSRC),地址为secure@microsoft.com您应该会在24小时内收到回复。如果您由于某些原因没有收到您的邮件,请通过电子邮件跟进,以确保我们收到您的原始邮件。更多信息,包括MSRC PGP密钥,可以在Security TechCenter

版权所有(C)Microsoft Corporation。版权所有

典型的AngularJS工作流程和项目结构(使用Python Flask)

问题:典型的AngularJS工作流程和项目结构(使用Python Flask)

对于整个MV *客户端框架的狂热,我还是一个新手。它不一定是AngularJS,但我选择它是因为它对我来说比淘汰赛,Ember或Backbone更自然。无论如何,工作流程是什么样的?人们是否开始在AngularJS中开发客户端应用程序,然后将其连接到后端?

还是相反,首先在Django,Flask,Rails中构建后端,然后将AngularJS应用附加到该后端?是否有“正确”的方式来做,还是最终只是个人喜好?

我也不确定是否根据Flask或AngularJS构建我的项目?社区实践。

例如,Flask的minitwit应用程序的结构如下:

minitwit
|-- minitwit.py
|-- static
   |-- css, js, images, etc...
`-- templates
   |-- html files and base layout

AngularJS教程应用程序的结构如下:

angular-phonecat
|-- app
    `-- css
    `-- img
    `-- js
    `-- lib
    `-- partials
    `-- index.html
|-- scripts
 `-- node.js server and test server files

我可以单独想象一个Flask应用程序,并且很容易看到像ToDo List这样的AngularJS应用程序,但是当同时使用这两种技术时,我不知道它们是如何协同工作的。当您已经拥有AngularJS时,似乎几乎不需要服务器端Web框架,一个简单的Python Web服务器就足够了。例如,在AngularJS待办应用程序中,他们使用MongoLab通过Restful API与数据库对话。无需在后端使用Web框架。

也许我只是非常困惑,而AngularJS只是一个花哨的jQuery库,所以我应该像在Flask项目中使用jQuery一样使用(假设我将AngularJS模板语法更改为与Jinja2不冲突的语法)。我希望我的问题有道理。我主要在后端工作,这个客户端框架对我来说是未知领域。

I am pretty new to this whole MV* client-side framework frenzy. It doesn’t have to be AngularJS, but I picked it because it feels more natural to me than either Knockout, Ember or Backbone. Anyway what is the workflow like? Do people start with developing a client-side application in AngularJS and then hooking up the back-end to it?

Or the other way around by first building the back-end in Django, Flask, Rails and then attaching an AngularJS app to it? Is there a “right” way of doing it, or is it just a personal preference in the end?

I am also not sure whether to structure my project according to the Flask or AngularJS? community practices.

For example, Flask’s minitwit app is structured like so:

minitwit
|-- minitwit.py
|-- static
   |-- css, js, images, etc...
`-- templates
   |-- html files and base layout

AngularJS tutorial app is structured like this:

angular-phonecat
|-- app
    `-- css
    `-- img
    `-- js
    `-- lib
    `-- partials
    `-- index.html
|-- scripts
 `-- node.js server and test server files

I could picture a Flask app by itself, and it’s fairly easy to see AngularJS app like ToDo List by itself but when it comes to using both of these technologies I don’t understand how they work together. It almost seems like I don’t need a server-side web-framework when you already have AngularJS, a simple Python web server will suffice. In the AngularJS to-do app for example they use MongoLab to talk to the database using Restful API. There was no need having a web framework on the back-end.

Maybe I am just awfully confused, and AngularJS is nothing more than a fancy jQuery library so I should use just like I would use jQuery in my Flask projects (assuming I change the AngularJS template syntax to something that doesn’t conflict with Jinja2). I hope my questions make some sense. I mainly work on the back-end and this client-side framework is an unknown territory for me.


回答 0

首先,我将以标准结构组织Flask应用程序,如下所示:

app
|-- app.py
|-- static
    |-- css
    |-- img
    |-- js
|-- templates

正如btford所提到的,如果您正在开发Angular应用程序,则需要集中精力使用Angular客户端模板,而远离服务器端模板。使用render_template(’index.html’)会使Flask将您的角度模板解释为Jinja模板,因此它们将无法正确渲染。相反,您需要执行以下操作:

@app.route("/")
def index():
    return send_file('templates/index.html')

请注意,使用send_file()意味着文件将被缓存,因此您至少在开发时可能要使用make_response():

    return make_response(open('templates/index.html').read())

然后,构建应用程序的AngularJS部分,修改应用程序结构,使其看起来像这样:

app
|-- app.py
|-- static
    |-- css
    |-- img
    |-- js
        |-- app.js, controllers.js, etc.
    |-- lib
        |-- angular
            |-- angular.js, etc.
    |-- partials
|-- templates
    |-- index.html

确保index.html包含AngularJS以及任何其他文件:

<script src="static/lib/angular/angular.js"></script>

此时,您尚未构建RESTful API,因此可以让js控制器返回预定义的示例数据(仅是临时设置)。准备就绪后,实现RESTful API并使用angular-resource.js将其连接到您的angular应用程序。

编辑:我整理了一个应用程序模板,尽管比我上面描述的要复杂一些,但它说明了如何使用AngularJS + Flask构建应用程序,并完成了AngularJS和简单的Flask API之间的通信。如果您想签出,这里是这里:https : //github.com/rxl/angular-flask

I would start out by organizing the Flask app in the standard structure as follows:

app
|-- app.py
|-- static
    |-- css
    |-- img
    |-- js
|-- templates

And as btford mentioned, if you are doing an Angular app, you’ll want to focus on using Angular client-side templates and stay away from server-side templates. Using render_template(‘index.html’) will cause Flask to interpret your angular templates as jinja templates, so they won’t render correctly. Instead, you’ll want to do the following:

@app.route("/")
def index():
    return send_file('templates/index.html')

Note that using send_file() means that the files will be cached, so you might want to use make_response() instead, at least for development:

    return make_response(open('templates/index.html').read())

Afterwards, build out the AngularJS part of your app, modifying the app structure so that it looks like this:

app
|-- app.py
|-- static
    |-- css
    |-- img
    |-- js
        |-- app.js, controllers.js, etc.
    |-- lib
        |-- angular
            |-- angular.js, etc.
    |-- partials
|-- templates
    |-- index.html

Make sure your index.html includes AngularJS, as well as any other files:

<script src="static/lib/angular/angular.js"></script>

At this point, you haven’t yet constructed your RESTful API, so you can have your js controllers return predefined sample data (only a temporary setup). When you’re ready, implement the RESTful API and hook it up to your angular app with angular-resource.js.

EDIT: I put together an app template that, though a little more complex that what I’ve described above, illustrates how one could build an app with AngularJS + Flask, complete with communication between AngularJS and a simple Flask API. Here it is if you want to check it out: https://github.com/rxl/angular-flask


回答 1

您可以从任一端开始。

没错,AngularJS可能不需要完整的服务器端框架。通常,最好提供静态HTML / CSS / JavaScript文件,并为后端提供RESTful API供客户端使用。您可能应该避免的一件事是将服务器端模板与AngularJS客户端模板混合。

如果您想使用Flask来提供文件(可能有些过头,但仍然可以使用它),则可以将“ app”的内容从“ angular-phonecat”复制到“ minitwit”的“ static”文件夹中。

AngularJS的目标是类似AJAX的应用程序,而flask使您能够执行旧式Web应用程序以及创建RESTful API。每种方法都有优点和缺点,因此它实际上取决于您要执行的操作。如果您给我一些见解,我也许可以提出更多建议。

You can start on either end.

You are right that you probably don’t need a full server-side framework with AngularJS. It’s typically better to serve static HTML/CSS/JavaScript files, and provide a RESTful API for the back end for the client to consume. One thing that you should probably avoid is mixing server-side templates with AngularJS client-side templates.

If you want to use Flask to serve your files (might be overkill, but you can use it nonetheless) you would copy the contents of “app” from “angular-phonecat” into the “static” folder of “minitwit.”

AngularJS is more targeted at AJAX-like applications, whereas flask gives you the ability to do both the older-style web apps as well as create RESTful APIs. There are advantages and disadvantages to each approach, so it really depends what you want to do. If you give me some insights, I might be able to make further recommendations.


回答 2

John Lindquist的这个Jetbrains PyCharm官方视频(angular.js和jetbrains大师)是一个不错的起点,因为它显示了烧瓶中Web服务,数据库和angular.js的相互作用。

他在不到25分钟的时间内使用flask,sqlalchemy,flask-restless和angular.js 构建了一个pinterest克隆

欣赏:http//www.youtube.com/watch?v = 2geC50roans

This official Jetbrains PyCharm video by John Lindquist (angular.js and jetbrains guru) is a nice starting point as it shows the interplay of webservice, database and angular.js within flask.

He builds a pinterest clone with flask, sqlalchemy, flask-restless and angular.js in less than 25 minutes.

Enjoy: http://www.youtube.com/watch?v=2geC50roans


回答 3

编辑:新的Angular2样式指南更详细地建议了一个相似的结构,即使不是相同的结构。

下面的答案针对大型项目。我花了很多时间思考和尝试几种方法,因此我可以将一些用于后端功能的服务器端框架(在我的情况下为Flask和App Engine)与客户端框架(例如Angular)结合起来。这两个答案都很好,但是我想提出一个稍微不同的方法(至少在我看来),以一种更人性化的方式进行扩展。

当您实现TODO示例时,事情就很简单了。当您开始添加功能以及改善用户体验的细微细节时,不难发现样式,javascript等混乱。

我的应用程序开始变得很大,因此我不得不退后一步并重新思考。最初,通过将所有样式和所有JavaScript合并在一起,可以采用上述建议的方法,但是这种方法不是模块化的,也不容易维护。

如果我们按功能而非文件类型组织客户端代码怎么办:

app
|-- server
    |-- controllers
        |-- app.py
    |-- models
        |-- model.py
    |-- templates
        |-- index.html
|-- static
    |-- img
    |-- client
        |-- app.js
        |-- main_style.css
        |-- foo_feature
            |-- controller.js
            |-- directive.js
            |-- service.js
            |-- style.css
            |-- html_file.tpl.html
        |-- bar_feature
            |-- controller.js
            |-- directive.js
            |-- service.js
            |-- style.css
            |-- html_file.tpl.html
    |-- lib
        |-- jquery.js
        |-- angular.js
        |-- ...

等等。

如果我们这样构建它,我们可以将我们的每个目录都包装在angular模块中。而且我们以一种很好的方式拆分了文件,当我们使用特定功能时,我们不必经过不相关的代码。

正确配置了诸如Grunt的任务运行程序,将能够轻松找到并连接和编译文件。

edit: The new Angular2 style guide suggests a similar, if not the same structure in much more detail.

The answer below target large scale projects. I have spend quite some time thinking and experimenting with several approaches so I can combine some server side framework (Flask with App Engine in my case) for back-end functionality along with a client side framework like Angular. Both answers are very good, but I would like to suggest a slightly different approach which (in my mind at least) scales in a more human way.

When you are implementing a TODO example, things are quite straight forward. When you start adding functionality though and small nice details for user experience improvement, its not difficult to get lost in chaos of styles, javascript etc..

My application started to grow quite big, so I had to take a step back and rethink. Initially an approach like suggested above would work, by having all the styles together and all JavaScript together, but its not modular and not easily maintainable.

What if we organized the client code per feature and not per file type:

app
|-- server
    |-- controllers
        |-- app.py
    |-- models
        |-- model.py
    |-- templates
        |-- index.html
|-- static
    |-- img
    |-- client
        |-- app.js
        |-- main_style.css
        |-- foo_feature
            |-- controller.js
            |-- directive.js
            |-- service.js
            |-- style.css
            |-- html_file.tpl.html
        |-- bar_feature
            |-- controller.js
            |-- directive.js
            |-- service.js
            |-- style.css
            |-- html_file.tpl.html
    |-- lib
        |-- jquery.js
        |-- angular.js
        |-- ...

and so on.

If we build it like this, we can wrap every directory of ours in an angular module. And we have split our files in a nice way that we don’t have to go through irrelevant code when we are working with a specific feature.

A task runner like Grunt properly configured, will be able to find and concatenate and compile your files without much hassle.


回答 4

另一种选择是将两者完全分开。

项目
|-服务器
|-客户

与flask相关的文件位于服务器文件夹下,而与angularjs相关的文件位于客户端文件夹下。这样,将更容易更改后端或前端。例如,将来您可能希望从Flask切换到Django或从AngularJS切换到ReactJS。

Another option is to completely separate the two.

project
|-- server
|-- client

Files related to flask goes under the server folder and files related to angularjs goes under the client folder. This way, it will be easier to change the backend or front-end. For example, you might want to switch from Flask to Django or AngularJS to ReactJS in the future.


回答 5

我认为确定大部分数据处理的目标是前端还是后端很重要。
如果是前端,则使用有角度的工作流程,这意味着您的flask应用程序将更多地充当api的角色,在该api中,如flask-restful的扩展将结束。

但是,如果像我一样,您需要在后端完成大部分工作,然后使用flask结构,仅插入有角度的插件(或在我的情况下为vue.js)来构建前端(必要时)

I think it is important to determine at what end you want to do most of your data processing – front end or back end.
If it’s front end, then go with the angular workflow, which means your flask app will function as more of an api where an extension like flask-restful will come end.

But if like me, you are doing most work on the backend, then go with the flask structure and only plug angular ( or in my case vue.js) to build the front end (when neccessary)