如何用Python抓取动态内容(JavaScript生成)的页面?

281 投票
18 回答
474507 浏览
提问于 2025-04-17 05:51

我正在尝试开发一个简单的网页抓取工具。我想提取没有HTML标记的纯文本。我的代码在普通的(静态)HTML上能正常工作,但在页面中嵌入了JavaScript生成的内容时就不行了。

具体来说,当我使用 urllib2.urlopen(request) 来读取页面内容时,它不会显示任何由JavaScript代码添加的内容,因为那段代码在我的程序中根本没有被执行。通常,这段代码会在网页浏览器中运行,但这不是我程序的一部分。

我该如何在我的Python代码中访问这些动态内容呢?


另见 Scrapy能否用于抓取使用AJAX的网站的动态内容?,这里有关于Scrapy的具体答案。

18 个回答

63

也许selenium可以做到这一点。

from selenium import webdriver
import time

driver = webdriver.Firefox()
driver.get(url)
time.sleep(5)
htmlSource = driver.page_source
123

我们没有得到正确的结果,因为任何由JavaScript生成的内容都需要在DOM上渲染。当我们获取一个HTML页面时,我们得到的是最初的、没有被JavaScript修改的DOM。

因此,我们需要在抓取页面之前先渲染JavaScript内容。

由于在这个讨论中已经提到过很多次Selenium(也提到过它有时会很慢),我将列出另外两个可能的解决方案。


解决方案1:这是一个很好的教程,介绍了如何使用Scrapy抓取JavaScript生成的内容,我们将按照这个教程进行。

我们需要准备:

  1. 在我们的机器上安装Docker。这比其他解决方案更有优势,因为它使用的是一个与操作系统无关的平台。

  2. 按照我们对应操作系统的说明安装Splash
    引用Splash文档:

    Splash是一个JavaScript渲染服务。它是一个轻量级的网页浏览器,带有HTTP API,使用Python 3和Twisted以及QT5实现。

    简单来说,我们将使用Splash来渲染JavaScript生成的内容。

  3. 运行Splash服务器:sudo docker run -p 8050:8050 scrapinghub/splash

  4. 安装scrapy-splash插件:pip install scrapy-splash

  5. 假设我们已经创建了一个Scrapy项目(如果没有,我们来创建一个),我们将按照指南更新settings.py

    然后去你的Scrapy项目的settings.py文件,设置这些中间件:

    DOWNLOADER_MIDDLEWARES = {
          'scrapy_splash.SplashCookiesMiddleware': 723,
          'scrapy_splash.SplashMiddleware': 725,
          'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
    }
    

    Splash服务器的URL(如果你使用的是Windows或OSX,这应该是Docker机器的URL:如何从主机获取Docker容器的IP地址?):

    SPLASH_URL = 'http://localhost:8050'
    

    最后你还需要设置这些值:

    DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
    HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'
    
  6. 最后,我们可以使用SplashRequest

    在一个普通的爬虫中,你有Request对象可以用来打开URL。如果你想打开的页面包含JavaScript生成的数据,你必须使用SplashRequest(或SplashFormRequest)来渲染页面。这里有一个简单的例子:

    class MySpider(scrapy.Spider):
        name = "jsscraper"
        start_urls = ["http://quotes.toscrape.com/js/"]
    
        def start_requests(self):
            for url in self.start_urls:
            yield SplashRequest(
                url=url, callback=self.parse, endpoint='render.html'
            )
    
        def parse(self, response):
            for q in response.css("div.quote"):
            quote = QuoteItem()
            quote["author"] = q.css(".author::text").extract_first()
            quote["quote"] = q.css(".text::text").extract_first()
            yield quote
    

    SplashRequest将URL渲染为HTML,并返回响应,你可以在回调(parse)方法中使用。


解决方案2:目前我们称之为实验性(2018年5月)...
这个解决方案仅适用于Python 3.6(目前)。

你知道requests模块吗(谁不知道呢)?
现在它有一个小兄弟:requests-HTML

这个库旨在让解析HTML(例如抓取网页)尽可能简单和直观。

  1. 安装requests-html:pipenv install requests-html

  2. 向页面的URL发出请求:

    from requests_html import HTMLSession
    
    session = HTMLSession()
    r = session.get(a_page_url)
    
  3. 渲染响应以获取JavaScript生成的部分:

    r.html.render()
    

最后,这个模块似乎提供了抓取功能
另外,我们可以尝试使用BeautifulSoup和我们刚刚渲染的r.html对象,方法也很清晰。

247

编辑于2021年9月:phantomjs 现在已经不再维护了。

编辑于2017年12月30日:这个回答在谷歌搜索中排名靠前,所以我决定更新一下。旧的回答仍然在最后。

dryscape 也不再维护了,而它推荐的库只支持 Python 2。我发现使用 Selenium 的 Python 库配合 Phantom JS 作为网页驱动,速度足够快,而且容易完成工作。

一旦你安装了 Phantom JS,确保 phantomjs 这个程序可以在当前路径下找到:

phantomjs --version
# result:
2.1.1

#示例 举个例子,我创建了一个包含以下 HTML 代码的示例页面。(链接):

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Javascript scraping test</title>
</head>
<body>
  <p id='intro-text'>No javascript support</p>
  <script>
     document.getElementById('intro-text').innerHTML = 'Yay! Supports javascript';
  </script> 
</body>
</html>

没有 JavaScript 时,它显示:没有 JavaScript 支持;有 JavaScript 时,它显示:太好了!支持 JavaScript

#没有 JS 支持的抓取:

import requests
from bs4 import BeautifulSoup
response = requests.get(my_url)
soup = BeautifulSoup(response.text)
soup.find(id="intro-text")
# Result:
<p id="intro-text">No javascript support</p>

#有 JS 支持的抓取:

from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get(my_url)
p_element = driver.find_element_by_id(id_='intro-text')
print(p_element.text)
# result:
'Yay! Supports javascript'

你还可以使用 Python 库 dryscrape 来抓取基于 JavaScript 的网站。

#有 JS 支持的抓取:

import dryscrape
from bs4 import BeautifulSoup
session = dryscrape.Session()
session.visit(my_url)
response = session.body()
soup = BeautifulSoup(response)
soup.find(id="intro-text")
# Result:
<p id="intro-text">Yay! Supports javascript</p>

撰写回答