【问题标题】:How to download from JavaScript rendered webpage?如何从 JavaScript 呈现的网页下载?
【发布时间】:2016-02-06 13:58:32
【问题描述】:

如何从 JavaScript 呈现的网页上的链接下载? Python 是首选语言。

到目前为止,我已经尝试在无头服务器上使用Python bindings for Selenium。这种方法非常缓慢,充满错误,并且无法可靠地确定下载进度或成功。此外,无头服务器干扰了我的剪贴板(这是一个问题)。我使用 Firefox,因为它可以配置为下载到默认目录,但我不认为 Chrome 的情况会更好。

或者,我尝试过使用 WebKit。

def render(url):
    """Fully render a webpage (JavaScript and all) and return the HTML."""

    import subprocess
    from textwrap import dedent

    script = dedent("""\
    import sys
    from PyQt4.QtCore import QUrl
    from PyQt4.QtGui import QApplication
    from PyQt4.QtWebKit import QWebPage

    class Render(QWebPage):

        def __init__(self, url):
            self.app = QApplication(sys.argv)
            QWebPage.__init__(self)
            self.loadFinished.connect(self._loadFinished)
            self.mainFrame().load(QUrl(url))
            self.app.exec_()

        def _loadFinished(self, result):
            self.frame = self.mainFrame()
            self.app.quit()

    render = Render(sys.argv[1])
    print render.frame.toHtml().toAscii()""").encode()

    process = subprocess.Popen(['python2', '-', url],
                               stderr=subprocess.PIPE,
                               stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE)

    # pipe script into Python's stdin
    return process.communicate(script)[0].decode('latin1')

如果不是因为我需要下载在同一个会话中,这将是很棒的。有没有办法保留用于呈现页面的会话? PyQt4 和 WebKit 只是一堆共享库。我不知道如何撕毁他们的内脏,也不知道这样的事情是否可能。

现在我只是在做以下事情:

with requests.Session() as session:
    html = session.get(url).text
    link = get_url(html)
    download(link, session=session)

在不深入细节的情况下,get_url(html, url) 只是从页面中提取 JavaScript,删除对 DOM 的任何调用,然后在node 中执行它。真是恶心的东西……

有什么方法可以安全地呈现网页并保持会话?

如果 Python 不合适或 JavaScript 替代方案更优雅,我也愿意完全在节点中完成它。看起来node-dom 可能就足够了?我对它还不是很熟悉,但我对任何建议都很感兴趣。

【问题讨论】:

  • 也许您可以使用 Chrome 中的“开发人员工具”来分析浏览器和服务器之间的流量,以找到 javascript 用于从服务器获取数据的 url。然后你可以在 Python 脚本中使用这个 url 而无需渲染页面。
  • btw:添加网页网址以获得更好的答案。
  • URL 是通过嵌入式 JS 在浏览器客户端确定的,例如<script type="text/javascript">.*</script> 并且不与服务器交互。因此,接受页面的 HTML 源代码并返回呈现的源代码(如 WebKit)的工具将是完美的。也许我会看看我是否可以让 WebKit 接受页面源而不是页面 URL...

标签: javascript python node.js web-scraping


【解决方案1】:

如果直接命令行选项适合您,而不是通过 Python 和/或 Selenium,Google Chrome 可以在无头模式下运行。它会在转储 DOM 之前完成所有的 javascript 渲染。

/usr/local/bin/google-chrome \
  --headless \
  --virtual-time-budget=10000 \
  --timeout=10000 \
  --run-all-compositor-stages-before-draw \
  --user-agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36" \
  --disable-gpu \
  --dump-dom "https://example.com/index.html" > rendered.html

【讨论】:

    【解决方案2】:

    Python 2 或 3 中的 PyQt5 在这种情况下可以解决问题。请注意,该函数过于复杂,以支持使用 WebKit 的早期版本的 PyQt5 以及使用 WebEngine 的更高版本。

    import sys
    
    
    def render(source_html):
        """Return rendered HTML."""
        try:
            from PyQt5.QtCore import QEventLoop
            from PyQt5.QtWebEngineWidgets import QWebEngineView
            from PyQt5.QtWidgets import QApplication
    
            class Render(QWebEngineView):
                """Render HTML with PyQt5 WebEngine."""
    
                def __init__(self, html):
                    self.html = None
                    self.app = QApplication(sys.argv)
                    QWebEngineView.__init__(self)
                    self.loadFinished.connect(self._loadFinished)
                    self.setHtml(html)
                    while self.html is None:
                        self.app.processEvents(
                            QEventLoop.ExcludeUserInputEvents |
                            QEventLoop.ExcludeSocketNotifiers |
                            QEventLoop.WaitForMoreEvents)
                    self.app.quit()
    
                def _callable(self, data):
                    self.html = data
    
                def _loadFinished(self, result):
                    self.page().toHtml(self._callable)
        except ImportError:
            from PyQt5.QtWebKitWidgets import QWebPage
            from PyQt5.QtWidgets import QApplication
    
            class Render(QWebPage):
                """Render HTML with PyQt5 WebKit."""
    
                def __init__(self, html):
                    self.html = None
                    self.app = QApplication(sys.argv)
                    QWebPage.__init__(self)
                    self.loadFinished.connect(self._loadFinished)
                    self.mainFrame().setHtml(html)
                    self.app.exec_()
    
                def _loadFinished(self, result):
                    self.html = self.mainFrame().toHtml()
                    self.app.quit()
    
        return Render(source_html).html
    

    或 Python 2 中的 PyQt4。

    import sys
    from PyQt4.QtGui import QApplication
    from PyQt4.QtWebKit import QWebPage
    
    
    class Render(QWebPage):
    
        """Fully render HTML, JavaScript and all."""
    
        def __init__(self, html):
            self.app = QApplication(sys.argv)
            QWebPage.__init__(self)
            self.loadFinished.connect(self._loadFinished)
            self.mainFrame().setHtml(html)
            self.app.exec_()
    
        def _loadFinished(self, result):
            self.frame = self.mainFrame()
            self.app.quit()
    
    render = Render(html)
    result = str(render.frame.toHtml().toAscii())
    

    【讨论】:

    • 我无法运行你的代码,因为我在 PyQt5 中找不到 QtWebKitWidgets 模块
    • @uday 你运行的是什么版本的 PyQt5?原始代码是为 v5.4.1 编写的。 WebKit 已经被弃用,取而代之的是 WebEngine(它有很大的不同并且使用异步 API),所以这可以解释为什么你不能运行它。我更新了示例以支持最高 v5.6。我还没有机会在 v5.7 上对其进行测试,所以如果您还有其他问题,请告诉我。
    • 非常感谢更新版本。我想我有 v5.6。我尝试了您的新功能,但它似乎不起作用。例如,我试图从这个页面中删除表格:nasdaqomxnordic.com/optionsandfutures/…(顺便说一句,由于查询,表格可能需要一段时间才能加载到该网页上)
    • @uday Jsdom 似乎对该网站也有问题。例如。 npm install jsdom && node -e "require('jsdom').env({url: 'http://www.nasdaqomxnordic.com/optionsandfutures/microsite?Instrument=SE0000337842', scripts: ['http://code.jquery.com/jquery.js'], done: function(e, w) {console.log(w.\$('#optionsAndFuturesTable').text())}, features: {FetchExternalResources: ['script', 'frame', 'iframe', 'link'], ProcessExternalResources: ['script']}})" 应该打印出 jQuery 选择器 #optionsAndFuturesTable 的结果,但它是静默的。抱歉,我不确定。
    • 我不明白你想说什么
    猜你喜欢
    • 2020-11-22
    • 2018-01-12
    • 2013-10-12
    • 2019-06-13
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-05
    相关资源
    最近更新 更多