如何从JavaScript呈现的网页下载?

时间:2016-02-06 13:58:33

标签: javascript python node.js web-scraping

如何从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可能就足够了?我不是很熟悉它,但我对任何建议感兴趣。

1 个答案:

答案 0 :(得分:-1)

在这种情况下,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())