Beautiful Soup:如何用Python构建一个网页爬虫?

2021年11月9日16:49:21 发表评论 1,177 次浏览
Internet 上数量惊人的数据是任何研究领域或个人兴趣的丰富资源。为了有效地收集这些数据,你需要熟练掌握网络抓取。Python 库requests和 Beautiful Soup 是完成这项工作的强大工具。Python Beautiful Soup构建网页爬虫?如果你喜欢通过动手示例学习并且对 Python 和 HTML 有基本的了解,那么本教程适合你。 如何构建一个网页爬虫?在本教程中,你将学习如何:
  • 使用浏览器的开发工具检查目标站点的HTML 结构
  • 解密URL 中编码的数据
  • Beautiful Soup网络爬虫示例:
  • 使用requestsand Beautiful Soup从 Web抓取和解析数据
  • 通过一个步骤网页抓取管道从开始到结束
  • 构建一个从 Web 获取工作机会并在你的控制台中显示相关信息的脚本
Beautiful Soup如何构建网络爬虫?完成这个项目将使你了解在万维网上抓取任何静态网站所需的过程和工具。你可以点击以下链接下载项目源代码: 获取示例代码: 单击此处获取将用于本教程中的项目和示例的示例代码。 让我们开始吧!

什么是网页抓取?

Web 抓取是从 Internet 收集信息的过程。甚至复制和粘贴你最喜欢的歌曲的歌词也是一种网络抓取形式!但是,“网页抓取”一词通常指的是涉及自动化的过程。一些网站不喜欢自动抓取工具收集他们的数据,而另一些网站则不介意。 如果你出于教育目的而恭敬地抓取页面,那么你不太可能遇到任何问题。尽管如此,在开始大型项目之前,自己做一些研究并确保没有违反任何服务条款是个好主意。

网页抓取的原因

假设你是在线和现实生活中的冲浪者,并且你正在寻找工作。但是,你并不是在寻找任何工作。以冲浪者的心态,你正在等待一个完美的机会! 有一个工作网站可以准确地提供你想要的工作类型。不幸的是,一个新职位只会在蓝月亮中弹出一次,并且该网站不提供电子邮件通知服务。你想每天检查它,但这听起来并不是最有趣和最有效的消磨时间的方式。 值得庆幸的是,世界提供了其他方式来应用冲浪者的心态!你可以使用 Python 来帮助自动化你求职中的重复部分,而不是每天查看工作现场。自动网页抓取可以成为加快数据收集过程的解决方案。你编写一次代码,它将多次从许多页面中获取你想要的信息。 相比之下,当你尝试手动获取所需信息时,你可能会花费大量时间点击、滚动和搜索,尤其是当你需要来自定期更新新内容的网站的大量数据时。手动网页抓取可能需要大量时间和重复。 网络上有如此多的信息,并且不断添加新信息。你可能至少会对其中的一些数据感兴趣,而且其中大部分只是为了获取。无论你是真的在找工作还是想下载你最喜欢的艺术家的所有歌词,自动网络抓取都可以帮助你实现目标。

网页抓取的挑战

Web 已经从许多来源有机地发展起来。它结合了许多不同的技术、风格和个性,并且一直发展到今天。换句话说,Web 一团糟!因此,你在抓取 Web 时会遇到一些挑战:
  • 多样性:每个网站都不同。虽然你会遇到重复的一般结构,但每个网站都是独一无二的,如果你想提取相关信息,则需要对其进行个性化处理。
  • 耐用性:网站不断变化。假设你已经构建了一个闪亮的新网络抓取工具,它会自动从你感兴趣的资源中挑选出你想要的内容。第一次运行脚本时,它可以完美运行。但是,当你稍后运行相同的脚本时,你会遇到令人沮丧且冗长的回溯堆栈!
不稳定的脚本是一个现实的场景,因为许多网站都在积极开发中。一旦站点的结构发生变化,你的抓取工具可能无法正确导航站点地图或找到相关信息。好消息是,对网站的许多更改都是​​小的和增量的,因此你可能只需进行最少的调整就可以更新你的抓取工具。 但是,请记住,由于 Internet 是动态的,你将构建的抓取工具可能需要不断维护。你可以设置持续集成以定期运行抓取测试,以确保你的主脚本不会在你不知情的情况下中断。

网页抓取的替代方案:API

一些网站提供商提供应用程序编程接口 (API),允许你以预定义的方式访问他们的数据。使用 API,你可以避免解析 HTML。相反,你可以使用JSON和 XML等格式直接访问数据。HTML 主要是一种以视觉方式向用户呈现内容的方式。 当你使用 API 时,该过程通常比通过网络抓取收集数据更稳定。那是因为开发人员创建的 API 是供程序而不是人眼使用的。 网站的前端呈现可能经常发生变化,但网站设计的这种变化不会影响其 API 结构。API 的结构通常更持久,这意味着它是更可靠的站点数据来源。 但是,API也可能会发生变化。多样性和持久性的挑战适用于 API,就像它们适用于网站一样。此外,如果提供的文档缺乏质量,则自己检查 API 的结构要困难得多。 使用 API 收集信息所需的方法和工具超出了本教程的范围。要了解更多信息,请查看Python 中的 API 集成。

抓取虚假的 Python 工作站点

在本教程中,你将构建一个 Web 抓取工具,从Fake Python Jobs站点获取 Python 软件开发人员的职位列表。这是一个带有虚假招聘信息的示例网站,你可以随意抓取这些信息以训练你的技能。你的网络抓取工具将解析网站上的 HTML 以挑选相关信息并针对特定词过滤该内容。 注意:本教程的先前版本侧重于抓取Monster工作板,此后已更改并且不再提供静态 HTML 内容。本教程的更新版本侧重于自托管静态站点,该站点保证保持不变,并为你提供一个可靠的操场来练习网络抓取所需的技能。 你可以抓取 Internet 上可以查看的任何站点,但这样做的难度取决于站点。本教程向你介绍了网页抓取,以帮助你了解整个过程。然后,你可以对要抓取的每个网站应用相同的过程。 在整个教程中,你还会遇到一些练习块。你可以单击以展开它们并通过完成那里描述的任务来挑战自己。

步骤 1:检查你的数据源

在编写任何 Python 代码之前,你需要了解要抓取的网站。这应该是你想要解决的任何网络抓取项目的第一步。你需要了解站点结构才能提取与你相关的信息。首先使用你喜欢的浏览器打开你想要抓取的网站

浏览网站

单击该站点并与其进行交互,就像任何典型的求职者一样。例如,你可以滚动浏览网站的主页:
Beautiful Soup:如何用Python构建一个网页爬虫?
你可以看到许多卡片格式的招聘信息,每个招聘信息都有两个按钮。如果单击应用,你将看到一个新页面,其中包含所选工作的更详细说明。你可能还会注意到,当你与网站交互时,浏览器地址栏中的 URL 会发生变化。

破译 URL 中的信息

程序员可以在 URL 中编码大量信息。如果你首先熟悉 URL 的工作原理以及它们的构成,你的网络抓取之旅将会容易得多。例如,你可能会发现自己位于具有以下 URL 的详细信息页面上:
https://realpython.github.io/fake-jobs/jobs/senior-python-developer-0.html
你可以将上述 URL 解构为两个主要部分:
  1. 基本 URL表示网站搜索功能的路径。在上面的示例中,基本 URL 是https://realpython.github.io/fake-jobs/.
  2. 以 结尾的特定站点位置.html是职位描述唯一资源的路径。
本网站上发布的任何职位都将使用相同的基本 URL。但是,独特资源的位置会有所不同,具体取决于你正在查看的具体职位发布。 URL 可以包含更多信息,而不仅仅是文件的位置。某些网站使用查询参数对你在执行搜索时提交的值进行编码。你可以将它们视为发送到数据库以检索特定记录的查询字符串。 你将在 URL 末尾找到查询参数。例如,如果你转到Indeed并通过其搜索栏在“澳大利亚”中搜索“软件开发人员”,你将看到 URL 更改为包含这些值作为查询参数:
https://au.indeed.com/jobs?q=software+developer&l=Australia
此 URL 中的查询参数为?q=software+developer&l=Australia. 查询参数由三部分组成:
  1. 开始:查询参数的开头用问号 ( ?) 表示。
  2. 信息:构成一个查询参数的信息片段被编码为键值对,其中相关的键和值通过等号 ( key=value)连接在一起。
  3. 分隔符:每个 URL 可以有多个查询参数,由与符号 ( &)分隔。
有了这些信息,你就可以将 URL 的查询参数分成两个键值对:
  1. q=software+developer 选择作业类型。
  2. l=Australia 选择作业的位置。
尝试更改搜索参数并观察它如何影响你的 URL。继续并在顶部的搜索栏中输入新值:
Beautiful Soup:如何用Python构建一个网页爬虫?
更改这些值以观察 URL 中的更改。
接下来,尝试直接更改 URL 中的值。看看将以下 URL 粘贴到浏览器地址栏中会发生什么:
https://au.indeed.com/jobs?q=developer&l=perth
如果你更改并提交网站搜索框中的值,那么它将直接反映在 URL 的查询参数中,反之亦然。如果你更改其中任何一个,那么你将在网站上看到不同的结果。 如你所见,浏览网站的 URL 可以让你深入了解如何从网站的服务器检索数据。 回到Fake Python Jobs并继续探索它。该站点是一个纯静态网站,不在数据库之上运行,这就是为什么你不必在本抓取教程中使用查询参数的原因。

如何构建一个网页爬虫?使用开发人员工具检查站点

接下来,你将需要了解更多有关如何构建数据以进行显示的信息。你需要了解页面结构,才能从接下来的步骤之一中收集的 HTML 响应中选择你想要的内容。 开发人员工具可以帮助你了解网站的结构。所有现代浏览器都安装了开发人员工具。在本节中,你将了解如何使用 Chrome 中的开发人员工具。该过程将与其他现代浏览器非常相似。 Beautiful Soup网络爬虫示例:在 macOS 上的 Chrome 中,你可以通过选择View → Developer → Developer Tools通过菜单打开开发者工具。在 Windows 和 Linux 上,你可以通过单击右上角的菜单按钮 ( ) 并选择更多工具开发人员工具来访问它们。你还可以通过右键单击页面并选择“检查”选项或使用键盘快捷键来访问你的开发人员工具:
  • 苹果: CmdAlt+I
  • Windows/Linux: Ctrl+Shift+I
开发人员工具允许你以交互方式探索站点的文档对象模型 (DOM)以更好地了解你的来源。要深入了解页面的 DOM,请在开发人员工具中选择Elements选项卡。你将看到一个包含可点击 HTML 元素的结构。你可以直接在浏览器中展开、折叠甚至编辑元素:
Beautiful Soup:如何用Python构建一个网页爬虫?
右侧的 HTML 表示你可以在左侧看到的页面结构。
你可以将浏览器中显示的文本视为该页面的 HTML 结构。如果你有兴趣,那么你可以在CSS-TRICKS上阅读有关 DOM 和 HTML 之间差异的更多信息。 当你右键单击页面上的元素时,你可以选择“检查”以缩放到它们在 DOM 中的位置。你还可以将鼠标悬停在右侧的 HTML 文本上,然后查看页面上的相应元素亮起。 单击以展开特定任务的练习块以练习使用你的开发人员工具: 练习:探索 HTML显示隐藏 四处玩耍和探索!你对正在使用的页面了解得越多,抓取它就越容易。但是,不要被所有的 HTML 文本弄得不知所措。你将利用编程的力量逐步穿越这个迷宫并精心挑选与你相关的信息。

第 2 步:从页面中抓取 HTML 内容

Python Beautiful Soup构建网页爬虫?现在你已经了解了你正在使用的内容,现在是开始使用 Python 的时候了。首先,你需要将站点的 HTML 代码放入你的 Python 脚本中,以便你可以与其进行交互。对于此任务,你将使用 Python 的requests库。 在安装任何外部包之前,为你的项目创建一个虚拟环境。激活新的虚拟环境,然后在终端中键入以下命令以安装外部requests库:
$ python -m pip install requests
然后在你喜欢的文本编辑器中打开一个新文件。检索 HTML 所需的只是几行代码:
import requests

URL = "https://realpython.github.io/fake-jobs/"
page = requests.get(URL)

print(page.text)
此代码向给定的 URL发出HTTPGET请求。它检索服务器发回的 HTML 数据并将该数据存储在 Python 对象中。 如果你打印的.text属性page,那么你会注意到它看起来就像你之前使用浏览器的开发人员工具检查过的 HTML。你已成功从 Internet 获取静态站点内容!你现在可以从 Python 脚本中访问站点的 HTML。

Beautiful Soup网络爬虫示例:静态网站

你在本教程中抓取的网站提供静态 HTML 内容。在这种情况下,托管站点的服务器发回 HTML 文档,这些 HTML 文档已经包含你作为用户可以看到的所有数据。 当你之前使用开发人员工具检查页面时,你发现招聘信息由以下长而杂乱的 HTML 组成:
<div class="card">
  <div class="card-content">
    <div class="media">
      <div class="media-left">
        <figure class="image is-48x48">
          <img
            src="https://files.realpython.com/media/real-python-logo-thumbnail.7f0db70c2ed2.jpg" alt="Beautiful Soup:如何用Python构建一个网页爬虫?"
            alt="Real Python Logo"
          />
        </figure>
      </div>
      <div class="media-content">
        <h2 class="title is-5">Senior Python Developer</h2>
        <h3 class="subtitle is-6 company">Payne, Roberts and Davis</h3>
      </div>
    </div>

    <div class="content">
      <p class="location">Stewartbury, AA</p>
      <p class="is-small has-text-grey">
        <time datetime="2021-04-08">2021-04-08</time>
      </p>
    </div>
    <footer class="card-footer">
      <a
        href="https://www.realpython.com"
        target="_blank"
        class="card-footer-item"
        >Learn</a
      >
      <a
        href="https://realpython.github.io/fake-jobs/jobs/senior-python-developer-0.html"
        target="_blank"
        class="card-footer-item"
        >Apply</a
      >
    </footer>
  </div>
</div>
将你的头包裹在很长的 HTML 代码块中可能具有挑战性。为了更容易阅读,你可以使用HTML 格式化程序来自动清理它。良好的可读性有助于你更好地理解任何代码块的结构。虽然它可能有助于也可能不会帮助改进 HTML 格式,但它总是值得一试。 注意:请记住,每个网站的外观都不同。这就是为什么在继续之前有必要检查和了解你当前正在使用的站点的结构的原因。 你将遇到的 HTML 有时会令人困惑。幸运的是,此工作板的 HTML 对你感兴趣的元素具有描述性类名称
  • class="title is-5" 包含职位发布的标题。
  • class="subtitle is-6 company" 包含提供该职位的公司名称。
  • class="location" 包含你将工作的位置。
如果你在一大堆 HTML 中迷失了方向,请记住,你始终可以返回浏览器并使用开发人员工具以交互方式进一步探索 HTML 结构。 到目前为止,你已经成功地利用了 Pythonrequests库的强大功能和用户友好设计。仅用几行代码,你就成功地从 Web 中抓取了静态 HTML 内容并使其可用于进一步处理。 但是,在抓取网站时可能会遇到更具挑战性的情况。在你学习如何从刚刚抓取的 HTML 中挑选相关信息之前,你将快速了解其中两个更具挑战性的情况。

隐藏网站

某些页面包含隐藏在登录名后面的信息。这意味着你需要一个帐户才能从页面上抓取任何内容。从 Python 脚本发出 HTTP 请求的过程与从浏览器访问页面的方式不同。仅仅因为你可以通过浏览器登录页面并不意味着你可以使用 Python 脚本抓取它。 但是,该requests库具有处理身份验证的内置能力。使用这些技术,你可以在从 Python 脚本发出 HTTP 请求时登录网站,然后抓取隐藏在登录名后面的信息。你无需登录即可访问工作板信息,这就是本教程不涉及身份验证的原因。

动态网站

Beautiful Soup如何构建网络爬虫?在本教程中,你将学习如何抓取静态网站。静态站点易于使用,因为服务器会向你发送一个 HTML 页面,该页面已包含响应中的所有页面信息。你可以解析该 HTML 响应并立即开始挑选相关数据。 另一方面,对于动态网站,服务器可能根本不会发回任何 HTML。相反,你可以接收JavaScript代码作为响应。此代码看起来与你使用浏览器的开发人员工具检查页面时看到的完全不同。 注意:在本教程中,术语动态网站是指不返回你在浏览器中查看页面时看到的相同 HTML 的网站。 许多现代 Web 应用程序旨在与客户端的浏览器协作提供其功能。这些应用程序不会发送 HTML 页面,而是发送JavaScript代码,指示你的浏览器创建所需的 HTML。Web 应用程序以这种方式提供动态内容,以将工作从服务器卸载到客户端的计算机,并避免页面重新加载并改善整体用户体验。 浏览器中发生的事情与脚本中发生的事情不同。你的浏览器会认真执行它从服务器接收到的 JavaScript 代码,并在本地为你创建 DOM 和 HTML。但是,如果你在 Python 脚本中请求动态网站,那么你将无法获得 HTML 页面内容。 当你使用 时requests,你只会收到服务器发回的内容。对于动态网站,你最终会得到一些 JavaScript 代码而不是 HTML。从你收到的 JavaScript 代码转到你感兴趣的内容的唯一方法是执行代码,就像你的浏览器一样。该requests库不能为你做的,但也有其他的解决方案,可以。 例如,requests-html是由requests库的作者创建的一个项目,它允许你使用类似于requests. 它还包括通过在后台使用Beautiful Soup来解析数据的功能。 注意:另一个用于抓取动态内容的流行选择是Selenium。你可以将 Selenium 视为一个精简的浏览器,在将呈现的 HTML 响应传递给你的脚本之前,它会为你执行 JavaScript 代码。 在本教程中,你不会更深入地抓取动态生成的内容。现在,如果你需要抓取动态网站,只需记住查看上述选项之一就足够了。

第 3 步:使用 Beautiful Soup 解析 HTML 代码

Beautiful Soup网络爬虫示例:你已经成功地从 Internet 上抓取了一些 HTML,但是当你查看它时,它似乎一团糟。到处都有成吨的 HTML 元素,散布着成千上万的属性——难道还混入了一些 JavaScript?是时候在 Python 的帮助下解析这个冗长的代码响应,使其更易于访问并挑选出你想要的数据。 Beautiful Soup是一个用于解析结构化数据的 Python 库。它允许你以类似于使用开发人员工具与网页交互的方式与 HTML 交互。该库公开了一些直观的功能,你可以使用它们来探索你收到的 HTML。首先,使用你的终端安装 Beautiful Soup:
$ python -m pip install beautifulsoup4
然后,在你的 Python 脚本中导入库并创建一个 Beautiful Soup 对象:
import requests
from bs4 import BeautifulSoup

URL = "https://realpython.github.io/fake-jobs/"
page = requests.get(URL)

soup = BeautifulSoup(page.content, "html.parser")
添加突出显示的两行代码后,你将创建一个 Beautiful Soup 对象page.content,该对象将,即你之前抓取的 HTML 内容,作为其输入。 注意:你将希望通过page.content而不是page.text避免字符编码问题。该.content属性保存原始字节,可以比你之前使用该.text属性打印的文本表示更好地解码。 第二个参数"html.parser"确保你对 HTML 内容使用适当的解析器

按 ID 查找元素

在 HTML 网页中,每个元素都可以id分配一个属性。顾名思义,该id属性使元素在页面上唯一可识别。你可以通过按 ID 选择特定元素来开始解析你的页面。 切换回开发人员工具并确定包含所有职位发布的 HTML 对象。通过将鼠标悬停在页面的部分上并使用右键单击Inspect来探索。 注意:定期切换回浏览器并使用开发人员工具交互式浏览页面会有所帮助。这有助于你了解如何找到你正在寻找的确切元素。 你要查找的元素是具有值为<div>id属性"ResultsContainer"。它还有一些其他属性,但以下是你要查找的内容的要点:
<div id="ResultsContainer">
  <!-- all the job listings -->
</div>
Beautiful Soup 允许你通过 ID 查找特定的 HTML 元素:
results = soup.find(id="ResultsContainer")
为了更容易查看,你可以在打印时美化任何 Beautiful Soup 对象。如果你调用上面刚刚分配.prettify()results变量,那么你将看到包含在以下内容中的所有 HTML <div>
print(results.prettify())
当你使用元素的 ID 时,你可以从 HTML 的其余部分中挑选一个元素。现在,你只能使用页面 HTML 的这一特定部分。汤好像变稀了!然而,它仍然非常密集。

按 HTML 类名查找元素

你已经看到每个职位发布都包含在一个<div>带有 class的元素中card-content。现在,你可以使用新对象调用results并仅选择其中的职位发布。毕竟,这些是你感兴趣的 HTML 部分!你可以在一行代码中执行此操作:
job_elements = results.find_all("div", class_="card-content")
在这里,你调用.find_all()一个 Beautiful Soup 对象,它返回一个包含该页面上显示的所有工作列表的所有 HTML的可迭代对象。 看看所有这些:
for job_element in job_elements:
    print(job_element, end="\n"*2)
这已经很整洁了,但还有很多 HTML!你之前看到你的页面在某些元素上具有描述性的类名。你可以使用.find()以下命令从每个职位发布中挑选出这些子元素:
for job_element in job_elements:
    title_element = job_element.find("h2", class_="title")
    company_element = job_element.find("h3", class_="company")
    location_element = job_element.find("p", class_="location")
    print(title_element)
    print(company_element)
    print(location_element)
    print()
每个job_element都是另一个BeautifulSoup()对象。因此,你可以对其使用与其父元素相同的方法,results. 使用此代码片段,你会越来越接近你真正感兴趣的数据。 尽管如此,所有这些 HTML 标记和属性仍然存在很多问题:
<h2 class="title is-5">Senior Python Developer</h2>
<h3 class="subtitle is-6 company">Payne, Roberts and Davis</h3>
<p class="location">Stewartbury, AA</p>
接下来,你将学习如何缩小此输出范围以仅访问你感兴趣的文本内容。

Beautiful Soup网络爬虫示例:从 HTML 元素中提取文本

你只想查看每个职位发布的标题、公司和地点。看哪!Beautiful Soup 已满足你的需求。你可以添加.text到 Beautiful Soup 对象以仅返回该对象包含的 HTML 元素的文本内容
for job_element in job_elements:
    title_element = job_element.find("h2", class_="title")
    company_element = job_element.find("h3", class_="company")
    location_element = job_element.find("p", class_="location")
    print(title_element.text)
    print(company_element.text)
    print(location_element.text)
    print()
运行上面的代码片段,你将看到显示的每个元素的文本。但是,你也可能会得到一些额外的whitespace。由于你现在正在使用Python 字符串,因此你可以.strip()使用多余的空格。你还可以应用任何其他熟悉的 Python 字符串方法来进一步清理你的文本:
for job_element in job_elements:
    title_element = job_element.find("h2", class_="title")
    company_element = job_element.find("h3", class_="company")
    location_element = job_element.find("p", class_="location")
    print(title_element.text.strip())
    print(company_element.text.strip())
    print(location_element.text.strip())
    print()
结果最终看起来好多了:
Senior Python Developer
Payne, Roberts and Davis
Stewartbury, AA

Energy engineer
Vasquez-Davidson
Christopherville, AA

Legal executive
Jackson, Chambers and Levy
Port Ericaburgh, AA
这是一个可读的工作列表,其中还包括公司名称和每个工作的位置。但是,你正在寻找软件开发人员的职位,这些结果还包含许多其他领域的招聘信息。

按类名和文本内容查找元素

如何构建一个网页爬虫?并非所有职位列表都是开发人员职位。你将首先使用关键字过滤它们,而不是打印出网站上列出的所有工作。 你知道页面中的职位名称保存在<h2>元素中。要仅过滤特定作业,你可以使用string参数
python_jobs = results.find_all("h2", string="Python")
此代码查找<h2>包含的字符串"Python"完全匹配的所有元素。请注意,你是直接在第一个results变量上调用该方法。如果你继续print()将上面的代码片段输出到你的控制台,那么你可能会感到失望,因为它是空的:>>>
>>> print(python_jobs)
[]
还有就是在搜索结果中一个Python的工作,所以为什么不显示出来? 当你string=像上面那样使用时,你的程序会准确地查找该字符串。拼写、大写或空格的任何差异都会阻止元素匹配。在下一节中,你将找到一种使搜索字符串更通用的方法。

将函数传递给Beautiful Soup方法

Python Beautiful Soup构建网页爬虫?除了字符串之外,你有时还可以将函数作为参数传递给 Beautiful Soup 方法。你可以更改前一行代码以使用函数:
python_jobs = results.find_all(
    "h2", string=lambda text: "python" in text.lower()
)
现在你将匿名函数传递给string=参数。该lambda函数看起来在每个文本<h2>元素,将其转换为小写,并检查是否有子"python"是随处可见。你可以检查是否使用这种方法识别了所有 Python 作业:>>>
>>> print(len(python_jobs))
10
你的程序找到10"python"在其职位名称中包含该词的匹配职位! 根据文本内容查找元素是过滤 HTML 响应以获取特定信息的有效方法。Beautiful Soup 允许你使用精确的字符串或函数作为参数来过滤 Beautiful Soup 对象中的文本。 但是,当你尝试运行刮板以打印过滤后的 Python 作业的信息时,你将遇到错误:
AttributeError: 'NoneType' object has no attribute 'text'
此消息是你在从 Internet 抓取信息时经常遇到的常见错误。检查python_jobs列表中元素的 HTML 。它是什么样子的?你认为错误来自哪里?

Beautiful Soup如何构建网络爬虫?识别错误情况

当你查看 中的单个元素时python_jobs,你会发现它仅<h2>包含包含职位的元素:
<h2 class="title is-5">Senior Python Developer</h2>
当你重新访问用于选择项目的代码时,你会看到这就是你的目标。你只筛选了<h2>包含单词 的职位发布的标题元素"python"。如你所见,这些元素不包括有关作业的其余信息。 你之前收到的错误消息与此有关:
AttributeError: 'NoneType' object has no attribute 'text'
你试图在 中的每个元素中查找职位名称、公司名称和职位位置python_jobs,但每个元素仅包含职位名称文本。 你的勤奋解析库仍然会寻找其他的,但None由于找不到它们而返回。然后,print()当你尝试.text从这些None对象之一中提取属性时,失败并显示错误消息。 你要查找的文本嵌套在<h2>过滤器返回的元素的同级元素中。Beautiful Soup 可以帮助你选择每个 Beautiful Soup 对象的兄弟元素、子元素和父元素。

访问父元素

访问你需要的所有信息的一种方法是从<h2>你识别的元素开始,逐步进入 DOM 的层次结构。再看一下单个职位发布的 HTML。查找<h2>包含职位的元素及其最近的包含你感兴趣的所有信息的父元素:
<div class="card">
  <div class="card-content">
    <div class="media">
      <div class="media-left">
        <figure class="image is-48x48">
          <img
            src="https://files.realpython.com/media/real-python-logo-thumbnail.7f0db70c2ed2.jpg" alt="Beautiful Soup:如何用Python构建一个网页爬虫?"
            alt="Real Python Logo"
          />
        </figure>
      </div>
      <div class="media-content">
        <h2 class="title is-5">Senior Python Developer</h2>
        <h3 class="subtitle is-6 company">Payne, Roberts and Davis</h3>
      </div>
    </div>

    <div class="content">
      <p class="location">Stewartbury, AA</p>
      <p class="is-small has-text-grey">
        <time datetime="2021-04-08">2021-04-08</time>
      </p>
    </div>
    <footer class="card-footer">
      <a
        href="https://www.realpython.com"
        target="_blank"
        class="card-footer-item"
        >Learn</a
      >
      <a
        href="https://realpython.github.io/fake-jobs/jobs/senior-python-developer-0.html"
        target="_blank"
        class="card-footer-item"
        >Apply</a
      >
    </footer>
  </div>
</div>
<div>带有card-content类的元素包含你想要的所有信息。它<h2>是你使用过滤器找到的title 元素的第三级父级。 Beautiful Soup网络爬虫示例:考虑到这些信息,你现在可以使用 中的元素python_jobs并获取它们的曾祖父元素来访问你想要的所有信息:
python_jobs = results.find_all(
    "h2", string=lambda text: "python" in text.lower()
)

python_job_elements = [
    h2_element.parent.parent.parent for h2_element in python_jobs
]
你添加了一个列表推导式,它对你通过 lambda 表达式过滤获得的每个<h2>标题元素进行操作python_jobs。你正在选择每个<h2>标题元素的父元素的父元素的父元素。这已经是三代了! 当你查看单个职位发布的 HTML 时,你发现这个具有类名的特定父元素card-content包含你需要的所有信息。 现在,你可以修改for循环中的代码以迭代父元素:
for job_element in python_job_elements:
    # -- snip --
当你再次运行脚本时,你会看到你的代码再次可以访问所有相关信息。那是因为你现在循环的是<div class="card-content">元素,而不仅仅是<h2>标题元素。 使用.parent每个 Beautiful Soup 对象附带的属性,你可以直观地浏览 DOM 结构并处理所需的元素。你还可以以类似的方式访问子元素和同级元素。阅读导航树以获取更多信息。

从 HTML 元素中提取属性

此时,你的 Python 脚本已经抓取了该站点并过滤了其 HTML 以查找相关职位发布。做得好!但是,仍然缺少申请工作的链接。 在检查页面时,你会在每张卡片的底部发现两个链接。如果你以与处理其他元素相同的方式处理链接元素,你将不会获得你感兴趣的 URL:
for job_element in python_job_elements:
    # -- snip --
    links = job_element.find_all("a")
    for link in links:
        print(link.text.strip())
如果你运行此代码片段,那么你将获得链接文本LearnApply不是关联的 URL。 这是因为该.text属性只留下 HTML 元素的可见内容。它去除了所有 HTML 标签,包括包含 URL 的 HTML 属性,只留下链接文本。要改为获取 URL,你需要提取 HTML 属性之一的值而不是丢弃它。 链接元素的 URL 与href属性相关联。你要查找的特定 URL是单个职位发布的 HTML 底部href第二个<a>标签的属性值:
    <!-- snip -->
    <footer class="card-footer">
        <a href="https://www.realpython.com" target="_blank"
           class="card-footer-item">Learn</a>
        <a href="https://realpython.github.io/fake-jobs/jobs/senior-python-developer-0.html"
           target="_blank"
           class="card-footer-item">Apply</a>
    </footer>
  </div>
</div>
Beautiful Soup网络爬虫示例:首先获取<a>工作卡中的所有元素。然后,href使用方括号表示法提取它们的属性值:
for job_element in python_job_elements:
    # -- snip --
    links = job_element.find_all("a")
    for link in links:
        link_url = link["href"]
        print(f"Apply here: {link_url}\n")
在此代码段中,你首先从每个过滤后的职位发布中获取所有链接。然后你提取href包含 URL的属性,使用["href"]并将其打印到你的控制台。 在下面的练习块中,你可以找到挑战的说明以优化你收到的链接结果: 练习:优化你的结果显示隐藏 单击解决方案块以阅读本练习的可能解决方案: 解决方案:优化你的结果显示隐藏 你也可以使用相同的方括号表示法来提取其他 HTML 属性

保持练习

Beautiful Soup如何构建网络爬虫?如果你在本教程旁边编写了代码,那么你可以按原样运行脚本,你将在终端中看到虚假的工作信息弹出。你的下一步是处理现实生活中的工作委员会!要继续练习你的新技能,请使用以下任何或所有站点重新访问网络抓取过程: 链接的网站将其搜索结果作为静态 HTML 响应返回,类似于 Fake Python 工作板。因此,你可以仅使用requestsBeautiful Soup来刮掉它们。 使用这些其他站点之一从顶部重新开始阅读本教程。你会看到每个网站的结构都不同,你需要以稍微不同的方式重新构建代码以获取所需的数据。应对这一挑战是练习刚刚学到的概念的好方法。虽然它可能会让你经常出汗,但你的编码技能会因此而更强! 在第二次尝试期间,你还可以探索 Beautiful Soup 的其他功能。使用文档作为你的指南和灵感。额外的练习将帮助你更熟练地使用 Python、requests.. 和 Beautiful Soup进行网页抓取。 为了结束你的网络抓取之旅,你可以对代码进行最终改造并创建一个命令行界面 (CLI)应用程序,该应用程序可以抓取一个工作板并通过你可以在每次执行时输入的关键字过滤结果. 你的 CLI 工具可以让你搜索特定类型的工作或特定位置的工作。 如果你有兴趣学习如何将脚本改编为命令行界面,请查看如何使用 argparse 在 Python 中构建命令行界面。

如何构建一个网页爬虫?结论

requests库为你提供了一种用户友好的方式来使用 Python 从 Internet 获取静态 HTML。然后,你可以使用另一个名为 Beautiful Soup 的包解析 HTML。这两个软件包都是你的网络抓取冒险值得信赖和有用的伴侣。你会发现 Beautiful Soup 将满足你的大部分解析需求,包括导航高级搜索。 Python Beautiful Soup构建网页爬虫?在本教程中,你学习了如何使用 Python requests、 和 Beautiful Soup从 Web 抓取数据。你构建了一个从 Internet 获取职位发布的脚本,并从头到尾完成了完整的网络抓取过程。 你学会了如何:
  • 通过一个步骤网页抓取管道从开始到结束
  • 使用浏览器的开发工具检查目标站点的HTML 结构
  • 解密URL 中编码的数据
  • 使用 Python 的下载页面的HTML 内容requests
  • Beautiful Soup解析下载的 HTML提取相关信息
  • 构建一个从 Web 获取工作机会并在你的控制台中显示相关信息的脚本
考虑到这个广泛的管道和工具包中的两个强大的库,你可以出去看看还有哪些其他网站可以抓取。玩得开心,永远记住要尊重并负责任地使用你的编程技能。
木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: