Selenium和Python进行网页抓取

如何使用Selenium和Python抓取网页内容

Selenium 是一种流行的网络抓取开源库,它使用 WebDriver 协议来控制 Chrome、Firefox 和 Safari 等浏览器。但为什么这有用?传统的抓取工具难以从依赖 JavaScript 的网站收集数据。那是因为你需要运行 JS,而 Selenium 支持它。

该库还提供了多种方法来像人类用户一样与页面进行交互,这意味着您可以获得额外的功能并且更容易避免被阻止。一些行动的例子是:

  • 向下滚动。
  • 点击按钮。
  • 填写表格。
  • 截图。

让我们深入研究使用 Selenium 和 Python 进行网络抓取!

首先,我们将完成这些步骤,让一切准备就绪,以遵循此 Selenium 网络抓取教程并运行无头浏览器

准备工作

您需要安装Python 3。由于许多系统已经配置了它,您甚至可能不需要安装它。通过在终端中运行以下命令来验证:

python --version

如果你这样做,该命令将打印如下内容:

Python 3.11.2

如果返回的版本是 2.x 或错误终止,则必须安装 Python。从官网下载Python 3.x,按照安装向导进行设置。

Selenium 支持多种浏览器,但我们将使用Google Chrome,因为它是最受欢迎的浏览器,市场份额超过 65%。因此,您需要安装:

接下来,建立一个新的 Python 项目并安装Selenium Python 绑定包

pip install selenium

您现在可以开始通过 Selenium 控制 Chrome。初始化一个scraper.py文件如下:

from selenium import webdriver

# replace this with the complete path
# to your Chrome driver
CHROME_DRIVER_PATH = '/path/to/ChromeDriver'
# initialize the Selenium WebDriver
driver = webdriver.Chrome(executable_path=CHROME_DRIVER_PATH)
# visit your target site
driver.get('https://scrapingclub.com/')

# scraping logic...

# release the resources allocated by Selenium
# and shut down the browser
driver.quit()

此 Python 片段包含开始使用 Selenium 所需的基本逻辑。它初始化一个ChromeWebDriver实例并使用它来访问我们将使用的目标站点ScrapingClub 。

上面的代码非常有用,除了一个小问题:每次 Chrome 升级到新版本时,您都需要重新下载 ChromeDriver。这很麻烦、令人沮丧,并且涉及样板操作。webdriver-manager但是您可以使用Python 包来避免这一切,以简化驱动程序管理。安装它:

pip install webdriver-manager

WebDriver然后,使用它来配置具有以下内容的Chrome :

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
driver.get('https://scrapingclub.com')

正如所见,我们的代码不再涉及到 ChromeDriver 的任何路径。

webdriver_manager仅供参考:您也可以与其他浏览器一起使用。在撰写本文时,它还支持GeckoDriverIEDriverOperaDriverEdgeChromiumDriver

scraper.py使用以下代码验证您的 Selenium 脚本是否有效:

python scraper.py

Python 应用程序应打开此 Chrome 窗口:

medium_Chrome_Alert_Popup_4fa9d7c513

请注意“Chrome 正在被自动测试软件控制”消息,这是一个额外的警告部分,通知您 Selenium 正在控制 Chrome 实例。

伟大的!您的 Python 脚本按预期工作。但是真的有必要打开一个 Chrome 窗口吗?

让我们深入研究一下。

Chrome 无头模式

Selenium 以其无头浏览器功能而闻名。无头浏览器是一种没有图形用户界面 (GUI) 但具有真实浏览器的所有功能的浏览器。

Options通过定义适当的对象并将其传递给 WebDriver Chrome 构造函数,在 Selenium 中为 Chrome 启用无头模式。此外,您必须从 Chrome 109 开始设置headless=new激活无头模式。

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

# enable headless mode in Selenium
options = Options()
options.add_argument('--headless=new')

driver = webdriver.Chrome(
    options=options, 
    # other properties...
)

这是在以前版本的浏览器上启用它的方法:

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

options = Options()
# enable headless mode
options.headless = True

driver = webdriver.Chrome(
    options=options, 
    #...
)

Selenium 现在将启动一个无头 Chrome 实例。因此,如果您再次运行该脚本,您将不会再看到 Chrome 窗口。这是在服务器上运行抓取脚本时生产环境的理想设置,因为您不想在 GUI 上浪费资源。

同时,在测试爬虫时,查看 Chrome 窗口中发生的情况非常有用,因为它允许您直接在浏览器中观察脚本的效果。

设置部分完成。是时候动手使用一些 Python Selenium 网络抓取逻辑和提取数据了!

如何查找网页元素

Web 抓取需要从 DOM(文档对象模型)中选择 HTML 元素来提取它们的数据。为此,Selenium 提供了两种主要的方法来定位页面上的元素:

  • find_element:查找特定的单个元素。
  • find_elements:查找所有符合选择策略的元素。

两者都支持七种不同的方法来定位 HTML 元素。这是一个汇总表:

方法 描述 HTML 示例代码 实例
By.ID 根据id属性选择 HTML 元素 <div id="s-437">...</div> find_element(By.ID, "s-437")
By.NAME 根据name属性选择 HTML 元素 <input name="email" /> find_element(By.NAME, "email")
find_elements(By.NAME, "email")
By.XPATH 选择匹配XPath表达式的HTML 元素 <h1>My <strong>Fantastic</strong> Blog</h1> find_element(By.XPATH, "//h1/strong")
find_elements(By.XPATH, "//h1/strong")
By.LINK_TEXT 选择<a>包含特定链接文本的 HTML 元素 <a href="/">Home</a> find_element(By.LINK_TEXT, "Home")
find_elements(By.LINK_TEXT, "Home")
By.TAG_NAME 根据标签名称选择 HTML 元素 <span>...</span> find_element(By.TAG_NAME, "span")
find_elements(By.TAG_NAME, "span")
By.CLASS_NAME 根据class属性选择 HTML 元素 <div class="text-center">Welcome!</div> find_element(By.CLASSNAME, "text-center")
find_elements(By.CLASSNAME, "text-center")
By.CSS_SELECTOR 选择匹配CSS 选择器的HTML 元素 <div class="product-card"><span class="price">$140</span></div> find_element(By.CSS_SELECTOR, ".product-card .price")
find_elements(By.CSS_SELECTOR, ".product-card .price")

由于查询可以识别多个元素,因此这两种方法的行为如下:

  • find_element: 返回匹配搜索条件的第一个 HTML 元素。
  • find_elements:返回数组中所有符合搜索条件的。

您可能想知道如何定义有效的位置策略,这就是答案:在您的目标页面上使用浏览器的开发人员工具。为此,右键单击 HTML 元素并选择“检查”以打开 DevTools:

medium_Dev_Tools_Inspect_3511ffeab4

然后,分析页面的 DOM 并找出可行的选择策略。您还可以直接获取 XPath 表达式和 CSS 选择器:右键单击一个元素,打开“复制”菜单,然后选择“复制选择器”或“复制 XPath”以获取与所选元素相关的元素元素。

XPATH此功能在使用or方法时很有用CSS_SELECTOR。同时,请记住,您必须仅将这些自动生成的选择器视为起点。它们中的大多数不适合抓取,但它们仍然可以帮助您了解选择器的工作原理。

让我们看看一些实际的搜索策略!假设您要选择上面屏幕截图中显示的“练习 #1”卡片。您可以通过以下方式实现find_element()

from selenium.webdriver.common.by import By
#... 

exercise1_card = driver.find_element(By.CLASS_NAME, 'card')
# or
exercise1_card = driver.find_element(By.XPATH, '/html/body/div[3]/div/div[1]/div')
# or
exercise1_card = driver.find_element(By.CSS_SELECTOR, '.card')

这些都是达到目的的有效方法。

现在,如果您想获取页面上的所有练习卡,请使用find_elements()

from selenium.webdriver.common.by import By
# ...

exercise_cards = driver.find_elements(By.CLASS_NAME, 'card')
# or
exercise_cards = driver.find_elements(By.XPATH, '/html/body/div[3]/div/div/div[@class='card card-full']')
# or
exercise_cards = driver.find_elements(By.CSS_SELECTOR, '.card')

极好的!再一次,所有选项都像一个魅力。

如果要查找单个 HTML 元素,可以选择By.ID. 然而,并非所有人都具有该id属性。相反,By.CSS_SELECTORBy.XPath允许您选择 DOM 中的任何 HTML 元素。这就是为什么使用 CSS 选择器或 XPath 表达式是推荐的方法。

find_element()并分别find_elements()返回一个或多个 SeleniumWebElement对象。但是 Selenium 中的 a 是什么WebElement,您可以用它做什么?找出下面!

如何像在浏览器中那样与网页交互

当用户在浏览器中访问网页时,他们通过其 HTML 元素与其进行交互:他们单击它们、读取它们的数据、使用它们输入信息等。这些只是您可以通过其在页面上执行的一些操作元素。

SeleniumWebElement对象表示 DOM 中的 HTML 节点,同时WebElement公开了几种与底层元素交互的方法。这允许您像人类用户一样使用 DOM 节点。

让我们看一个例子!在 Selenium 中连接到 ScrapeClub 的登录表单页面。这是登录<form>的样子:

medium_Login_Form_e88fa5ad4d

WebElement您可以对对象执行的一些最常见的操作是:

  • 单击 HTML 元素:
submit_button = driver.find_element(By.CSS_SELECTOR, 'form button')
submit_button.click()

此代码段单击“登录”按钮并提交表单。该click()方法允许您单击所选元素。

  • 在 HTML 文本元素中键入数据:
name_input = driver.find_element(By.ID, 'id_name')
name_input.send_keys('Serena')

此代码用“Serena”填充“Name”输入元素。'sWebElement方法send_keys()模拟键入。

  • 获取包含在 HTML 元素中的文本:
name_label = driver.find_element(By.CSS_SELECTOR, 'label[for=id_name]')
print(name_label.text)

name打印:

Name*

在这里,text属性为您提供文本内容。

  • 获取 HTML 元素的属性中包含的数据:
hidden_input = driver.find_element(By.CSS_SELECTOR, 'input[type=hidden]')
hidden_input_value = hidden_input.get_attribute('value')
print(hidden_input_value)

该片段返回:

VanmxjKNMqfYxyAj7AonjpsAWmCq87Pkg2IjbUeXeV7E9wxpFuqkua3DXTly2dgc

get_attribute()a 的方法返回WebElement指定属性的字符串值。

最后两个示例在涉及 Python Selenium 网络抓取时特别有用。请记住,您也可以find_element()在. 这会将搜索限制在所选 HTML 元素的子元素中。find_elements()WebElement

现在,您可以使用 Selenium 做很多其他事情。继续阅读!

等待元素出现

大多数网站都依赖 API 调用来获取所需的数据。第一次加载后,他们通过JavaScript 中的AJAX执行许多异步 XHR 请求。因此,他们检索一些内容,然后用它来动态地用新的 HTML 元素填充 DOM。这就是 React、Vue 和 Angular 等流行的客户端渲染技术的工作原理。

查看 DevTools 窗口的“网络”选项卡。在“Fetch/XHR”部分,您可以看到页面执行的 AJAX 请求:

medium_Network_Tab_9c4ec3bcd4

将 HTML 源代码与当前的 DOM 进行比较以研究差异。使用开发人员工具了解目标页面的作用以及它如何使用 JavaScript 来操作 DOM。请记住,网站可以依赖 JavaScript 来完全呈现其页面或仅呈现部分页面

使用 JS 呈现的页面,您无法立即开始抓取数据。那是因为 DOM 只会在一段时间后完成。换句话说,您必须等到 JavaScript 完成它的工作。

您有两种方法可以使用 Selenium 从此类页面中抓取数据:

  • time.sleep()在从 DOM 中选择元素之前,停止 Python Selenium 网络抓取脚本几秒钟。
  • WebDriverWait在代码中进一步处理之前等待特定条件。

time.sleep()大多数时候使用Python 函数是可行的。但是你应该等多久?没有绝对的答案,因为这完全取决于具体情况和网络条件。可以肯定的是,等待太久或太短在任何一种情况下都不理想。

这就是为什么您应该更喜欢基于 SeleniumWebDriverWait类的第二种方法。这允许您只等待需要的时间。在下面查看它的实际效果:

使用 Selenium 连接到 ScrapeClub 的Mimicking Ajax 请求页面,这是一个很好的依赖 AJAX 调用的页面示例。

medium_AJAX_Example_1bb806285d

该页面调用 API 请求以检索突出显示的卡片中显示的内容。使用 WebDriver 抓取其数据,如下所示:

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# ...

# wait up to 3 seconds until there is the 'Jersey Dress' string
# in the '.card-title' element
element = WebDriverWait(driver, 3).until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.card-title'), 'Jersey Dress')
)

# you are now sure that the card has been loaded
# and can scrape data from it
card = driver.find_element(By.CSS_SELECTOR, '.card')
product_name = driver.find_element(By.CSS_SELECTOR, '.card-title').text
product_image = driver.find_element(By.CSS_SELECTOR, '.card-img-top').get_attribute('src')
product_price = driver.find_element(By.CSS_SELECTOR, '.card-price').text
product_description = driver.find_element(By.CSS_SELECTOR, '.card-description').text

print(f'Product title: {product_name}')
print(f'Product image: {product_image}')
print(f'Product price: {product_price}')
print(f'Product description: {product_description}')

此代码最多等待三秒钟,直到卡片标题 HTML 元素包含预期的文本。一旦满足条件,它就会从卡中抓取数据。但是,如果在指定的超时时间内没有出现预期的情况,TimeoutException则会引发 a 。

上面的 Selenium 网络抓取 Python 代码打印如下:

Product title: Jersey Dress
Product image: https://scrapingclub.com/static/img/96113-C.jpg
Product price: $19.99
Product description: Fitted dress in jersey with long, straight sleeves. Unlined. 72% polyester, 23% rayon, 5% spandex. Machine wash...

你可以等几个ExpectedConditions。最受欢迎的是:

  • title_contains: 直到页面标题包含特定的字符串。
  • presence_of_element_located: 直到 HTML 元素出现在 DOM 中。
  • visibility_of: 直到一个已经在 DOM 中的元素变得可见。
  • text_to_be_present_in_element: 直到元素包含特定文本。
  • element_to_be_clickable: 直到 HTML 元素可点击。
  • alert_is_present:直到出现 JavaScript 本机警报。

现在应该很清楚了:Selenium 允许您抓取严重依赖 JavaScript 的网页。毕竟,您拥有一个完整的浏览器供您使用。

接下来,让我们了解如何使用 Selenium 进一步利用 JavaScript。

如何在 Python 中使用 Selenium 运行 JavaScript

如果您的爬虫运行 JavaScript,那么它可以从任何类型的网页中检索数据。因此,您的 Python Selenium 脚本将能够抓取服务器端和客户端呈现的网站。

您的目标站点可能会使用 JavaScript 异步检索数据,根据特定操作添加或删除元素,或实施反抓取挑战。对于使用 Selenium 的 Python 网络抓取工具,在大多数情况下,这些都不代表问题。

相反,基于 Beautiful Soup 或类似技术的蜘蛛无法抓取此类站点。那是因为只有浏览器才能运行 JavaScript,这就是 Selenium 的意义所在。这就是使用 Selenium 进行网页抓取的真正力量!

使用 Python 和 Selenium,网络抓取没有限制!让我们看一些示例,其中运行 JavaScript 的能力起着关键作用。

无限滚动

无限滚动是网站用来避免分页的一种有效方法,因此用户无需单击以加载下一页,新内容会在用户向下滚动时通过 AJAX 动态加载。访问Scraping Infinite Scrolling Pages (Ajax)示例。

要模拟按下网页上的“结束”键,您首先必须选择一个元素(例如,)<body>并将键发送到那里:

XK_6_N_Sn0_Q_08de453cf8

要抓取使用无限滚动加载数据的页面,您需要指示浏览器向下滚动。如何?使用空格键、“Page Down”或“End”键。

from selenium.webdriver import Keys
# ...

driver.find_element(By.TAG_NAME, 'body').send_keys(Keys.END)

当谈到无限滚动时,您必须在所有元素加载时多次应用此逻辑。此外,如前所述,您必须等待新的出现。您可以使用 Python 在 Selenium 中实现这一点,如下所示:

cards = []
old_card_size = len(cards)
while True:
    # reach the end of the scroll bar
    driver.find_element(By.TAG_NAME, 'body').send_keys(Keys.END)

    # wait 3 seconds for new elements to load
    time.sleep(3)

    # retrieve all cards
    cards = driver.find_elements(By.CSS_SELECTOR, '.card')

    # if no new cards were found
    if (old_card_size == len(cards)):
        # break the cycle since the scroll
        # is over
        break

    # keep track of the number of cards
    # currently discovered
    old_card_size = len(cards)

# scrape data from cards...

while循环允许您从出现的内容中抓取所有信息。但是,请记住,您无法提前知道将加载哪些元素,这会导致不知道WebDriverWait. 因此,最好选择time.sleep()这种情况。

填写表格

这是您可以使用 Selenium 填写 ScrapingClub登录表单页面上的表单的方法:

# retrieve the form elements
name_input = driver.find_element(By.ID, 'id_name')
password_input = driver.find_element(By.ID, 'id_password')
submit_button = driver.find_element(By.CSS_SELECTOR, 'form button[type=submit]')

# filling out the form elements
name_input.send_keys('scrapingclub')
password_input.send_keys('scrapingclub')

# submit the form and log in
submit_button.click()

这在抓取受登录保护的页面时会派上用场。

使用 Selenium 截取屏幕截图

除了抓取文本数据外,Selenium 还允许您截取屏幕截图,这是一个对调试很有用的功能,用视觉证据支持您的论文,或抓取 UI 选择。例如,您可能想要截屏以检查竞争对手如何在其网站上展示产品。

整个页面截图如下:

driver.save_screenshot('screenshot.png')

结果如下:

medium_scrapingclub_screeenshot_6873297991

您还可以截取单个元素的屏幕截图:

# select the desired element
card = driver.find_element(By.CSS_SELECTOR, '.card')
# take a screenshot of the selected element
card.screenshot("card_screenshot.png")

这将为您提供以下内容:

Single_Element_Screenshot_b5a517359c

 

内置方法

在浏览器控制台中,您可以在页面上启动 JavaScript 指令。不要忘记 Selenium 提供对所有浏览器功能的访问,这意味着您可以通过浏览器的 JS 引擎发送命令。

execute_script()方法使您能够同步执行 JavaScript 指令。当 Selenium 公开的功能不足以实现您的目标时,这尤其有用。

为避免这种情况,请使用window.scrollBy()JavaScript 函数。截图前滚动到元素位置:

# select the desired element
card = driver.find_element(By.CSS_SELECTOR, '.card')
# retrieve the y position on the page
# of the selected element
card_y_location = card.location["y"]
# "-100" to give some extra space and make
# make sure the screenshot will be taken correctly
javaScript = f'window.scrollBy(0, {card_y_location}-100);'
driver.execute_script(javaScript)

execute_script()还可以通过returnJavaScript 关键字将值传递给您的脚本。看看它怎么运作:

title = driver.execute_script('return document.title')
print(title) # "ScrapingClub | Learn Web Scraping Using Python for free"

上面的代码将从 JavaScript 读取的页面标题值传递给titlePython 变量。

自定义窗口大小

如今,大多数网站都是响应式的,这意味着它们会根据用户屏幕或浏览器窗口的大小调整布局。在某些情况下,它们甚至可以通过 JavaScript 显示或隐藏元素,具体取决于可用空间。这是抓取时要考虑的一个重要方面,Selenium 允许您通过两种方式更改浏览器窗口的初始大小:

在下面的示例中查看它们的实际效果:

options = Options()

# set the initial window size
options.add_argument('--window-size=800,600') 
driver = webdriver.Chrome(
    options=options,
    # ...
)
# print the current window size
print(driver.get_window_size()) # {"width": 800, "height": 600} 

# change the window size in a
# different way   
driver.set_window_size(1920, 1200) 
 
# scraping logic...

# print the new window size 
print(driver.get_window_size()) # {"width": 1400, "height": 1200}

我们曾经get_window_size()检查过当前窗口的宽度和高度。这在几种情况下会派上用场,例如在截取屏幕截图之前确保浏览器窗口的大小正确。

在 Python 中使用 Selenium 绕过反抓取保护

您知道如何在 Python 中使用 Selenium 进行网络抓取。然而,从 Web 检索数据并不那么容易,因为某些网站采用了反抓取技术,可能会将您的脚本检测为机器人并阻止它。

让我们用一个例子来详细说明!

尝试使用 Selenium 从 G2 产品页面抓取数据:

driver.get('https://www.g2.com/products/zenrows/reviews')
# scraping logic...

它的块技术将检测您的脚本并阻止它访问该站点:

medium_Access_Denied_13b17f087c

 

您可能会构建最好的 Selenium Python 网络抓取脚本。但是,如果它被检测到并被阻止,那将是毫无意义的努力!

反抓取可能对您的数据检索过程构成巨大挑战。找出一些有价值的技巧和技术来执行网页抓取而不会被阻止

作为替代方案,考虑采用ZenRows来避免您的头痛并轻松绕过所有反抓取保护。

添加真实标题

反机器人技术通常检查的基本标头之一是User-Agent. Selenium 默认提供一个,但您可以自定义它以添加一个真实的并增加不被阻止的机会:

options = Options()
# Chrome 104 Android User Agent
custom_user_agent = "Mozilla/5.0 (Linux; Android 11; 100011886A Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.69 Safari/537.36"
options.add_argument(f'user-agent={custom_user_agent}')
driver = webdriver.Chrome(
    options=options,
    # ...
)

# visit a page
driver.get("https://scrapingclub.com/")

# print the user agent used to perform the request
print(driver.execute_script("return navigator.userAgent")) # "Mozilla/5.0 (Linux; Android 11; 100011886A Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.69 Safari/537.36"

注意:User-Agent如果您忘记调整其他标题,更改可能会适得其反。例如,sec-ch-ua标头通常也会发送您浏览器的版本,这意味着它必须与User-Agent.

这是sec-ch-uaGoogle Chrome 版本 110 上的样子:
"Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"

同时,旧版本根本不发送该标头,因此添加它可能是可疑的。

User-Agent在上面的代码片段中,您在浏览器配置中设置了,而不是通过 Selenium 方法设置,因为 Selenium 不支持设置自定义标头。这就是像Selenium Wire这样的第三方 Python 库发挥作用的地方。

它扩展了 Selenium,使您能够访问浏览器进行的底层进程,并允许您拦截请求、更新标头或添加新请求。使用以下命令安装它:

pip install selenium-wire

使用 Selenium Wire 设置自定义标头,如下所示:

from seleniumwire import webdriver
# ...

user_agent = 'Mozilla/5.0 (Linux; Android 11; 100011886A Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.69 Safari/537.36'
sec_ch_ua = '"Google Chrome";v="104", " Not;A Brand";v="105", "Chromium";v="104"'
referer = 'https://www.google.com'

def interceptor(request):
    # delete the "User-Agent" header and
    # set a new one
    del request.headers["user-agent"]  # Delete the header first
    request.headers["user-agent"] = user_agent
    # set the "Sec-CH-UA" header
    request.headers["sec-ch-ua"] = sec_ch_ua
    # set the "referer" header
    request.headers["referer"] = referer

options = Options()
# setting options...

driver = webdriver.Chrome(
    options=options,
    # ...
    service=ChromeService(ChromeDriverManager().install())
)
# set the Selenium Wire interceptor
driver.request_interceptor = interceptor

# a page that gives you the HTTP headers
# of the request
driver.get("http://httpbin.org/anything")

# print the headers used to perform the request
# they will match the ones set with Selenium Wire
print(driver.find_element(By.TAG_NAME, "body").text)

注意:当用库更改标题时,必须先删除原始标题。这将避免发送重复项。

使用代理更改 IP

Selenium 对代理的支持有限,但这是使用免费代理列表实现代理的方式:

from selenium import webdriver 
# ... 

# free proxy IP
options = Options()
# free proxy server
proxy_server_ip = "143.198.228.250"
options.add_argument(f'--proxy-server={proxy_server_ip}')

driver = webdriver.Chrome(
    options=options
    # ...
)
# to get the IP the request comes from
driver.get('http://httpbin.org/ip')
print(driver.find_element(By.TAG_NAME, "body").text)  # { "origin": "143.198.228.250" }

注意:请记住,这些方法不可靠且寿命短,因此我们使用的方法可能根本不适合您。

对于需要身份验证的更复杂的解决方案或代理,Selenium Wire 可以再次为您提供帮助。将其配置为使用ZenRows 提供的代理服务器 API,它提供了一个高级 IP 池:

proxy_username = 'YOUR_ZENROWS_API_KEY'
selenium_wire_options = {
    'proxy': {
        'http': f'http://{proxy_username}:@proxy.zenrows.com:8001',
        'verify_ssl': False,
    },
}

driver = webdriver.Chrome(
    options=options,
    seleniumwire_options=selenium_wire_options
    # ...
)
driver.get('http://httpbin.org/ip')
print(driver.find_element(By.TAG_NAME, 'body').text)

您可以通过免费注册获得您的 ZenRows API 密钥。

如果您的代理服务器不会自动轮换 IP 而您需要一个新身份,请driver.proxy按以下方式覆盖:

#... 

# using the proxy set initial
driver.get(url)  
# set a new proxy
driver.proxy = { 
        "http": "http://<PROXY_USER>:<PROXY_PASSWORD>@<PROXY_IP>:<PROXY_PORT>", 
} 
# the request will use the new proxy
driver.get(url)

根据需要多次重复此操作。或者,为了方便和可靠,更喜欢具有 IP 轮换功能的高级代理。

检测刮刀的隐藏元素

一些网站依靠蜜罐陷阱(真实用户看不到但对机器人有用的元素)来检测和阻止抓取工具。

假设您的站点包含一个不可见的蜜罐链接,如下所示:
<a href="https://your-target-site/honeypot-page" style="display: none">Click here</a>

您可能想要检索<a>页面中的所有元素并提取它们的 URL 以进行抓取。但是,当跟随蜜罐链接时,您的 Selenium 网络抓取工具将被检测为机器人。

WebElement公开一个is_displayed()方法,允许您验证 HTML 元素是否对用户可见。在上面的场景中,您可以使用它以这种方式过滤掉不可见的链接:

a_elements = driver.find_elements(By.TAG_NAME, "a")
# filter out non-visible a elements
visible_a_elements = list(filter(lambda e: (e.is_displayed()), a_elements))

使用 Selenium 进行 Web 抓取时节省资源

Selenium 使您可以访问完整范围的标准浏览器功能,帮助您将抓取过程提升到一个新的水平。但是,您可能并不总是需要所有这些功能。

例如,如果您不需要执行屏幕截图,那么加载图像就没有什么价值,因为那会浪费网络资源。实际上,图像占页面总重量的很大一部分!值得庆幸的是,Selenium 为该问题和类似问题提供了解决方案。

通过阻止某些资源,您可以提高性能和带宽并避免被跟踪。这些操作在扩展您的 Selenium Python 抓取操作时特别有用。

接下来找出一些真实世界的例子。

块图像

以这种方式阻止浏览器在 Selenium 中加载图像:

options = Options()
# block image loading
options.experimental_options['prefs'] = {
    'profile.managed_default_content_settings.images': 2
}

driver = webdriver.Chrome(
    options=options
)
# load a page that involves several images
driver.get('https://scrapingclub.com/exercise/list_infinite_scroll/')

# take a screenshot
driver.save_screenshot('screenshot.png')

该代码生成以下屏幕截图:

medium_Scraping_Club_Screenshot_e9ea597ffd

阻止 JavaScript

同样,您可以像这样阻止由 Selenium 控制的浏览器运行 JavaScript:

options = Options()
# block image loading
options.experimental_options['prefs'] = {
    'profile.managed_default_content_settings.javascript': 2
}

driver = webdriver.Chrome(
    options=options
)

拦截请求

感谢 Selenium Wire,您可以以编程方式拦截和停止请求。这意味着您可以有效地阻止某些图像,同时允许其他图像!让我们看看如何:

def interceptor(request): 
        # block only GIF images  
        if request.path.endswith((".png", ".gif")): 
                request.abort() 
 
driver = webdriver.Chrome(
    options=options,
    seleniumwire_options=selenium_wire_options
    # ...
)

您还可以使用该选项阻止域exclude_hosts,或者仅允许基于与正则表达式匹配的 URL 的特定请求driver.scopes

这些只是几个例子,但还有更多需要了解。按照我们的指南深入研究在网络抓取中拦截 XHR 请求

结论

这个循序渐进的教程涵盖了使用 Selenium在 Python 中进行网页抓取的最重要知识。你现在知道:

  • 如何在 Python 中设置 Selenium。
  • 在 Selenium 中定位网页元素的基础知识。
  • 如何使用 Selenium 与浏览器中页面上的 Web 元素交互。
  • 如何通过 Selenium 在浏览器中运行 JavaScript 代码。
  • 如何避免 Selenium 中的一些反刮方法。

正如所见,数据提取涉及许多挑战,主要是由于网站采用的反抓取技术。绕过它们既复杂又麻烦,但是您可以使用像 ZenRows 这样的高级 Python 网络抓取 API 来忘记所有这些使用它通过 API 请求运行数据抓取并避免反机器人保护。

常见问题

Web 抓取中的 Selenium 是什么?

Selenium 是一种流行的网络抓取解决方案,它允许您创建像浏览器一样与网页交互的脚本。它的无头浏览器功能对于呈现 JavaScript 和避免被阻止很有用。

Selenium 可以用于网页抓取吗?

尽管 Selenium 是为自动化测试而创建的,但您可以将其用于网络抓取。它与网页交互和模拟人类行为的能力使其成为一种流行的数据提取工具。

如何在 Python 中使用 Selenium 进行网页抓取?

在 Python 中使用 Selenium 进行网页抓取涉及以下步骤:

  1. 使用 为 Python 安装 Selenium 绑定pip install selenium,并下载与您的浏览器兼容的 Web 驱动程序。
  2. 在您的 Python 代码中导入 Selenium 库并创建一个新WebDriver实例。
  3. 使用驱动程序实例导航到目标页面。
  4. 实施抓取逻辑并从中提取数据。

Selenium 适合网页抓取吗?

Selenium 是网络抓取的绝佳选择,尤其是对于依赖 JavaScript 呈现整个页面或具有动态内容的网站而言。同时,如果配置不正确,它可能比其他抓取解决方案更慢且更耗费资源。

 

 

类似文章