杰瑞科技汇

Python如何爬取JavaScript渲染的页面?

传统的爬虫库(如 requests + BeautifulSoup)只能获取网页的初始 HTML 源代码,如果页面的内容是通过 JavaScript 动态加载的(比如点击“加载更多”后出现的内容,或者数据是通过 API 请求异步获取的),requests 就拿不到这些数据。

解决这个问题主要有两大主流方案:

  1. 模拟浏览器(无头浏览器):使用工具(如 Selenium, Playwright)来控制一个真实的浏览器(如 Chrome 或 Firefox),浏览器会执行 JavaScript,渲染出完整的页面,然后我们从中提取数据,这是最通用、最强大的方法。
  2. 直接调用 API:分析网页的 JavaScript 代码,找到它用来加载数据的 API 接口,我们直接在 Python 中模拟发送这个 API 请求,获取 JSON 数据,这种方法更快、更高效,但需要一些逆向工程的技巧。

下面我将详细介绍这两种方法,并提供完整的代码示例。


使用 Selenium 模拟浏览器(推荐入门)

Selenium 是最流行的浏览器自动化测试工具,它也可以用来爬取由 JavaScript 渲染的页面,它会打开一个浏览器窗口(可以设置为“无头模式”即后台运行),访问网页,等待页面加载完成,然后你就可以像操作真实浏览器一样定位和提取元素。

步骤 1:安装必要的库

你需要安装 selenium 和一个 浏览器驱动,驱动是 Selenium 用来控制浏览器的“桥梁”。

# 安装 selenium 库
pip install selenium
# 安装浏览器驱动 (以 Chrome 为例)
# 1. 确保你已安装 Google Chrome 浏览器
# 2. 下载对应版本的 ChromeDriver: https://googlechromelabs.github.io/chrome-for-testing/
# 3. 将 chromedriver.exe (Windows) 或 chromedriver (Mac/Linux) 放到你的项目目录或系统 PATH 中
# (或者使用 webdriver-manager 自动管理驱动,强烈推荐!)
pip install webdriver-manager

步骤 2:编写 Python 代码

下面是一个使用 webdriver-manager 自动管理驱动的完整示例,它会爬取 https://quotes.toscrape.com/js/ 这个专门用来练习 JS 爬虫的网站。

import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
# --- 配置浏览器选项 ---
chrome_options = Options()
# 1. 无头模式:在后台运行,不弹出浏览器窗口
chrome_options.add_argument("--headless")
# 2. 禁用 GPU 加速,在某些系统上可以避免问题
chrome_options.add_argument("--disable-gpu")
# 3. 窗口大小
chrome_options.add_argument("--window-size=1920,1080")
# --- 初始化 WebDriver ---
# 使用 webdriver-manager 自动下载并管理驱动
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
try:
    # --- 访问目标网页 ---
    print("正在访问网页...")
    url = "https://quotes.toscrape.com/js/"
    driver.get(url)
    # --- 等待和提取数据 ---
    # 方法一:等待元素出现(推荐)
    # 使用 WebDriverWait 显式等待,比 time.sleep() 更智能、更稳定
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    print("等待页面加载完成...")
    # 等待至少一个 quote 元素被加载
    wait = WebDriverWait(driver, 10) # 最多等待10秒
    quotes = wait.until(EC.presence_of_all_elements_located((By.CLASS_NAME, "quote")))
    print(f"找到 {len(quotes)} 条名言。")
    # 遍历提取数据
    for quote in quotes:
        text = quote.find_element(By.CLASS_NAME, "text").text
        author = quote.find_element(By.CLASS_NAME, "author").text
        print(f"文本: {text}")
        print(f"作者: {author}\n")
    # 方法二:简单粗暴的等待(不推荐,仅用于演示)
    # time.sleep(5) # 等待5秒,给 JS 足够的时间渲染
    # quotes = driver.find_elements(By.CLASS_NAME, "quote")
    # print(f"找到 {len(quotes)} 条名言。")
finally:
    # --- 关闭浏览器 ---
    print("任务完成,关闭浏览器。")
    driver.quit()

代码解释:

  1. 配置选项Options 用于配置 Chrome 浏览器,--headless 是关键,它让浏览器在后台运行。
  2. 初始化 WebDriverChromeDriverManager() 会自动检测你的 Chrome 版本并下载对应的驱动,省去了手动下载和配置的麻烦。
  3. driver.get(url):命令浏览器打开指定 URL。
  4. 等待与定位
    • WebDriverWaitexpected_conditions:这是 Selenium 的核心功能,它会一直等待,直到某个条件(比如某个元素出现)被满足,或者超时,这比 time.sleep() 更可靠,因为它不会在页面还没加载好时就执行下一步。
    • By.CLASS_NAME:通过 CSS 类名来定位元素,这是最常用的定位方式之一。
  5. 提取数据:使用 find_elementfind_elements 在已定位的父元素(如 quote)中查找子元素,并获取其 .text 属性。
  6. driver.quit():非常重要!确保在最后关闭浏览器,释放资源。

直接调用 API(更高效)

很多时候,前端 JavaScript 会通过 fetchXMLHttpRequest (XHR) 请求一个后端 API 来获取数据,这个 API 通常返回的是 JSON 格式的数据,直接、干净,没有多余的 HTML 标签。

步骤 1:分析网页,找到 API

  1. 在目标网页上右键,选择“检查”打开开发者工具。
  2. 切换到 “网络”(Network) 标签页。
  3. 勾选 “保留日志”(Preserve log)“禁用缓存”(Disable cache)
  4. 刷新页面。
  5. 在网络请求列表中,寻找类型为 XHRFetch 的请求,并且响应内容是 JSON 格式的,你可以通过点击请求,在“响应”(Response)或“预览”(Preview)标签页查看。

https://quotes.toscrape.com/js/ 为例,我们可以在网络标签页发现一个名为 quotes?page=1 的请求,它的响应正是我们需要的 JSON 数据。

步骤 2:编写 Python 代码

一旦找到了 API,我们就可以用 requests 库直接请求它。

import requests
import json
# --- 找到的 API URL ---
# 注意:这个 URL 可能包含查询参数来控制分页等
api_url = "https://quotes.toscrape.com/api/quotes?page=1"
try:
    print(f"正在请求 API: {api_url}")
    # 发送 GET 请求
    response = requests.get(api_url)
    # 检查请求是否成功
    response.raise_for_status()  # 如果请求失败 (404, 500), 会抛出异常
    # 解析 JSON 数据
    data = response.json()
    print(f"成功获取数据,当前页面: {data['page']}, 总页数: {data['pages']}")
    # 提取并打印名言
    for quote in data['quotes']:
        text = quote['text']
        author = quote['author']
        print(f"文本: {text}")
        print(f"作者: {author}\n")
except requests.exceptions.RequestException as e:
    print(f"请求 API 失败: {e}")
except json.JSONDecodeError:
    print("响应内容不是有效的 JSON 格式。")

代码解释:

  1. requests.get():向 API 发送一个 HTTP GET 请求。
  2. response.raise_for_status():检查 HTTP 状态码,如果是 4xx 或 5xx 错误,则抛出异常。
  3. response.json():将响应内容直接解析为 Python 字典或列表,非常方便。
  4. 数据提取:直接操作 Python 字典来获取数据,比解析 HTML 简单得多。

总结与对比

特性 Selenium (模拟浏览器) 直接调用 API
原理 控制真实浏览器,执行 JS 渲染页面 直接向服务器发送数据请求
优点 通用性强,能处理任何 JS 渲染逻辑;能处理复杂的用户交互(点击、输入、滚动)。 速度快,资源消耗小;数据格式纯净(JSON),易于解析;更稳定,不易受前端代码变动影响。
缺点 速度慢,资源消耗大(需要启动浏览器);配置相对复杂;网页结构变动可能导致爬虫失效。 需要逆向工程,找到 API 接口;可能遇到反爬机制(如 API 的鉴权、签名);API 可能随时被网站修改或废弃。
适用场景 - 页面内容由复杂的 JS 逻辑生成,难以找到 API。
- 需要模拟用户登录、点击、拖拽等交互。
- 爬取 SPA(单页应用)网站。
- 网站数据通过 API 加载,且 API 容易发现。
- 对爬取速度和效率要求高。
- 只需要获取特定数据,不关心页面渲染效果。

最佳实践建议

  1. 优先尝试 API 方法:这是最高效、最稳定的方式,花几分钟在开发者工具里找找 API,通常会有惊喜。
  2. API 失败再使用 Selenium:如果找不到合适的 API,或者 API 有复杂的反爬机制(如动态生成的 token),再使用 Selenium。
  3. 结合使用:有时,你可能需要先用 Selenium 登录或完成某个操作,拿到登录后的 cookietoken,然后再用 requests 去请求需要这些认证信息的 API。

希望这个详细的指南能帮助你掌握 Python 爬取 JavaScript 渲染页面的技术!

分享:
扫描分享到社交APP
上一篇
下一篇