“所见即所爬”:使用Pyppeteer无头浏览器抓取动态壁纸

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: “所见即所爬”:使用Pyppeteer无头浏览器抓取动态壁纸

在数据抓取的领域中,我们常常会遇到一个棘手的难题:许多现代网站大量使用JavaScript在用户浏览器中动态地渲染内容。传统的爬虫库(如Requests搭配BeautifulSoup)对此无能为力,因为它们只能获取服务器最初返回的静态HTML文档,而无法执行其中的JS代码来生成最终呈现给用户的完整内容。对于动态壁纸网站这类高度依赖前端交互和动态加载的资源站,传统方法更是束手无策。
此时,"无头浏览器"(Headless Browser)技术便成为了破解这一困境的钥匙。而在Python世界中,除了广为人知的Selenium,一个更轻量、更现代的选择正受到越来越多开发者的青睐——Pyppeteer。它实现了"所见即所爬"的愿景,让你能抓取到任何在真实浏览器中能看到的内容。
一、为何选择Pyppeteer?
Pyppeteer是一个Python库,它提供了对Puppeteer(一个由Chrome团队维护的Node库)的高层级封装。其核心优势在于:

  1. 直接控制Chromium:Pyppeteer通过DevTools协议直接与Chromium浏览器通信,无需额外的WebDriver,因此更加高效和稳定。
  2. 异步高性能:基于asyncio库构建,天生支持异步操作,非常适合编写高性能的爬虫脚本,能轻松处理多个页面或并发任务。
  3. API简洁强大:提供了极其丰富的API来模拟几乎所有真实用户的操作,如点击、输入、滚动、拦截请求、执行JS等,几乎能做到任何手动操作可以做到的事情。
  4. 处理动态内容:能完整地执行页面中的JavaScript,等待Ajax请求完成或元素动态出现,轻松抓取动态生成的内容。
    本文将通过一个实战项目:爬取一个动态壁纸网站,来详细讲解如何使用Pyppeteer。
    二、项目实战:爬取动态壁纸网站
  5. 目标分析与准备
    假设目标网站:我们以一个虚构的动态壁纸网站dynamic-wallpapers.com为例。该网站的特点是:
    ● 壁纸列表通过滚动到底部动态加载更多(无限滚动)。
    ● 每张壁纸的详情页,其高清大图或视频文件的URL由JavaScript计算生成。
    开发环境准备:
    首先,安装必需的库。Pyppeteer在安装时会自动下载兼容版本的Chromium。
  6. 核心代码实现与分步解析
    以下代码将完成以下任务:
  7. 启动浏览器并打开新页面。
  8. 导航到目标壁纸列表页。
  9. 模拟滚动操作,加载全部壁纸列表。
  10. 提取所有壁纸的详情页链接。
  11. 逐个进入详情页,抓取高清壁纸资源(图片或视频)的真实URL。
  12. 下载资源并保存到本地。
    import asyncio
    import os
    from urllib.parse import urljoin
    import aiohttp
    import aiofiles
    from pyppeteer import launch

代理配置信息

proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"

构建代理认证字符串(Basic Auth)

proxyAuth = f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"

async def download_file(session, url, filepath):
"""异步下载文件并保存"""
try:
async with session.get(url, proxy=f"http://{proxyHost}:{proxyPort}", proxy_auth=aiohttp.BasicAuth(proxyUser, proxyPass)) as response:
if response.status == 200:
async with aiofiles.open(filepath, 'wb') as f:
await f.write(await response.read())
print(f"成功下载: {filepath}")
else:
print(f"下载失败,状态码: {response.status}, URL: {url}")
except Exception as e:
print(f"下载文件时发生错误: {str(e)}, URL: {url}")

async def main():

# 1. 启动浏览器,配置代理
browser = await launch(
    headless=False, 
    args=[
        '--no-sandbox',
        f'--proxy-server={proxyHost}:{proxyPort}',
        '--disable-web-security',  # 可选,禁用同源策略
        '--disable-features=VizDisplayCompositor'  # 可选,提高稳定性
    ],
    # 设置忽略HTTPS错误(某些代理环境下可能需要)
    ignoreHTTPSErrors=True
)

page = await browser.newPage()

# 设置代理认证(通过JavaScript在页面加载前注入)
await page.setExtraHTTPHeaders({
    'Proxy-Authorization': f'Basic {proxyUser}:{proxyPass}'.encode('base64').strip()
})

# 另一种认证方式:在页面上下文中执行认证
await page.authenticate({'username': proxyUser, 'password': proxyPass})

# 设置视窗大小
await page.setViewport({'width': 1920, 'height': 1080})

try:
    # 2. 导航到列表页
    list_url = 'https://dynamic-wallpapers.com/list'
    print(f"正在访问列表页: {list_url}")
    await page.goto(list_url, waitUntil='networkidle0')

    # 3. 模拟滚动,加载全部内容
    print("开始模拟滚动以加载更多壁纸...")
    scroll_times = 5
    for i in range(scroll_times):
        await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')
        await asyncio.sleep(2)
        print(f"已完成第 {i+1}/{scroll_times} 次滚动")

    # 4. 提取所有壁纸详情页链接
    print("正在提取壁纸链接...")
    wallpaper_links = await page.evaluate('''() => {
        const items = document.querySelectorAll('.wallpaper-item a');
        return Array.from(items).map(a => a.href);
    }''')
    print(f"共找到 {len(wallpaper_links)} 个壁纸链接")

    # 创建保存资源的文件夹
    os.makedirs('wallpapers', exist_ok=True)

    # 使用aiohttp创建会话,配置代理
    connector = aiohttp.TCPConnector(limit=10, verify_ssl=False)  # 限制并发数,忽略SSL验证
    async with aiohttp.ClientSession(
        connector=connector,
        trust_env=True  # 信任环境变量中的代理设置
    ) as session:
        # 5. 遍历每个详情页链接
        for index, detail_url in enumerate(wallpaper_links):
            print(f"正在处理第 {index+1} 个壁纸: {detail_url}")
            detail_page = await browser.newPage()

            # 为新页面也设置代理认证
            await detail_page.authenticate({'username': proxyUser, 'password': proxyPass})
            await detail_page.setViewport({'width': 1920, 'height': 1080})

            try:
                await detail_page.goto(detail_url, waitUntil='networkidle0')

                # 6. 获取资源真实URL
                resource_url = await detail_page.evaluate('''() => {
                    const hdSource = document.querySelector('#hd-source');
                    return hdSource ? hdSource.src : null;
                }''')

                if not resource_url:
                    print(f"未在第 {index+1} 个页面中找到资源URL")
                    await detail_page.close()
                    continue

                # 构建本地文件名
                filename = os.path.join('wallpapers', f"wallpaper_{index+1}{os.path.splitext(resource_url)[1]}")

                # 7. 异步下载资源(通过代理)
                print(f"开始下载: {resource_url}")
                await download_file(session, resource_url, filename)

            except Exception as e:
                print(f"处理详情页 {detail_url} 时发生错误: {str(e)}")
            finally:
                await detail_page.close()

except Exception as e:
    print(f"主流程发生错误: {str(e)}")
finally:
    await browser.close()
    print("浏览器已关闭,任务完成。")

运行主异步函数

if name == 'main':

# 对于高版本Python,使用新的异步运行方式
try:
    asyncio.run(main())
except RuntimeError:
    # 兼容Jupyter等环境
    asyncio.get_event_loop().run_until_complete(main())
  1. 关键技术与难点解析
    ● 等待策略:waitUntil: 'networkidle0' 是等待页面加载完成的关键。对于更精确的控制,可以使用page.waitForSelector(‘.some-class’)来等待某个特定元素出现,这比固定的asyncio.sleep()更加可靠。
    ● 执行JavaScript:page.evaluate()是Pyppeteer的灵魂。它允许你在页面上下文中执行任何JS代码,并获取返回值。这对于提取复杂数据或操作DOM至关重要。
    ● 资源拦截:Pyppeteer可以监听和修改网络请求(page.on(‘request’) / page.on(‘response’))。有时直接拦截下载资源的请求比在DOM中查找URL更高效,尤其对于大型二进制文件。
    ● 反爬虫应对:Pyppeteer虽然强大,但其指纹也可能被网站识别。可以通过args参数注入一些选项来隐藏指纹,例如--disable-blink-features=AutomationControlled,并配合await page.evaluateOnNewDocument(‘delete navigator.webdriver;’)来删除一些暴露的变量。
    三、总结
    通过Pyppeteer,我们成功地构建了一个能够应对现代动态网站的爬虫。它完美地模拟了真实用户的行为:访问页面、滚动、点击、在新标签页中打开链接,最终精准地抓取到了由JavaScript动态生成的壁纸资源地址。
    其异步架构使得爬虫在I/O密集型任务(如网络请求和下载)上表现卓越,效率远超同步方式的工具。尽管Pyppeteer相对于Requests+BeautifulSoup来说资源消耗更大,但在处理复杂动态内容时,它所提供的便利性和成功率是无可替代的。
相关文章
|
3天前
|
人工智能 运维 安全
|
1天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
5天前
|
SpringCloudAlibaba 负载均衡 Dubbo
微服务架构下Feign和Dubbo的性能大比拼,到底鹿死谁手?
本文对比分析了SpringCloudAlibaba框架下Feign与Dubbo的服务调用性能及差异。Feign基于HTTP协议,使用简单,适合轻量级微服务架构;Dubbo采用RPC通信,性能更优,支持丰富的服务治理功能。通过实际测试,Dubbo在调用性能、负载均衡和服务发现方面表现更出色。两者各有适用场景,可根据项目需求灵活选择。
401 124
微服务架构下Feign和Dubbo的性能大比拼,到底鹿死谁手?
|
8天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
757 109
|
2天前
|
机器学习/深度学习 传感器 算法
Edge Impulse:面向微型机器学习的MLOps平台——论文解读
Edge Impulse 是一个面向微型机器学习(TinyML)的云端MLOps平台,致力于解决嵌入式与边缘设备上机器学习开发的碎片化与异构性难题。它提供端到端工具链,涵盖数据采集、信号处理、模型训练、优化压缩及部署全流程,支持资源受限设备的高效AI实现。平台集成AutoML、量化压缩与跨硬件编译技术,显著提升开发效率与模型性能,广泛应用于物联网、可穿戴设备与边缘智能场景。
169 127
|
3天前
|
算法 Python
【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)
【轴承故障诊断】一种用于轴承故障诊断的稀疏贝叶斯学习(SBL),两种群稀疏学习算法来提取故障脉冲,第一种仅利用故障脉冲的群稀疏性,第二种则利用故障脉冲的额外周期性行为(Matlab代码实现)
229 152
|
5天前
|
Java 数据库 数据安全/隐私保护
Spring 微服务和多租户:处理多个客户端
本文介绍了如何在 Spring Boot 微服务架构中实现多租户。多租户允许单个应用实例为多个客户提供独立服务,尤其适用于 SaaS 应用。文章探讨了多租户的类型、优势与挑战,并详细说明了如何通过 Spring Boot 的灵活配置实现租户隔离、动态租户管理及数据源路由,同时确保数据安全与系统可扩展性。结合微服务的优势,开发者可以构建高效、可维护的多租户系统。
210 127
|
4天前
|
机器学习/深度学习 存储 资源调度
CMSIS-NN:ARM Cortex-M处理器的高效神经网络内核——论文解读
CMSIS-NN是专为ARM Cortex-M系列微控制器优化的神经网络计算内核库,旨在支持资源受限的物联网边缘设备进行高效的深度学习推理。该库通过对卷积、池化、全连接层等关键操作进行定点量化、SIMD指令优化和内存布局调整,显著提升了模型在嵌入式设备上的运行效率。实验表明,CMSIS-NN在Cortex-M7处理器上的推理速度比基准实现提升了近5倍,大幅降低了功耗,为边缘AI应用提供了可行的技术路径。
223 128