如何使用Python抓取动态网页

如何使用Python抓取动态网页数据

在抓取动态网页内容时,您是否得到了糟糕的结果?不仅仅是你。爬取动态数据对于标准爬虫来说是一项具有挑战性的工作(至少可以说)。这是因为当发出 HTTP 请求时,JavaScript 在后台运行。

抓取动态网站需要在浏览器中渲染整个页面并提取目标信息。

加入我们这个循序渐进的教程,学习使用 Python 进行动态网页抓取所需的一切——注意事项、挑战和解决方案,以及介于两者之间的一切。

什么是动态网站?

动态网站是一种不直接在静态 HTML 中包含所有内容的网站。它使用服务器端或客户端来显示数据,有时基于用户的操作(例如,单击、滚动等)。

简而言之,这些网站会根据每个服务器请求显示不同的内容或布局。这有助于缩短加载时间,因为无需在用户每次想要查看“新”内容时都重新加载相同的信息。

如何识别它们?一种方法是在浏览器的命令面板中禁用 JavaScript。如果网站是动态的,内容就会消失。

让我们以Saleor React Storefront 为例。这是它的首页的样子:

medium_reactstorefront_b1dc757225

注意标题、图像和艺术家的名字。

现在,让我们使用以下步骤禁用 JavaScript:

  1. 检查页面:右键单击并选择“检查”以打开 DevTools 窗口。
  2. 导航到命令面板:CTRL/CMD + SHIFT + P。
  3. 搜索“JavaScript”。
  4. 单击禁用 JavaScript
  5. 点击刷新。

结果如何?见下文:

medium_reactstorefront_empty_9a3e033fa7

禁用 JavaScript 会删除所有动态 Web 内容。

使用 Python 进行动态 Web 抓取的替代方案

所以,你想用 Python 抓取动态网站……

由于Beautiful SoupRequests等库不会自动获取动态内容,因此您有两种选择来完成任务:

  • 将内容提供给标准库。
  • 抓取时执行页面的内部 JavaScript。

然而,并不是所有的动态页面都是一样的。有些通过 JS API 呈现内容,可以通过检查“网络”选项卡访问这些内容。其他人将 JS 呈现的内容作为 JSON 存储在 DOM(文档对象模型)的某处。

好消息是,在这两种情况下,我们都可以解析 JSON 字符串以提取必要的数据。

请记住,有些情况下这些解决方案不适用。对于此类网站,您可以使用无头浏览器来呈现页面并提取所需数据。

使用 Python 爬取动态网页的替代方案是:

  • 手动定位数据并解析 JSON 字符串。
  • 使用无头浏览器执行页面的内部 JavaScript(例如,Selenium 或Pyppeteer,Puppeteer 的非官方 Python 端口)。

在 Python 中抓取动态网站的最简单方法是什么?

的确,无头浏览器可能很慢且性能密集。但是,他们取消了对网页抓取的所有限制。也就是说,如果您不计算反机器人检测。你不应该,因为我们已经告诉你如何绕过这些保护

手动定位数据和解析 JSON 字符串假定可以访问动态数据的 JSON 版本。不幸的是,情况并非总是如此,尤其是在涉及高级单页应用程序 (SPA) 时。

更不用说模仿 API 请求是不可扩展的。他们通常需要 cookie 和身份验证以及其他可以轻松阻止您的限制。

在 Python 中抓取动态网页的最佳方式取决于您的目标和资源。如果您有权访问网站的 JSON 并希望提取单个页面的数据,则可能不需要无头浏览器。

然而,除了这一小部分情况,大多数时候使用 Beautiful Soup 和 Selenium 是最好和最简单的选择。

是时候动手了!准备好编写一些代码并准确了解如何使用 Python 抓取动态网站!

准备工作

要学习本教程,您需要满足一些要求。我们将使用以下工具:

  • Python 3:最新版本的 Python 效果最好。在撰写本文时,即 3.11.2。
  • selenium
  • Webdriver Manager:这将确保浏览器和驱动程序的版本匹配。您不必为此手动下载 WebDriver。
pip install selenium webdriver-manager

方法#1:使用 Beautiful Soup 使用 Python 进行动态 Web 抓取

Beautiful Soup 可以说是最流行的用于抓取 HTML 数据的 Python 库。

要用它提取信息,我们需要目标页面的 HTML 字符串。但是,动态内容并不直接出现在网站的静态 HTML 中。这意味着Beautiful Soup 无法访问 JavaScript 生成的数据

这是一个解决方案:如果网站使用 AJAX 请求加载内容,则可以从 XHR 请求中提取数据。

方法#2:使用 Selenium 在 Python 中抓取动态网页

要了解 Selenium 如何帮助您抓取动态网站,首先,我们需要检查常规库(例如 )如何Requests与它们交互。

我们将使用Angular作为我们的目标网站:

medium_angulario_f7708aa6dd

让我们尝试抓取它Requests并查看结果。在此之前,我们必须安装Requests可以使用pip命令执行的库。

pip install requests

下面是我们的代码:

import requests 
 
url = 'https://angular.io/' 
 
response = requests.get(url) 
 
html = response.text 
 
print(html)

如您所见,仅提取了以下 HTML:

<noscript> 
    <div class="background-sky hero"></div> 
    <section id="intro" style="text-shadow: 1px 1px #1976d2;"> 
        <div class="hero-logo"></div> 
        <div class="homepage-container"> 
            <div class="hero-headline">The modern web<br>developer's platform</div> 
        </div> 
    </section> 
    <h2 style="color: red; margin-top: 40px; position: relative; text-align: center; text-shadow: 1px 1px #fafafa; border-top: none;"> 
        <b><i>This website requires JavaScript.</i></b> 
    </h2> 
</noscript>

但是,检查该网站显示的内容比检索到的内容多。

这是我们在页面上禁用 JavaScript 时发生的情况:

medium_angulario_js_disabled_a71c52b18d

这正是Requests能够返回的。该库在从网站的静态 HTML 解析数据时没有发现任何错误,这正是它创建的目的。

在这种情况下,不可能达到与网站上显示的结果相同的结果。你能猜出为什么吗?没错,因为这是一个动态网页。

要访问全部内容并提取我们的目标数据,我们必须呈现 JavaScript。

是时候使用 Selenium 动态网页抓取来解决这个问题了。

我们将使用以下脚本来快速抓取我们的目标网站:

from selenium import webdriver 
from selenium.webdriver.chrome.service import Service as ChromeService 
from webdriver_manager.chrome import ChromeDriverManager 
 
url = 'https://angular.io/' 
 
driver = webdriver.Chrome(service=ChromeService( 
    ChromeDriverManager().install())) 
 
driver.get(url) 
 
print(driver.page_source)

选择selenium中的元素

有多种方法可以访问 Selenium 中的元素。我们在使用 Python 中的 Selenium 进行网络抓取指南中深入讨论了这个问题。

尽管如此,我们还是会用一个例子来解释这一点。让我们只选择目标网站上的 H2:

medium_angulario_h2_big_437e4e8ff1

在此之前,我们需要检查网站并确定我们要提取的元素的位置。

我们可以看到,class="text-container"这些标题很常见。我们复制它并映射 H2 以使用 Chrome 驱动程序获取元素。

medium_angulario_h2_inspect_eac6fc2525

粘贴此代码:

from selenium import webdriver 
from selenium.webdriver.common.by import By 
from selenium.webdriver.chrome.service import Service as ChromeService 
from webdriver_manager.chrome import ChromeDriverManager 
 
# instantiate options 
options = webdriver.ChromeOptions() 
 
# run browser in headless mode 
options.headless = True 
 
# instantiate driver 
driver = webdriver.Chrome(service=ChromeService( 
    ChromeDriverManager().install()), options=options) 
 
# load website 
url = 'https://angular.io/' 
 
# get the entire website content 
driver.get(url) 
 
# select elements by class name 
elements = driver.find_elements(By.CLASS_NAME, 'text-container') 
for title in elements: 
    # select H2s, within element, by tag name 
    heading = title.find_element(By.TAG_NAME, 'h2').text 
    # print H2s 
    print(heading)

您将获得以下内容:

"DEVELOP ACROSS ALL PLATFORMS" 
"SPEED & PERFORMANCE" 
"INCREDIBLE TOOLING" 
"LOVED BY MILLIONS"

如何使用 Selenium 抓取无限滚动网页

当用户向下滚动到页面底部时,一些动态页面会加载更多内容。这些被称为“无限滚动网站”。爬行它们更具挑战性。为此,我们需要指示我们的蜘蛛滚动到底部,等待所有新内容加载,然后才开始抓取。

用一个例子来理解这一点。让我们使用Scraping Club的示例页面。

medium_scraping_club_f567716bee

此脚本将滚动浏览前 20 个结果并提取其标题:

from selenium import webdriver 
from selenium.webdriver.common.by import By 
from selenium.webdriver.chrome.service import Service as ChromeService 
from webdriver_manager.chrome import ChromeDriverManager 
import time 
 
options = webdriver.ChromeOptions() 
options.headless = True 
driver = webdriver.Chrome(service=ChromeService( 
    ChromeDriverManager().install()), options=options) 
 
# load target website 
url = 'https://scrapingclub.com/exercise/list_infinite_scroll/' 
 
# get website content 
driver.get(url) 
 
# instantiate items 
items = [] 
 
# instantiate height of webpage 
last_height = driver.execute_script('return document.body.scrollHeight') 
 
# set target count 
itemTargetCount = 20 
 
# scroll to bottom of webpage 
while itemTargetCount > len(items): 
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight);') 
 
    # wait for content to load 
    time.sleep(1) 
 
    new_height = driver.execute_script('return document.body.scrollHeight') 
 
    if new_height == last_height: 
        break 
 
    last_height == new_height 
 
    # select elements by XPath 
    elements = driver.find_elements(By.XPATH, "//div[@class='card-body']/h4/a") 
    h4_texts = [element.text for element in elements] 
 
    items.extend(h4_texts) 
 
    # print title 
    print(h4_texts)

备注:为无限滚动页面设置目标计数很重要,这样您就可以在某个时候结束脚本。_

在前面的示例中,我们使用了另一个选择器:By.XPath。如前所述,它将基于 XPath 而不是类和 ID 来定位元素。检查页面,右键单击<div>包含要抓取的元素的 a 并选择Copy Path

你的结果应该是这样的:

['Short Dress', 'Patterned Slacks', 'Short Chiffon Dress', 'Off-the-shoulder Dress', ...]

这就是前 20 个产品的 H4!

_Remark:使用 Selenium 进行动态 Web 抓取可能会因连续的Selenium 更新而变得棘手。好好经历最新的变化。_

结论

动态网页无处不在。因此,您很有可能会在数据提取工作中遇到它们。请记住,熟悉它们的结构将帮助您确定检索目标信息的最佳方法。

类似文章