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

小白学大数据
• 阅读 9

在数据抓取的领域中,我们常常会遇到一个棘手的难题:许多现代网站大量使用JavaScript在用户浏览器中动态地渲染内容。传统的爬虫库(如Requests搭配BeautifulSoup)对此无能为力,因为它们只能获取服务器最初返回的静态HTML文档,而无法执行其中的JS代码来生成最终呈现给用户的完整内容。对于动态壁纸网站这类高度依赖前端交互和动态加载的资源站,传统方法更是束手无策。 此时,"无头浏览器"(Headless Browser)技术便成为了破解这一困境的钥匙。而在Python世界中,除了广为人知的Selenium,一个更轻量、更现代的选择正受到越来越多开发者的青睐——Pyppeteer。它实现了"所见即所爬"的愿景,让你能抓取到任何在真实浏览器中能看到的内容。 一、为何选择Pyppeteer? Pyppeteer是一个Python库,它提供了对Puppeteer(一个由Chrome团队维护的Node库)的高层级封装。其核心优势在于: 直接控制Chromium:Pyppeteer通过DevTools协议直接与Chromium浏览器通信,无需额外的WebDriver,因此更加高效和稳定。 异步高性能:基于asyncio库构建,天生支持异步操作,非常适合编写高性能的爬虫脚本,能轻松处理多个页面或并发任务。 API简洁强大:提供了极其丰富的API来模拟几乎所有真实用户的操作,如点击、输入、滚动、拦截请求、执行JS等,几乎能做到任何手动操作可以做到的事情。 处理动态内容:能完整地执行页面中的JavaScript,等待Ajax请求完成或元素动态出现,轻松抓取动态生成的内容。 本文将通过一个实战项目:爬取一个动态壁纸网站,来详细讲解如何使用Pyppeteer。 二、项目实战:爬取动态壁纸网站

  1. 目标分析与准备 假设目标网站:我们以一个虚构的动态壁纸网站dynamic-wallpapers.com为例。该网站的特点是: 壁纸列表通过滚动到底部动态加载更多(无限滚动)。 每张壁纸的详情页,其高清大图或视频文件的URL由JavaScript计算生成。 开发环境准备: 首先,安装必需的库。Pyppeteer在安装时会自动下载兼容版本的Chromium。

  2. 核心代码实现与分步解析 以下代码将完成以下任务: 启动浏览器并打开新页面。 导航到目标壁纸列表页。 模拟滚动操作,加载全部壁纸列表。 提取所有壁纸的详情页链接。 逐个进入详情页,抓取高清壁纸资源(图片或视频)的真实URL。 下载资源并保存到本地。

     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())


3. 关键技术与难点解析
等待策略: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来说资源消耗更大,但在处理复杂动态内容时,它所提供的便利性和成功率是无可替代的。
点赞
收藏
评论区
推荐文章
Irene181 Irene181
4年前
别去送死了。Selenium 与 Puppeteer 能被网站探测的几十个特征
摄影:产品经理味道很好的毛血旺很多人喜欢使用Selenium或者Puppeteer(Pyppeteer)通过模拟浏览器来编写爬虫,自以为这样可以不被网站检测到,想爬什么数据就爬什么数据。但实际上,Selenium启动的浏览器,有几十个特征可以被网站通过JavaScript探测到。Puppeteer启动的浏览器,也有很多特征能够被网站探测。
浅梦一笑 浅梦一笑
4年前
分别用python2和python3伪装浏览器爬取网页内容
python网页抓取功能非常强大,使用urllib或者urllib2可以很轻松的抓取网页内容。但是很多时候我们要注意,可能很多网站都设置了防采集功能,不是那么轻松就能抓取到想要的内容。今天我来分享下载python2和python3中都是如何来模拟浏览器来跳过屏蔽进行抓取的。最基础的抓取:!/usr/bin/envpythoncodingutf8@Au
Stella981 Stella981
3年前
Scrapy笔记(1)
Scrapy笔记01入门篇  Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取(更确切来说,网络抓取)所设计的,也可以应用在获取API所返回的数据(比如WebServices)或者通用的网络爬虫。  Scr
Stella981 Stella981
3年前
Selenium及Headless Chrome抓取动态HTML页面
一般的的静态HTML页面可以使用requests等库直接抓取,但还有一部分比较复杂的动态页面,这些页面的DOM是动态生成的,有些还需要用户与其点击互动,这些页面只能使用真实的浏览器引擎动态解析,Selenium和ChromeHeadless可以很好的达到这种目的。HeadlessChromeHeadlessChrome是Chrome
小白学大数据 小白学大数据
4个月前
动态网页爬取:Python如何获取JS加载的数据?
在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段。许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取方法难以奏效。然而,对于数据分析师、研究人员以及开发者来说,获取这些动态加载的数据仍然是一个重要的需求。本文
小白学大数据 小白学大数据
2星期前
应对反爬:使用Selenium模拟浏览器抓取12306动态旅游产品
在当今数据驱动的时代,网络爬虫已成为获取互联网信息的重要手段。然而,许多网站如12306都实施了严格的反爬虫机制,特别是对于动态加载的内容。本文将详细介绍如何使用Selenium模拟真实浏览器行为,有效绕过这些限制,成功抓取12306旅游产品数据。1230
爬虫中无头浏览器如何选择
我们日常使用浏览器的步骤为:启动浏览器、打开一个网页、进行交互。而无头浏览器指的是我们使用脚本来执行以上过程的浏览器,能模拟真实的浏览器使用场景。主要是用作爬虫,用以捕捉Web上的各类数据;这里的无头主要是指没有界面,完全是后台操作。它就是一个真实的浏览器。只是这个浏览器是无界面的。在爬虫中使用无头浏览器有很多的注意事项,比如我们的业务场景是否适合使用无头浏
小白学大数据 小白学大数据
6个月前
Scrapy结合Selenium实现滚动翻页数据采集
引言在当今的互联网数据采集领域,许多网站采用动态加载技术(如AJAX、无限滚动)来优化用户体验。传统的基于Requests或Scrapy的爬虫难以直接获取动态渲染的数据,而Selenium可以模拟浏览器行为,实现滚动翻页和动态内容加载。本文将介绍如何结合S
小白学大数据 小白学大数据
4个月前
Python爬虫中time.sleep()与动态加载的配合使用
一、动态加载网页的挑战动态加载网页是指网页的内容并非一次性加载完成,而是通过JavaScript等技术在用户交互或页面加载过程中逐步加载。这种设计虽然提升了用户体验,但对于爬虫来说,却增加了抓取的难度。传统的爬虫方法,如简单的HTTP请求,往往只能获取到网
小白学大数据
小白学大数据
Lv1
男 · 亿牛云 · python技术
宁为代码类弯腰,不为bug点提交!
文章
119
粉丝
5
获赞
18