如何用Python解析网页中的动态JavaScript?

1 投票
1 回答
4349 浏览
提问于 2025-04-16 16:08

我正在制作一个爬虫,使用Beautiful Soup来解析特定网址的内容。现在,有些网站使用JavaScript来显示动态内容,这些内容只有在用户进行某些操作(比如点击或等待一段时间)后才会出现。Beautiful Soup只能解析在JavaScript标签运行之前的静态内容。我想要的是JavaScript运行后的内容。有没有什么办法可以做到这一点呢?

我想到了一种方法:获取网址,打开浏览器,运行这个网址和JavaScript标签。然后把这个网址传给Beautiful Soup,这样它就能看到JavaScript生成的动态内容了。不过,如果我需要爬取数百万个链接,这个方法就不太实用了。如果有一些内置的模块可以提前生成HTML页面的动态内容就好了。

1 个回答

2

如果你想准确地从网页中解析带有JavaScript的内容,最好的办法是通过浏览器引擎来加载页面。幸运的是,在Python中有一些方法可以自动化这个过程。

我最成功的方法是使用pywebkitgtk项目,这个项目允许你在Python应用程序中以编程方式创建和控制Webkit浏览器引擎的实例。我还使用jswebkit模块,这样可以更简单地在网页上下文中执行JavaScript。

另一个选择是PyQt4的QtWebKit类,我只在实验中使用过它。

下面是一个使用pywebkitgtk和jswebkit一起从Webkit渲染的页面中提取数据的示例。在实际应用中,你可能希望并行运行多个这样的处理器,每个处理器渲染到自己的X虚拟帧缓冲区(Xvfb)实例中。

import os

import gtk
import jswebkit
import lxml.html
import pygtk
import webkit

def load_finished(view, frame):
    # called when the document finishes loading
    if frame != view.get_main_frame():
        return
    ctx = jswebkit.JSContext(frame.get_global_context())
    res = ctx.EvaluateScript('window.location.href')
    print res
    res = ctx.EvaluateScript('document.body.innerHTML')
    tree = lxml.html.fromstring(res)
    print tree.xpath('//input[@type="submit"]')

# initialization
pygtk.require20()
gtk.gdk.threads_init()

# create the webview and hook up callbacks to signals
view = webkit.WebView()
view.set_size_request(1024, 768)
view.connect('load-finished', load_finished)

# configure the webview
props = view.get_settings()
props.set_property('enable-java-applet', False)
props.set_property('enable-plugins', False)
props.set_property('enable-page-cache', False)

# create a window to host the webview
win = gtk.Window()
win.add(view)
win.show_all()

# open google front page
view.open('http://www.google.com')

# spin, processing gtk events
while True:
    try:
        while gtk.events_pending():
            gtk.main_iteration(False)
    except KeyboardInterrupt:
        break

示例输出:

http://www.google.com/
[<InputElement 2a64a78 name='btnG' type='submit'>, <InputElement 2a64bb0 name='btnG' type='submit'>, <InputElement 2a64ae0 name='btnI' type='submit'>]

撰写回答