Python 爬虫技术 第19节 Selenium和动态网页抓取

Selenium 是一个强大的自动化测试工具,它最初是为了进行 web 应用的功能性测试而设计的。然而,由于它可以模拟真实用户的行为与浏览器交互,因此也被广泛应用于动态网页的爬取中。

在处理动态网页时,传统的爬虫方法(如使用requestsBeautifulSoup)可能无法获取到完整的页面内容,因为这些页面的内容是通过 JavaScript 动态加载的。Selenium 可以驱动浏览器执行 JavaScript 代码,从而能够获取到完全加载后的页面内容。

下面是一个使用 Python 和 Selenium 进行动态网页抓取的基本步骤:

安装必要的库

首先确保安装了 selenium 库。可以使用 pip 安装:

pip install selenium

还需要下载与你的浏览器版本兼容的 WebDriver。例如,如果你使用的是 Chrome 浏览器,那么你需要下载 ChromeDriver 并将其路径添加到系统 PATH 中。

示例代码

这里是一个简单的示例,演示如何使用 Selenium 抓取一个动态生成的网页:

  1. 导入必要的模块:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
  1. 设置 WebDriver:
options = webdriver.ChromeOptions()
# 如果需要无头模式(不打开浏览器窗口)
# options.add_argument('headless')
driver = webdriver.Chrome(options=options)
  1. 导航到目标网址:
url = 'https://example.com'
driver.get(url)
  1. 等待元素加载:
try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "some_element_id"))
    )
finally:
    print("Element loaded.")
  1. 提取数据:
data = driver.find_elements(By.CSS_SELECTOR, '.some_class .another_class')
for item in data:
    print(item.text)
  1. 关闭浏览器:
driver.quit()

解释

  • WebDriver: Selenium 使用 WebDriver 与浏览器交互。在上面的例子中,我们使用了 ChromeDriver。
  • 等待元素: 使用 WebDriverWaitexpected_conditions 来等待某些元素加载完成,这有助于避免因为元素未加载完成而导致的错误。
  • 提取数据: 使用 find_elements 方法来查找页面上的元素,并从中提取数据。

注意事项

  • 性能问题: 使用 Selenium 爬取动态网站会比传统的方法慢得多,因为它实际上是在启动一个真实的浏览器实例。
  • 反爬虫策略: 许多网站都有反爬虫机制,使用 Selenium 也可能会遇到 IP 封锁、验证码等问题。
  • 资源消耗: 启动浏览器实例会占用较多的系统资源,对于大规模爬取任务,可能需要考虑使用更轻量级的解决方案,或者分布式爬取。

以上就是使用 Selenium 进行动态网页抓取的一个简单介绍。如果有具体的网站想要抓取,可以根据实际情况调整上述代码中的选择器和等待条件等。

当然可以。让我们基于之前的示例进一步扩展代码,实现一个更加完整的动态网页抓取脚本。在这个例子中,我们将抓取一个假设的网站,该网站在滚动后加载更多内容。

我们将添加如下功能:

  1. 模拟滚动页面到底部,以便加载所有内容。
  2. 提取特定的数据点。
  3. 处理异常情况。

下面是扩展后的完整代码:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time

def get_driver():
    # 设置 Chrome 的选项
    options = webdriver.ChromeOptions()
    # 如果需要无头模式(不打开浏览器窗口)
    # options.add_argument('headless')
    
    # 创建 WebDriver 实例
    driver = webdriver.Chrome(options=options)
    return driver

def wait_for_element(driver, by, value, timeout=10):
    try:
        element = WebDriverWait(driver, timeout).until(
            EC.presence_of_element_located((by, value))
        )
        return element
    except TimeoutException:
        print(f"Element not found after {timeout} seconds.")
        return None

def scroll_to_bottom(driver, interval=3):
    # 获取当前页面的高度
    last_height = driver.execute_script("return document.body.scrollHeight")
    
    while True:
        # 滚动到页面底部
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        
        # 等待页面加载
        time.sleep(interval)
        
        # 计算新高度并比较
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            break
        last_height = new_height

def main():
    url = 'https://example.com'  # 替换为目标网站的 URL
    driver = get_driver()

    try:
        driver.get(url)

        # 等待特定元素加载完成
        element = wait_for_element(driver, By.ID, "some_element_id")
        if element is None:
            raise Exception("Required element not found.")

        # 模拟滚动加载更多内容
        scroll_to_bottom(driver)

        # 提取数据
        data_elements = driver.find_elements(By.CSS_SELECTOR, ".item-class")
        for element in data_elements:
            print(element.text)
    
    except Exception as e:
        print(f"An error occurred: {e}")
    finally:
        driver.quit()

if __name__ == "__main__":
    main()

代码解释

  1. get_driver(): 设置 Chrome 驱动程序。
  2. wait_for_element(): 等待指定的元素出现。
  3. scroll_to_bottom(): 模拟滚动至页面底部的动作。
    • 使用 JavaScript 执行 window.scrollTo(0, document.body.scrollHeight); 来滚动页面。
    • 每次滚动后等待一段时间让页面加载新内容。
    • 重复这个过程直到页面不再增长。
  4. main(): 主函数,包含整个流程。
    • 初始化 WebDriver。
    • 导航到目标网站。
    • 等待关键元素出现。
    • 滚动页面到底部。
    • 提取数据。
    • 关闭 WebDriver。

注意事项

  • 异常处理: 我们使用了 try-except 块来捕获可能出现的异常,比如元素找不到或网络连接问题。
  • 性能优化: 对于大型网站,滚动间隔时间可能需要适当增加,以确保页面有足够的时间加载所有内容。
  • 资源管理: 在脚本结束时,确保通过 driver.quit() 释放资源。

请根据实际的目标网站调整 CSS 选择器、等待条件等。如果需要进一步的帮助,请告诉我具体的目标网站和想要抓取的数据类型。

好的,我们可以进一步完善代码,使其更加健壮并且能够处理更多的异常情况。同时,我们可以加入一些额外的功能,比如日志记录和重试机制。

以下是改进后的代码:

import logging
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

def setup_logger(name, log_file, level=logging.INFO):
    """创建日志记录器"""
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    handler = logging.FileHandler(log_file)
    handler.setFormatter(formatter)
    
    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.addHandler(handler)
    
    return logger

def get_driver():
    """初始化 WebDriver"""
    options = webdriver.ChromeOptions()
    # options.add_argument('headless')  # 无头模式
    
    # 自动管理 ChromeDriver 版本
    service = Service(ChromeDriverManager().install())
    driver = webdriver.Chrome(service=service, options=options)
    
    return driver

def wait_for_element(driver, by, value, timeout=10):
    """等待元素出现"""
    try:
        element = WebDriverWait(driver, timeout).until(
            EC.presence_of_element_located((by, value))
        )
        return element
    except TimeoutException:
        logger.error(f"Element not found after {timeout} seconds.")
        return None

def scroll_to_bottom(driver, interval=3, max_attempts=3):
    """滚动到页面底部,直到没有新内容加载为止"""
    attempts = 0
    last_height = driver.execute_script("return document.body.scrollHeight")

    while attempts < max_attempts:
        # 滚动到页面底部
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

        # 等待页面加载
        time.sleep(interval)

        # 计算新高度并比较
        new_height = driver.execute_script("return document.body.scrollHeight")
        if new_height == last_height:
            attempts += 1
        else:
            attempts = 0
            last_height = new_height

    if attempts >= max_attempts:
        logger.warning("Reached maximum scroll attempts without new content.")

def extract_data(driver):
    """提取页面上的数据"""
    try:
        data_elements = driver.find_elements(By.CSS_SELECTOR, ".item-class")
        for element in data_elements:
            print(element.text)
    except NoSuchElementException:
        logger.error("No data elements found.")

def main():
    url = 'https://example.com'  # 替换为目标网站的 URL
    logger = setup_logger('web_scraper', 'scraper.log')

    driver = get_driver()
    
    try:
        driver.get(url)

        # 等待特定元素加载完成
        element = wait_for_element(driver, By.ID, "some_element_id")
        if element is None:
            raise Exception("Required element not found.")

        # 模拟滚动加载更多内容
        scroll_to_bottom(driver)

        # 提取数据
        extract_data(driver)
    
    except Exception as e:
        logger.error(f"An error occurred: {e}")
    finally:
        driver.quit()

if __name__ == "__main__":
    main()

新增功能

  1. 日志记录:

    • 使用 logging 模块记录日志信息。
    • 日志文件名为 scraper.log,你可以根据需要更改文件名和日志级别。
  2. 自动管理 ChromeDriver:

    • 使用 webdriver_manager 自动安装和管理 ChromeDriver,无需手动下载和配置路径。
  3. 重试机制:

    • scroll_to_bottom 函数中增加了最大尝试次数,以防止无限循环。
  4. 异常处理:

    • extract_data 函数中加入了对 NoSuchElementException 的处理。
  5. 更详细的错误信息:

    • 当发生异常时,将详细信息记录到日志文件中。

使用说明

  • 日志文件: 更改 setup_logger 函数中的 log_file 参数来指定日志文件的位置。
  • 无头模式: 若要启用无头模式(即不显示浏览器窗口),取消注释 options.add_argument('headless') 行。
  • 目标网站: 替换 url 变量中的 URL 为你想要抓取的实际网站地址。
  • CSS 选择器: 根据实际的 HTML 结构修改 .item-class 选择器。

请根据实际需求调整这些参数,并确保你的环境已经安装了必要的库(如 webdriver_manager)。如果需要进一步的帮助或有其他具体需求,请随时告诉我。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值