Puppeteer与Selenium的主要区别以及对比
您是否坚持在 Puppeteer 和 Selenium 之间做出网络抓取的决定?我们得到你。两者都是出色的浏览器自动化框架,在做出决定时考虑您的抓取需求以及可用资源非常重要。
在下表中查看Puppeteer 和 Selenium 之间的主要区别,然后让我们深入了解细节。
标准 | Puppeteer | Selenium |
---|---|---|
兼容语言 | 仅官方支持 JavaScript,但有非官方的 PHP 和 Python 端口 | Java、Python、C#、Ruby、PHP、JavaScript 和 Kotlin |
浏览器支持 | Chromium 和实验性 Firefox 支持 | Chrome、Safari、Firefox、Opera、Edge 和 IE |
表现 | 比 Selenium 快 60% | 快速的 |
操作系统支持 | Windows、Linux 和 macOS | Windows、Linux、macOS 和 Solaris |
建筑学 | 具有无头浏览器实例的事件驱动架构 | Web 驱动程序上的 JSONWire 协议,用于控制浏览器实例 |
先决条件 | JavaScript包就够了 | Selenium Bindings(用于选择的编程语言)和浏览器网络驱动程序 |
社区 | 与 Selenium 相比的小型社区 | 建立了一个文档集合以及一个庞大的社区 |
让我们继续详细讨论这些库,并为每个库做一个抓取示例,以展示它们在从网页中提取数据方面的效率。
Puppeteer
Puppeteer是一个自动化 Node.js 库,支持通过 DevTools 协议使用无头 Chromium 或 Chrome。它提供了在 Chrome 或 Chromium 中自动执行任务的工具,例如截屏、生成 PDF 和导航页面。Puppeteer 还可用于通过模拟用户交互(例如单击按钮、填写表格和验证结果是否按预期显示)来测试网页。
Puppeteer有什么优势?
Puppeteer 的优点是:
- 它易于使用。
- 无需设置,因为它与 Chromium 捆绑在一起。
- 它默认无头运行,但也可以禁用。
- Puppeteer 具有事件驱动的架构,无需在代码中手动调用睡眠。
- 它可以截取屏幕截图、生成 PDF 并自动执行浏览器上的每个操作。
- 它提供了诸如记录运行时和加载性能等管理功能,这对于优化和调试你的爬虫很有用。
- Puppeteer 可以爬取 SPA(Single Page Applications)并生成预渲染的内容(即 Server-side Rendering)。
- 您可以使用 WebTools 控制台记录您在浏览器上的操作来创建 Puppeteer 脚本。
Puppeteer的缺点是什么?
Puppeteer 的缺点是:
使用 Puppeteer 进行网页抓取示例
让我们快速浏览一下Puppeteer 网络抓取教程,以深入了解 Puppeteer 与 Selenium 之间的性能比较。我们将从Danube 网站的Crime and Thriller类别中提取表项:
首先,导入Puppeteer
模块并创建一个异步函数来运行 Puppeteer 代码:
const puppeteer = require('puppeteer'); async function main() { // write code here }
puppeteer.launch()
完成后,启动浏览器实例并使用和方法创建页面newPage()
:
const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage();
使用该goto
方法导航到创建的页面并传递waitUntil: 'networkidle2'
以等待所有网络流量完成。然后等待ul
带有sidebar-list
类的元素加载列表项:
await page.goto('https://danube-webshop.herokuapp.com/', { waitUntil: 'networkidle2' }) await page.waitForSelector('ul.sidebar-list');
单击第一个元素列表导航到Crime & Thrillers
类别的页面,然后添加waitForNavigation()
等待网页加载的方法。接下来,通过将它们包装在一个中来处理异步操作Promise
:
await Promise.all([ page.waitForNavigation(), page.click("ul[class='sidebar-list'] > li > a"), ]);
使用该方法等待图书预览加载waitForSelector()
。然后使用该方法提取书籍querySelectorAll()
并将它们存储在books
变量中。最后,抓取每个预览的标题、作者、价格和评级,并打印出来:
await page.waitForSelector("li[class='preview']"); const books = await page.evaluateHandle( () => [...document.querySelectorAll("li[class='preview']")] ) const processed_data = await page.evaluate(elements => { let data = [] elements.forEach( element => { let title = element.querySelector("div.preview-title").innerHTML; let author = element.querySelector("div.preview-author").innerHTML; let rating = element.querySelector("div.preview-details > p.preview-rating").innerHTML; let price = element.querySelector("div.preview-details > p.preview-price").innerHTML; let result = {title: title, author: author, rating: rating, price: price} data.push(result); }) return data }, books) console.log(processed_data) await page.close(); await browser.close();
现在,让我们把它包装在main
函数中,然后我们可以使用main()
它来运行它:
// import the puppeteer library const puppeteer = require('puppeteer'); // create the asynchronous main function async function main() { // launch a headless browser instance const browser = await puppeteer.launch({ headless: true }) // create a new page object const page = await browser.newPage(); // navigate to the target URL, wait until the loading finishes await page.goto('https://danube-webshop.herokuapp.com/', { waitUntil: 'networkidle2' }) // wait for left-side bar to load await page.waitForSelector('ul.sidebar-list'); // click to the first element and wait for the navigation to finish await Promise.all([ page.waitForNavigation(), page.click("ul[class='sidebar-list'] > li > a"), ]); // wait for previews to load await page.waitForSelector("li[class='preview']"); // extract the book previews const books = await page.evaluateHandle( () => [...document.querySelectorAll("li[class='preview']")] ) // extract the relevant data using page.evaluate // just pass the elements as the second argument and processing function as the first argument const processed_data = await page.evaluate(elements => { // define an array to store the extracted data let data = [] // use a forEach loop to loop through every preview elements.forEach( element => { // get the HTMl text of title, author, rating, and price data, respectively. let title = element.querySelector("div.preview-title").innerHTML; let author = element.querySelector("div.preview-author").innerHTML; let rating = element.querySelector("div.preview-details > p.preview-rating").innerHTML; let price = element.querySelector("div.preview-details > p.preview-price").innerHTML; // build a dictionary and store the data as key:value pairs let result = {title: title, author: author, rating: rating, price: price} // append the data to the `data` array data.push(result); }) // return the result (it will be stored in `processed_data` variable) return data }, books) // print out the extracted data console.log(processed_data) // close the page and browser respectively await page.close(); await browser.close(); } // run the main function to scrape the data main();
继续运行代码。您的输出应如下所示:
[ { title: 'Does the Sun Also Rise?', author: 'Ernst Doubtingway', rating: '★★★★☆', price: '$9.95' }, { title: 'The Insiders', author: 'E. S. Hilton', rating: '★★★★☆', price: '$9.95' }, { title: 'A Citrussy Clock', author: 'Bethany Urges', rating: '★★★★★', price: '$9.95' } ]
Selenium
Selenium是一种开源的端到端测试和 Web 自动化工具,通常用于网页抓取,其主要组件有 Selenium IDE、Selenium Webdriver 和 Selenium Grid。
Selenium IDE 用于在自动化之前记录操作,Selenium Grid 用于并行执行,Selenium WebDriver 用于在浏览器中执行命令。
Selenium的优点是什么?
Selenium的优点是:
- 它易于使用。
- Selenium 支持多种编程语言,如 Python、Java、JavaScript、Ruby 和 C#。
- 它能够自动化 Firefox、Edge、Safari 甚至自定义 QtWebKit 浏览器。
- 可以使用不同的技术将 Selenium 扩展到数百个实例,例如使用不同的浏览器设置设置云服务器。
- 它可以在 Windows、macOS 和 Linux 上运行。
Selenium的缺点是什么?
Selenium的缺点是:
- Selenium 设置方法很复杂。
使用 Selenium 的 Web 抓取示例
正如我们对 Puppeteer 所做的那样,让我们使用相同的目标站点运行一个关于使用 Selenium 进行网络抓取的教程。首先导入必要的模块,然后配置 Selenium 实例:
# for timing calculations and sleeping the scraper import time # import the Selenium from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By # web driver manager: https://github.com/SergeyPirogov/webdriver_manager # will help us automatically download the web driver binaries # then we can use `Service` to manage the web driver's state. from webdriver_manager.chrome import ChromeDriverManager options = webdriver.ChromeOptions() options.headless = True
使用 WebDriverManager 安装 Chrome 网络驱动实例,然后定义 Chrome 服务并初始化网络驱动:
# this returns the path web driver downloaded chrome_path = ChromeDriverManager().install() chrome_service = Service(chrome_path) driver = webdriver.Chrome(service=chrome_service, options=options)
使用方法指向目标网站driver.get()
:
url = "https://danube-webshop.herokuapp.com/" driver.get(url)
导航到并单击 Crime & Thrillers 类别元素,然后添加time.sleep(1)
以让抓取器休眠一秒钟。然后使用find_elements
提取图书预览的方法:
time.sleep(1) crime_n_thrillers = driver.find_element(By.CSS_SELECTOR, "ul[class='sidebar-list'] > li") crime_n_thrillers.click() time.sleep(1) books = driver.find_elements(By.CSS_SELECTOR, "div.shop-content li.preview")
使用 BeautifulSoup 分别从元素中提取标题、作者、评级和定价,并将其包装在函数中extract
:
def extract(element): title = element.find_element(By.CSS_SELECTOR, "div.preview-title").text author = element.find_element(By.CSS_SELECTOR, "div.preview-author").text rating = element.find_element(By.CSS_SELECTOR, "div.preview-details p.preview-rating").text price = element.find_element(By.CSS_SELECTOR, "div.preview-details p.preview-price").text # return the extracted data as a dictionary return {"title": title, "author": author, "rating": rating, "price": price}
循环预览并提取数据,然后退出驱动程序:
extracted_data = [] for element in books: data = extract(element) extracted_data.append(data) driver.quit()
输出如下所示:
[ {'title': 'Does the Sun Also Rise?', 'author': 'Ernst Doubtingway', 'rating': '★★★★☆', 'price': '$9.95'}, {'title': 'The Insiders', 'author': 'E. S. Hilton', 'rating': '★★★★☆', 'price': '$9.95'}, {'title': 'A Citrussy Clock', 'author': 'Bethany Urges', 'rating': '★★★★★', 'price': '$9.95'} ]
Puppeteer 与 Selenium:速度比较
Puppeteer 比 Selenium 快吗?这是一个经常被问到的问题,直截了当地说:Puppeteer 比 Selenium 快。
为了比较 Puppeteer 与 Selenium 的速度,我们使用了 danube-store 沙盒并运行了我们在上面展示的脚本 20 次,并对执行时间取平均值。
为了找到运行 Selenium 脚本所需的时间,我们使用时间模块来设置开始和结束时间。我们把它start_time = time.time()
放在脚本的开头和end_time = time.time()
结尾。然后计算开始时间和结束时间之间的差异end_time - start_time
。
这是用于 Selenium 的完整脚本:
import time from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait # web driver manager: https://github.com/SergeyPirogov/webdriver_manager # will help us automatically download the web driver binaries # then we can use `Service` to manage the web driver's state. from webdriver_manager.chrome import ChromeDriverManager def extract(element): title = element.find_element(By.CSS_SELECTOR, "div.preview-title").text author = element.find_element(By.CSS_SELECTOR, "div.preview-author").text rating = element.find_element(By.CSS_SELECTOR, "div.preview-details p.preview-rating").text price = element.find_element(By.CSS_SELECTOR, "div.preview-details p.preview-price").text return {"title": title, "author": author, "rating": rating, "price": price} # start the timer start = time.time() options = webdriver.ChromeOptions() options.headless = True # this returns the path web driver downloaded chrome_path = ChromeDriverManager().install() # define the chrome service and pass it to the driver instance chrome_service = Service(chrome_path) driver = webdriver.Chrome(service=chrome_service, options=options) url = "https://danube-webshop.herokuapp.com/" driver.get(url) # get the first page and click to the its link # first element will be the Crime & Thrillers category time.sleep(1) crime_n_thrillers = driver.find_element(By.CSS_SELECTOR, "ul[class='sidebar-list'] > li") print(crime_n_thrillers) crime_n_thrillers.click() time.sleep(1) # get the data div and extract the data using beautifulsoup books = driver.find_elements(By.CSS_SELECTOR, "div.shop-content li.preview") extracted_data = [] print(books) for element in books: data = extract(element) extracted_data.append(data) print(data) end = time.time() print(f"The whole script took: {end-start:.4f}") driver.quit()
对于 Puppeteer 脚本,我们使用了Date
对象。const start = Date.now();
只需在脚本的开头和const end = Date.now();
结尾添加即可。然后可以用 计算差值end-start
。
这是用于 Puppeteer 的脚本:
const puppeteer = require('puppeteer'); async function main() { const start = Date.now(); const browser = await puppeteer.launch({ headless: true }) const page = await browser.newPage(); await page.goto('https://danube-webshop.herokuapp.com/', { waitUntil: 'networkidle2' }) await page.waitForSelector('ul.sidebar-list'); await Promise.all([ page.waitForNavigation(), page.click("ul[class='sidebar-list'] > li > a"), ]); await page.waitForSelector("li[class='preview']"); const books = await page.evaluateHandle( () => [...document.querySelectorAll("li[class='preview']")] ) const processed_data = await page.evaluate(elements => { let data = [] elements.forEach( element => { let title = element.querySelector("div.preview-title").innerHTML; let author = element.querySelector("div.preview-author").innerHTML; let rating = element.querySelector("div.preview-details > p.preview-rating").innerHTML; let price = element.querySelector("div.preview-details > p.preview-price").innerHTML; let result = {title: title, author: author, rating: rating, price: price} data.push(result); }) return data }, books) console.log(processed_data) await page.close(); await browser.close(); const end = Date.now(); console.log(`Execution time: ${(end - start) / 1000} s`); } main();
您可以在下面看到 Selenium 与 Puppeteer 的性能测试结果:
该图表显示Puppeteer 比 Selenium 快约 60%。因此,在这方面,为需要 Chromium 的项目扩展 Puppeteer 应用程序是网络抓取的最佳选择。
Puppeteer 与 Selenium:哪个更好?
那么在抓取方面,Selenium 和 Puppeteer 哪个更好?这个问题没有直接的答案,因为它取决于多种因素,比如长期库支持、跨浏览器支持和您的网络抓取需求。Puppeteer 速度很快,但与 Selenium 相比,它支持的浏览器更少。与 Puppeteer 相比,Selenium 还支持更多语言。
尽管使用 Puppeteer 或 Selenium 是网络抓取工具的不错选择,但扩展和优化您的网络抓取项目可能很困难,因为高级反机器人能够检测和阻止这些库。避免这种情况的最佳方法是使用网络抓取 API。