使用Python和Selenium抓取动态网页数据教程

使用Python和Selenium抓取动态网页数据教程

本教程旨在指导读者如何使用python结合selenium和beautifulsoup库,有效抓取包含切换按钮等动态交互元素的网页数据。文章将详细阐述传统静态网页抓取方法在处理此类场景时的局限性,并提供一套完整的解决方案,通过模拟用户浏览器行为来获取动态加载的内容,最终实现对目标数据的精确提取。

在现代网页应用中,许多内容并非在页面首次加载时就全部呈现,而是通过JavaScript动态生成或在用户交互(如点击按钮、滚动页面)后加载。对于这类动态网页,仅依赖requests库获取原始HTML并结合BeautifulSoup进行解析的方法往往会失效,因为它只能获取到页面初始的静态内容,而无法捕获JavaScript执行后的变化。本教程将以抓取一个包含“每场比赛数据”切换按钮的体育统计网站为例,详细介绍如何利用Selenium模拟浏览器行为,进而获取动态数据。

理解动态网页抓取的挑战

当目标网站(例如https://www.sports-reference.com/cbb/seasons/men/2024-school-stats.html)中的数据需要通过点击一个切换按钮(如“Per Game”与“Total Stats”之间的切换)才能显示时,requests库获取的HTML源代码将只包含默认状态下的数据。这是因为JavaScript负责处理按钮点击事件并更新DOM,而requests不执行JavaScript。此时,我们需要一个能够模拟真实浏览器环境并执行JavaScript的工具——Selenium。

引入Selenium进行浏览器自动化

Selenium是一个强大的Web自动化测试工具,但它也能被广泛用于网页抓取。通过Selenium,我们可以启动一个真实的浏览器实例(如Chrome、Firefox),控制其导航到指定URL,模拟用户点击、输入等交互行为,并最终获取到JavaScript执行后的页面内容。

1. 环境准备

首先,确保你的Python环境中安装了必要的库:

立即学习“Python免费学习笔记(深入)”;

pip install selenium beautifulsoup4 pandas requests

此外,你需要下载对应浏览器的WebDriver。以Chrome为例,你需要下载chromedriver,并将其放置在系统PATH中,或者在代码中指定其路径。chromedriver的版本必须与你安装的Chrome浏览器版本相匹配。

2. 初始化WebDriver

在使用Selenium之前,需要初始化一个WebDriver实例。这里我们以Chrome为例:

from selenium import webdriverfrom selenium.webdriver.chrome.options import Options# 配置Chrome选项chrome_options = Options()# 可以选择添加无头模式,使浏览器在后台运行,不显示GUI# chrome_options.add_argument('--headless')# 最大化窗口,有时有助于确保元素可见chrome_options.add_argument('--start-maximized')# 禁用GPU加速,在某些环境下可能避免问题chrome_options.add_argument('--disable-gpu')# 禁用沙箱模式,在某些Linux环境中可能需要# chrome_options.add_argument('--no-sandbox')# 初始化Chrome WebDriver# 如果chromedriver不在PATH中,需要指定executable_pathdriver = webdriver.Chrome(options=chrome_options)

3. 导航到目标URL

使用driver.get()方法让浏览器导航到目标网页:

url = 'https://www.sports-reference.com/cbb/seasons/men/2024-school-stats.html'driver.get(url)

4. 定位并模拟点击切换按钮

这是抓取动态内容的关键步骤。我们需要找到负责切换“Per Game”数据的按钮,并模拟点击它。为了确保页面元素已经加载完成,我们应该使用显式等待(WebDriverWait)。

首先,通过浏览器开发者工具检查目标按钮的HTML结构,找到其唯一的标识符(如ID、class或XPath)。在本例中,切换“每场比赛数据”的按钮ID为basic_school_stats_per_match_toggle。

from selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as EC# 设置显式等待,最长等待20秒wait = WebDriverWait(driver, 20)# 等待切换按钮出现并可点击# 使用By.ID定位元素toggle_button = wait.until(EC.element_to_be_clickable((By.ID, 'basic_school_stats_per_match_toggle')))# 模拟点击按钮# 可以直接使用 .click() 方法toggle_button.click()# 或者使用JavaScript执行点击,有时更稳定# driver.execute_script("arguments[0].click();", toggle_button)# 等待页面内容更新,例如等待某个关键元素出现或数据加载完成# 这里我们假设点击后数据会立即更新,如果页面有异步加载,可能需要更长的等待或等待特定数据元素wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table.modified'))) # 假设表格内容会更新

5. 获取更新后的页面HTML

按钮点击并页面内容更新后,我们可以通过driver.page_source获取当前页面的完整HTML源代码,其中包含了动态加载的数据。

html_source = driver.page_source

6. 关闭WebDriver

完成操作后,务必关闭浏览器实例以释放资源:

driver.quit()

使用BeautifulSoup解析动态内容

现在我们已经获得了包含动态数据的HTML源代码,接下来的步骤与静态网页抓取类似,使用BeautifulSoup进行解析和数据提取。

from bs4 import BeautifulSoupimport pandas as pddef parse_dynamic_html(html_content):    soup = BeautifulSoup(html_content, 'html.parser')    # 尝试找到包含统计数据的表格    # 根据页面结构,这里可能需要更精确的定位    table = soup.find('table', class_='modified') # 假设切换后表格的class是'modified'    if not table:        print("未找到目标表格。")        return []    team_stats_list = []    # 提取表头    headers = [th.text.strip() for th in table.find('thead').find_all('th') if th.text.strip()]    # 遍历表格的每一行数据    for row in table.find('tbody').find_all('tr'):        # 排除可能是表头的行,或者根据实际情况调整        if row.find('th', {'data-stat': 'ranker'}): # 跳过排名行            continue        team_data = {}        # 遍历行中的每个数据单元格        for cell in row.find_all(['th', 'td']): # th可能包含队伍名称            stat_name = cell.get('data-stat')            if stat_name:                team_data[stat_name] = cell.text.strip()        # 过滤并提取我们需要的“每场比赛”数据        if team_data.get('school_name'): # 确保是有效数据行            extracted_stats = {                'Team': team_data.get('school_name', 'N/A'),                'FG%': team_data.get('fg_pct', 'N/A'),                'ORB/G': team_data.get('orb_per_g', 'N/A'), # 注意这里是per_g后缀                'AST/G': team_data.get('ast_per_g', 'N/A'),                'STL/G': team_data.get('stl_per_g', 'N/A'),                'BLK/G': team_data.get('blk_per_g', 'N/A'),                'TOV/G': team_data.get('tov_per_g', 'N/A'),                'PF/G': team_data.get('pf_per_g', 'N/A')            }            team_stats_list.append(extracted_stats)    return team_stats_list# 将数据转换为DataFrame并保存def save_to_csv(data, filename='team_per_game_stats.csv'):    if data:        df = pd.DataFrame(data)        df.to_csv(filename, index=False, encoding='utf-8')        print(f"数据已成功保存到 {filename}")    else:        print("没有数据可保存。")

完整代码示例

将上述步骤整合,形成一个完整的抓取脚本:

from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECfrom bs4 import BeautifulSoupimport pandas as pdimport timedef scrape_dynamic_sports_stats(url):    chrome_options = Options()    # 启用无头模式,在后台运行浏览器,不显示图形界面    chrome_options.add_argument('--headless')     chrome_options.add_argument('--start-maximized')    chrome_options.add_argument('--disable-gpu')    chrome_options.add_argument('--no-sandbox') # 适用于某些Linux环境    driver = None    try:        driver = webdriver.Chrome(options=chrome_options)        print(f"导航到URL: {url}")        driver.get(url)        wait = WebDriverWait(driver, 20) # 最长等待20秒        # 等待并点击“Per Game”切换按钮        # 按钮ID是 'basic_school_stats_per_match_toggle'        toggle_button_id = 'basic_school_stats_per_match_toggle'        print(f"尝试定位并点击切换按钮 (ID: {toggle_button_id})...")        toggle_button = wait.until(EC.element_to_be_clickable((By.ID, toggle_button_id)))        toggle_button.click()        print("切换按钮点击成功。")        # 给予页面一些时间来加载和渲染新的数据        time.sleep(3)         # 获取更新后的页面HTML        html_source = driver.page_source        print("成功获取动态页面HTML。")        # 使用BeautifulSoup解析HTML        soup = BeautifulSoup(html_source, 'html.parser')        # 找到目标表格        table = soup.find('table', class_='modified') # 根据页面检查,切换后的表格class可能仍然是modified        if not table:            print("错误:未找到目标统计表格。")            return []        team_stats_list = []        # 遍历表格行,提取数据        for row in table.find('tbody').find_all('tr'):            team_data = {}            # 确保只处理包含数据的行            if row.find('th', {'data-stat': 'ranker'}): # 跳过表头或非数据行                continue            for cell in row.find_all(['th', 'td']):                stat_name = cell.get('data-stat')                if stat_name:                    team_data[stat_name] = cell.text.strip()            # 提取我们关心的“每场比赛”数据            if team_data.get('school_name'):                extracted_stats = {                    'Team': team_data.get('school_name', 'N/A'),                    'FG%': team_data.get('fg_pct', 'N/A'),                    'ORB/G': team_data.get('orb_per_g', 'N/A'),                     'AST/G': team_data.get('ast_per_g', 'N/A'),                    'STL/G': team_data.get('stl_per_g', 'N/A'),                    'BLK/G': team_data.get('blk_per_g', 'N/A'),                    'TOV/G': team_data.get('tov_per_g', 'N/A'),                    'PF/G': team_data.get('pf_per_g', 'N/A')                }                team_stats_list.append(extracted_stats)        return team_stats_list    except Exception as e:        print(f"抓取过程中发生错误: {e}")        return []    finally:        if driver:            driver.quit() # 确保在任何情况下都关闭浏览器# 主程序执行if __name__ == "__main__":    target_url = 'https://www.sports-reference.com/cbb/seasons/men/2024-school-stats.html'    per_game_stats = scrape_dynamic_sports_stats(target_url)    if per_game_stats:        df = pd.DataFrame(per_game_stats)        csv_filename = 'team_per_game_stats_dynamic.csv'        df.to_csv(csv_filename, index=False, encoding='utf-8')        print(f"数据已成功保存到 {csv_filename}")    else:        print("未获取到任何数据。")

注意事项与最佳实践

WebDriver路径: 确保chromedriver(或其他浏览器驱动)的版本与你的浏览器版本匹配,并正确配置其路径。显式等待: 使用WebDriverWait和expected_conditions是处理动态网页的关键。它能让你的脚本在元素出现或变得可交互时才继续执行,避免因页面加载速度不一导致的错误。隐式等待: driver.implicitly_wait(seconds)可以设置一个全局的等待时间,WebDriver会在查找元素时等待指定时间,直到元素出现。但显式等待通常更精确。无头模式: 在生产环境中,建议使用–headless选项启动浏览器,这样浏览器会在后台运行,不显示图形界面,可以提高效率并减少资源消耗。User-Agent: 虽然Selenium模拟真实浏览器,但在某些情况下,网站仍可能通过JavaScript检测到自动化行为。设置合适的User-Agent有时可以帮助规避反爬机制(通过chrome_options.add_argument(“user-agent=…”))。错误处理: 使用try…except…finally块来捕获可能发生的异常,并在finally块中确保driver.quit()被调用,以避免浏览器进程残留。时间延迟: 在某些复杂交互后,可能需要使用time.sleep()进行短暂的硬性等待,以确保页面完全渲染或异步请求完成,但应尽量优先使用显式等待。元素定位: 优先使用ID、CSS选择器或XPath等精确且稳定的定位方式。避免使用过于泛化的类名或文本内容定位,因为它们可能随网站更新而改变。

总结

通过结合Selenium的浏览器自动化能力和BeautifulSoup的HTML解析能力,我们可以有效地应对动态网页抓取带来的挑战。Selenium负责模拟用户交互、等待页面加载并获取JavaScript执行后的HTML内容,而BeautifulSoup则专注于从这些内容中提取所需的数据。掌握这种组合技术,将大大扩展你的网页抓取能力,让你能够处理更复杂的现代网站。

以上就是使用Python和Selenium抓取动态网页数据教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1378977.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Python3数据类型有哪些_Python3常见数据类型全面解析
上一篇 2025年12月14日 20:15:52
多模态数据融合:EfficientNetB0与LSTM模型的构建与训练实践
下一篇 2025年12月14日 20:16:04

相关推荐

  • 途虎养车空调滤芯怎么换_途虎养车空调滤芯更换图解

    汽车空调出风量变小、有异味或制冷差可能是空调滤芯堵塞所致,可通过途虎养车更换:一、拆卸手套箱,先清空并挤压内壁脱离卡扣,拔出右侧卡扣后缓慢放下;二、打开滤芯盖板,滑动取下盖板后水平抽出旧滤芯,检查脏污情况;三、安装新滤芯需注意气流方向标识,箭头指向车辆前方或向上,确保完全插入;四、复位时先装回盖板并…

    2026年9月4日
    100
  • 电脑出现mlx4_bus.sys错误_Mellanox驱动

    mlx4_bus.sys错误通常由驱动问题、硬件故障或系统文件损坏引起。1. 驱动问题:安装了不兼容或损坏的mellanox驱动,需从nvidia官网下载匹配型号和系统的最新驱动;2. 硬件故障:网卡接触不良、pcie插槽松动、供电不足或过热等物理问题,需检查硬件连接并确保散热良好;3. 系统文件损…

    2026年9月4日
    000
  • Linux的常用命令的学习

    学习linux命令之前当然先得安装个linux,可以先虚拟机,熟练后安装到实体机器上,命令可以先从软件的安装命令,文件的操作命令开始学习。 1.学习linux命令首先是选择自己喜欢的版本。 相关推荐:http://www.php.cn/linux-376007.html 2.安装linux系统,把自…

    2026年9月4日
    300
  • 渗透测试安全检测漏洞

    最近由于渗透测试任务繁重,笔者一直未能抽出时间撰写文章。今天,我们sine安全的渗透主管将为大家普及java安全测试的基础知识。许多客户希望了解具体的javascript调用漏洞、提交payload的过程,以及如何获取最高权限、绕过登录等执行命令漏洞的安全检测方法。 4.4.1.1. 显式原型和隐式…

    2026年9月4日
    000
  • 使用本地化资源动态配置 Java Switch 语句

    本文探讨了在 Java 中如何根据不同的区域设置,动态地配置 `switch` 语句。由于 `switch` 语句的 `case` 标签必须是常量表达式,直接使用资源文件中的字符串值是不允许的。本文提供了两种解决方案:使用 `if-else if` 语句替代 `switch`,以及使用 `Map` …

    2026年9月4日
    000
  • 如何为iPhone13Pro获取固件包?最新固件下载攻略

    首先通过Apple官方iTunes或Finder下载iPhone 13 Pro固件,确保安全可靠;其次可从iStoreOS平台获取优化版固件;最后开发者可登录苹果开发者网站下载测试版固件。 如果您尝试为您的iPhone 13 Pro获取固件包,但无法找到可靠的来源,则可能是由于未访问官方或受信任的固…

    2026年9月4日
    100
  • Yandex俄罗斯官网免登录一键进入 Yandex搜索引擎免登录快速入口

    Yandex俄罗斯官网免登录一键进入,这是不少网友都关注的,接下来由PHP小编为大家带来Yandex搜索引擎免登录快速入口,感兴趣的网友一起随小编来瞧瞧吧! 1、立即进入“yandex俄罗斯官网免登录一键进入https://yande.com/☜☜☜☜☜”; 2、立即进入“Yandex搜索引擎免登录…

    2026年9月4日
    000
  • edge浏览器怎么截图 edge浏览器截图方法

    edge浏览器怎么截图 edge浏览器截图方法edge浏览器怎么截图 edge浏览器截图方法edge浏览器怎么截图 edge浏览器截图方法edge浏览器怎么截图 edge浏览器截图方法

    点击浏览器右上角类似笔记本图标的“做web笔记”按钮,会打开一个菜单栏。 菜单栏左侧包括“笔”、“荧光笔”、“橡皮擦”、“添加键入的笔记”和“剪辑”功能, 右侧则有“做WEB笔记”、“共享”以及“退出”选项。 使用截图功能时,点击“剪辑”,界面上会出现“拖动以复制区域”的提示,同时鼠标指针变为“+”…

    2026年9月4日 用户投稿
    000
  • 告别支付集成难题:MONEI PHP SDK 助力高效支付

    在最近的项目中,我们需要集成一个安全的、高效的支付网关。起初,我们计划自己开发支付流程,这听起来似乎可行,但很快我们发现自己陷入了困境。首先,确保支付流程的安全性是一项艰巨的任务,需要处理各种潜在的安全漏洞。其次,我们需要支持多种支付方式,这需要大量的代码编写和测试工作,以确保与各种支付提供商的兼容…

    用户投稿 2026年9月4日
    000
  • 晋江app如何查看作者的更新频率_晋江作者更新情况查看方法

    将作品加入书架可标记更新;2. 开启通知能及时接收新章节提醒;3. 访问作者专栏可查看详细更新记录。通过这三种方式可精准追踪晋江作者的更新动态,避免因首页刷新过快而遗漏信息。 如果您想了解晋江文学城中某位作者的更新动态或作品更新频率,但由于网站文章更新较快,首页仅展示最近三分钟内更新的20篇文章,因…

    2026年9月4日
    000
  • win11电脑蓝牙音频断断续续 蓝牙音质不稳定的优化方案

    win11电脑蓝牙音频断断续续 蓝牙音质不稳定的优化方案win11电脑蓝牙音频断断续续 蓝牙音质不稳定的优化方案win11电脑蓝牙音频断断续续 蓝牙音质不稳定的优化方案win11电脑蓝牙音频断断续续 蓝牙音质不稳定的优化方案

    蓝牙音频断续、音质不稳的问题可通过以下方法解决:1. 更新或重装蓝牙驱动;2. 减少2.4ghz频段干扰,远离wi-fi、微波炉等设备;3. 禁用电源管理中的节能选项;4. 切换高质量音频编码格式如aac或aptx;5. 关闭windows音频增强效果;6. 检查硬件或更换蓝牙适配器;7. 通过连接…

    2026年9月4日 用户投稿
    000
  • 怎么开通TikTok小店?注册TK账号时需要注意的4个细节

    开通tiktok小店核心在于满足资质要求和正确注册账号。1. 账号类型必须选企业号,个人号后续功能受限且难以转换;2. 注册地区、ip地址需与目标开店地区一致,否则可能导致审核失败或功能受限;3. 绑定长期稳定的手机号和邮箱,确保账号安全及信息接收;4. 实名认证信息必须真实完整,避免审核失败或无法…

    2026年9月4日
    000
  • C++中如何用Lambda函数实现私有函数仅供公有函数调用?

    C++中使用Lambda函数模拟私有函数,仅供公有函数调用 问题:如何在C++中实现类似于其他语言中“私有函数仅供公有函数调用”的特性? 解决方法:虽然C++没有直接的“私有函数”概念像Java或C#那样,但我们可以巧妙地利用Lambda表达式来模拟这种行为。Lambda表达式创建的匿名函数,其作用…

    2026年9月4日
    000
  • Linux中inode用法教程

    这篇文章主要给大家介绍了在linux中关于inode的相关资料,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。 背景 最近在复习Linux命令,到df的时候发现了一个之前忽略的东西。也就是 -i 这个选项,列出文件系统分区的inode信息。这个inode,是个什么东…

    用户投稿 2026年9月4日
    500
  • 美信科技:湾区总部工业园预计今年上半年投入使用

    美信科技湾区总部工业园预计上半年投入使用,积极应对市场挑战。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 美信科技近日在投资者互动平台透露,其位于湾区总部的工业园区目前正在装修,计划于今年上半年正式投入运营。 面对严峻的市场竞争,公司持续…

    2026年9月4日
    000
  • 百度浏览器如何设置无图模式 百度浏览器设置无图模式教程

    百度浏览器怎么开启无图模式?用户如何在该应用中找到无图模式的功能?想要启用该功能具体该怎么操作?百度浏览器是一款广受用户欢迎的手机端浏览工具,用户可以在这个平台上使用多个搜索引擎来查找信息。其搜索能力强大,并且支持多种浏览设置,可以根据不同场景进行个性化调整。虽然平台会加载图片内容,但有时候图片会影…

    2026年9月4日
    000
  • 豆包电脑版上线AI播客功能,支持一键生成播客

    据悉,6月17日,豆包电脑版已全面上线ai播客功能。用户只需上传pdf文件或网页链接,即可一键生成双人对话形式的播客节目,语音效果高度拟人化,对话自然流畅。 参与内测的用户反馈称,他们会将一些较长的学习资料发送给豆包,通过一键转换为语音内容,实现随时随地“轻松听长文”。生成的AI播客在音色上与真人非…

    2026年9月4日
    000
  • 如何在linux下建站目录分配权限

    在建站的时候给目录分配权限是非常重要的,也是建站的程序员们必须要会的,下面这篇文章主要给大家总结了在linux下建站目录分配权限的经验技巧,需要的朋友可以参考借鉴,下面来一起看看吧。 前言 在网上搜索网站文件夹权限配置一般都是是: 文件夹权限最小权限755 文件最小权限644 文件的可读可写可执行很…

    用户投稿 2026年9月4日
    000
  • 《双点博物馆》首次加入促销阵容 “SEGA年中大促”进行中

    《双点博物馆》首次加入促销阵容 “SEGA年中大促”进行中《双点博物馆》首次加入促销阵容 “SEGA年中大促”进行中《双点博物馆》首次加入促销阵容 “SEGA年中大促”进行中《双点博物馆》首次加入促销阵容 “SEGA年中大促”进行中

    “sega年中大促”活动正式启动,playstationstore和nintendo eshop的部分在售游戏推出限时折扣。本次活动将持续至2025年7月2日。 “双点”系列的最新作品——博物馆经营模拟游戏《双点博物馆:探索者版》以8折优惠首次亮相促销活动。此外,《人中之龙8外传 Pirates i…

    2026年9月4日 用户投稿
    000
  • ​​无法定位程序输入点于动态链接库?错误修复指南​​

    重新安装或修复出错程序,确保安装完整且无残留;2. 运行sfc++ /scannow和dism命令修复系统文件,必要时手动替换受损dll;3. 更新或重装microsoft visual c++ redistributable和.net framework等运行库;4. 更新驱动程序和系统补丁,确保…

    2026年9月4日
    300

发表回复

登录后才能评论
关注微信