返回博客
指南
Robert SfichiLast updated on Mar 31, 20262 min read

如何使用 Python 和 Selenium 构建网络爬虫

如何使用 Python 和 Selenium 构建网络爬虫

许多开发者选择自己编写网络爬虫,而不是使用现成的产品。如果你问他们大多数人更喜欢哪种编程语言,你很可能会听到无数次“Python”这个答案。

Python之所以成为大众首选,是因为其宽松的语法以及丰富的库资源,这些都大大简化了网页抓取工作。今天,我们将重点介绍其中一个库。

本指南将介绍如何使用 Selenium 和 Python 开始提取数据。我们将编写一个 Python 脚本,它将登录网站、抓取数据、进行美观格式化,并将其存储为 CSV 文件。

若想全面了解 Python 在网页抓取中的应用,建议先阅读我们关于使用 Python 构建抓取工具的终极指南。之后请回到这里,我们将深入探讨更多细节!

Selenium 概述

正如 Selenium 官方网站所述,Selenium 是一套用于自动化控制网页浏览器的工具集,最初作为跨浏览器测试工具推出。

Selenium 团队开发的 API 采用 WebDriver 协议来控制 Chrome 或 Firefox 等网页浏览器,并执行各种任务,例如:

  • 填写表单
  • 滚动
  • 截屏
  • 点击按钮

现在你可能在想,这一切如何应用到网页抓取中。其实很简单。

数据提取有时确实让人头疼。如今,即使没有必要,网站也常被建造成单页应用。它们弹出验证码的频率远超必要,甚至会封禁普通用户的IP地址。

简而言之,机器人检测功能令人沮丧,简直就像一个漏洞。

Selenium 能够通过解析并执行 JavaScript 代码,自动化处理网页抓取中的许多繁琐流程,例如滚动页面、抓取 HTML 元素或导出获取的数据,从而在这些情况下提供帮助。

安装

为了展示 Selenium 和 Python 的真正实力,我们将从 /r/learnprogramming 子版块抓取一些信息。除了数据抓取,我还将向您展示如何实现登录功能。既然我们已经了解了主要工具和即将使用的网站,接下来看看还需要安装哪些其他必要组件:

1. Python。我们将使用 Python 3.0。不过,只需稍作调整,您也可以使用 Python 2.0。您可以从这里下载并安装。

2. Selenium 包。你可以使用以下命令安装 Selenium 包:

pip3 install selenium

3. Pandas 包。它将用于提取数据并将抓取到的数据存储在 .csv 文件中。请运行以下命令在您的设备上安装它。

pip3 install pandas

4. BeautifulSoup 包。用于解析 HTML 和 XML 文档。只需运行以下命令:

pip3 install beautifulsoup

5. Google Chrome。点击此链接了解如何下载和安装。

6. Chrome 驱动程序。它将帮助我们配置 Selenium 的 Web 驱动程序。请点击此链接下载并安装最新版本的 chromedriver。请务必记下安装路径。

启动浏览器

让我们开始吧。创建一个新的 scraper.py 文件,并通过复制以下代码行导入 Selenium 包:

from selenium import webdriver

现在,我们将通过以下代码创建一个新的 Google Chrome 实例:

driver = webdriver.Chrome(LOCATION)

请将 LOCATION 替换为您计算机上 Chrome 驱动程序的实际路径。请查阅 Selenium 文档,根据您使用的操作系统查找 Web 驱动程序的最准确路径。

最后一步是访问我们要抓取数据的网站。在本例中,网址为 https://www.reddit.com/r/learnprogramming/top/?t=month。将以下代码行复制到新创建的 Python 文件中:

driver.get("https://www.reddit.com/r/learnprogramming/top/?t=month")

在终端窗口中运行以下命令:

python3 scraper.py

此时应会打开一个新的 Google Chrome 实例,页面顶部会显示“Chrome 正由自动化测试软件控制”。

定位特定数据

想必您已经猜到了,本教程我们将抓取 /r/learnprogramming 子版块。我们将保存帖子的标题、作者、赞数,并将其存储在一个新的 .csv 文件中。让我们看看这些信息在 HTML 页面上的位置,以及如何提取它们。

当 Google Chrome 最终加载完页面后,右键点击任意一篇帖子并选择“检查”。我们可以在 _1oQyIsiPHYt6nx7VOmd1sz 类名下找到该帖子的 HTML 容器。

您也可以在不显示图形用户界面的情况下运行 Google Chrome,并通过添加几行代码来记录页面的 HTML 内容。我们将为 Chrome 驱动程序设置 headless 选项为 true(以移除图形界面),并将窗口大小设为 1080 像素(以便获取适用于我们用例的正确 HTML 代码)。

最后两行代码会在完成页面 HTML 记录后立即退出 Chrome。

新的 scraper.py 文件将如下所示:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.headless = True
options.add_argument("--window-size=1920,1080")

driver = webdriver.Chrome("./chromedriver")
driver.get("https://www.reddit.com/r/learnprogramming/top/?t=month")

print(driver.page_source)
driver.quit()

WebElement

WebElement 是一个 Selenium 对象,用于表示 HTML 元素。正如您将在后续教程中看到的,我们可以对这些元素执行多种操作,其中包括:

  • 使用 .click() 方法点击该元素
  • 通过调用 .send_keys() 方法向特定输入元素输入文本
  • 使用 element.text 读取元素的文本
  • 通过调用 .is_displayed() 方法检查元素是否显示在页面上

Selenium 实战示例

现在项目已搭建完成,我们可以开始进行网页抓取了。

登录

我们将通过登录 Reddit 账户并抓取之前展示的数据,来展示 Selenium 的强大功能。首先,让我们让 Selenium 点击页面顶部的登录按钮。检查页面 HTML 后,我们可以发现登录按钮的类名是 _2tU8R9NTqhvBrhoNAXWWcP

login_button = driver.find_element_by_class_name('_2tU8R9NTqhvBrhoNAXWWcP')
login_button.click()

这将打开登录弹窗,其中可见需要填写的用户名和密码输入框。接下来继续执行以下代码:

driver.switch_to_frame(driver.find_element_by_class_name('_25r3t_lrPF3M6zD2YkWvZU'))

driver.find_element_by_id("loginUsername").send_keys('USERNAME')
driver.find_element_by_id("loginPassword").send_keys('PASSWORD')

driver.find_element_by_xpath("//button[@type='submit']").click()

如果检查该模态框元素,我们会发现其容器是一个 iframe。这就是为什么我们在代码开头需要切换到 frame 模式,因为如果不这样做,选择输入框会导致错误。

接下来,我们获取输入字段并输入正确的凭据,然后点击提交按钮。这将带我们返回 /r/learnprogramming 页面,但此时我们已登录并准备好点赞!

截取屏幕截图

使用 Selenium 和 Python 截屏非常简单。你只需在声明 WebDriver 之后,在 scraper.py 文件中编写以下命令即可。

driver.save_screenshot('screenshot.png')

值得注意的是,您可以通过添加以下代码行来设置 Google Chrome 窗口的大小:

from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--window-size=1920,1080")

在我们的示例中,截图效果如下:

提取数据

如前所述,我们需要获取帖子的标题、作者以及赞数。首先导入 BeautifulSoup 和 Pandas 包,并为每种所需信息类型创建三个空数组。

from bs4 import BeautifulSoup
import pandas as pd

titles = []
upvotes=[]
authors = []

我们将使用 BeautifulSoup 解析 HTML 文档,编写以下代码:

content = driver.page_source
soup = BeautifulSoup(content, features="html.parser")

成功解析 HTML 文档并选择正确的选择器后,我们将提取标题、赞数和作者信息,并将其赋值给相应的数组:

for element in soup.findAll('div', attrs={'class': '_1oQyIsiPHYt6nx7VOmd1sz'}):
   title = element.find('h3', attrs={'class': '_eYtD2XCVieq6emjKBH3m'})
   upvote = element.find('div', attrs={'class': '_3a2ZHWaih05DgAOtvu6cIo'})
   author = element.find('a', attrs={'class': '_23wugcdiaj44hdfugIAlnX'})
   titles.append(title.text)
   upvotes.append(upvote.text)
   authors.append(author.text)

最后,我们将使用之前导入的 Pandas 包,将这些信息存储到 CSV 文件中。

df = pd.DataFrame({'Post title': titles, 'Author': authors, 'Number of upvotes': upvotes})
df.to_csv('posts.csv', index=False, encoding='utf-8')

就这样!让我们来看看导出的文件:

看起来包含我们所需的所有信息。

额外提示:有时,网站在首次加载时提供的数据并不完整。大多数情况下,数据加载操作会在用户向下滚动时触发。如果您需要向下滚动以获取更多数据,可以使用 .execute_script() 方法,如下所示:

scrollDown = "window.scrollBy(0,2000);"
driver.execute_script(scrollDown)

结语

希望你和我一样享受制作这个网页抓取工具的过程。编程并非总是充满乐趣,但编写这样的小脚本让我回想起初学编程时的时光,也让整个过程变得更有趣。

不过,本教程中构建的脚本尚无法胜任繁重的工作。它缺少几项关键功能,而这些功能正是让网页抓取体验流畅无瑕的关键。例如通过移动或住宅代理连接,以及破解验证码等。

如果您正在寻找更专业的数据提取方案,不妨了解一下 WebScrapingAPI 的功能,亲自验证它是否符合您的需求。该服务提供免费套餐,您只需投入 30 分钟的时间即可体验。

感谢您抽出时间阅读本文。祝您爬取顺利!

关于作者
Robert Sfichi, 全栈开发工程师 @ WebScrapingAPI
Robert Sfichi全栈开发工程师

罗伯特·斯菲奇是 WebScrapingAPI 的团队成员,致力于产品开发,并协助构建可靠的解决方案,以支持该平台及其用户。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。