Beautiful Soup:用 Python 构建一个网页爬虫
Table of Contents
Internet 上数量惊人的数据是任何研究领域或个人兴趣的丰富资源。为了有效地收集这些数据,您需要熟练掌握网络抓取。Python 库requests
和 Beautiful Soup 是完成这项工作的强大工具。如果您喜欢通过动手示例学习并且对 Python 和 HTML 有基本的了解,那么本教程适合您。
在本教程中,您将学习如何:
- 使用浏览器的开发工具检查目标站点的HTML 结构
- 解密URL 中编码的数据
- 使用
requests
and Beautiful Soup从网络上抓取和解析数据 - 通过一个步骤网页抓取管道从开始到结束
- 构建一个从 Web 获取工作机会并在您的控制台中显示相关信息的脚本
完成这个项目将使您了解在万维网上抓取任何静态网站所需的过程和工具。您可以点击以下链接下载项目源代码:
什么是网页抓取?
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 代码之前,您需要了解要抓取的网站。这应该是您想要解决的任何网络抓取项目的第一步。您需要了解站点结构才能提取与您相关的信息。首先使用您喜欢的浏览器打开您想要抓取的网站。
浏览网站
单击该站点并与其进行交互,就像任何典型的求职者一样。例如,您可以滚动浏览网站的主页:
您可以看到许多卡片格式的招聘信息,每个招聘信息都有两个按钮。如果单击应用,您将看到一个新页面,其中包含所选工作的更详细说明。您可能还会注意到,当您与网站交互时,浏览器地址栏中的 URL 会发生变化。
破译 URL 中的信息
程序员可以在 URL 中编码大量信息。如果您首先熟悉 URL 的工作原理以及它们的构成,您的网络抓取之旅将会容易得多。例如,您可能会发现自己位于具有以下 URL 的详细信息页面上:
https://realpython.github.io/fake-jobs/jobs/senior-python-developer-0.html
您可以将上述 URL 解构为两个主要部分:
- 基本 URL表示网站搜索功能的路径。在上面的示例中,基本 URL 是
https://realpython.github.io/fake-jobs/
. - 以 结尾的特定站点位置
.html
是职位描述唯一资源的路径。
本网站上发布的任何职位都将使用相同的基本 URL。但是,独特资源的位置会有所不同,具体取决于您正在查看的具体职位发布。
URL 可以包含更多信息,而不仅仅是文件的位置。某些网站使用查询参数对您在执行搜索时提交的值进行编码。您可以将它们视为发送到数据库以检索特定记录的查询字符串。
您将在 URL 末尾找到查询参数。例如,如果您转到Indeed并通过他们的搜索栏在“澳大利亚”中搜索“软件开发人员”,您将看到 URL 更改为包含这些值作为查询参数:
https://au.indeed.com/jobs?q=software+developer&l=Australia
此 URL 中的查询参数为?q=software+developer&l=Australia
. 查询参数由三部分组成:
- 开始:查询参数的开头用问号 (
?
) 表示。 - 信息:构成一个查询参数的信息片段被编码为键值对,其中相关的键和值通过等号 (
key=value
)连接在一起。 - 分隔符:每个 URL 可以有多个查询参数,由与符号 (
&
)分隔。
有了这些信息,您就可以将 URL 的查询参数分成两个键值对:
q=software+developer
选择作业类型。l=Australia
选择作业的位置。
尝试更改搜索参数并观察它如何影响您的 URL。继续并在顶部的搜索栏中输入新值:
接下来,尝试直接更改 URL 中的值。看看将以下 URL 粘贴到浏览器地址栏中会发生什么:
https://au.indeed.com/jobs?q=developer&l=perth
如果您更改并提交网站搜索框中的值,那么它将直接反映在 URL 的查询参数中,反之亦然。如果您更改其中任何一个,那么您将在网站上看到不同的结果。
如您所见,浏览网站的 URL 可以让您深入了解如何从网站的服务器检索数据。
回到Fake Python Jobs并继续探索它。该站点是一个纯静态网站,不在数据库之上运行,这就是为什么您不必在本抓取教程中使用查询参数的原因。
使用开发人员工具检查站点
接下来,您需要详细了解数据的结构以进行显示。您需要了解页面结构,才能从接下来的步骤之一中收集的 HTML 响应中选择您想要的内容。
开发人员工具可以帮助您了解网站的结构。所有现代浏览器都安装了开发人员工具。在本节中,您将了解如何使用 Chrome 中的开发人员工具。该过程将与其他现代浏览器非常相似。
在 macOS 上的 Chrome 中,您可以通过选择View → Developer → Developer Tools通过菜单打开开发者工具。在 Windows 和 Linux 上,您可以通过单击右上角的菜单按钮 ( ⋮
) 并选择更多工具→开发人员工具来访问它们。您还可以通过右键单击页面并选择“检查”选项或使用键盘快捷键来访问您的开发人员工具:
- 苹果: Cmd+ Alt+I
- Windows/Linux: Ctrl+Shift+I
开发人员工具允许您以交互方式探索站点的文档对象模型 (DOM)以更好地了解您的来源。要深入了解页面的 DOM,请在开发人员工具中选择Elements选项卡。您将看到一个包含可点击 HTML 元素的结构。您可以直接在浏览器中展开、折叠甚至编辑元素:
您可以将浏览器中显示的文本视为该页面的 HTML 结构。如果您有兴趣,那么您可以在CSS-TRICKS上阅读有关 DOM 和 HTML 之间差异的更多信息。
当您右键单击页面上的元素时,您可以选择“检查”以缩放到它们在 DOM 中的位置。您还可以将鼠标悬停在右侧的 HTML 文本上,然后查看页面上的相应元素亮起。
单击以展开特定任务的练习块以练习使用您的开发人员工具:
练习:探索 HTML显示隐藏
四处玩耍和探索!您对正在使用的页面了解得越多,抓取它就越容易。但是,不要被所有的 HTML 文本弄得不知所措。您将利用编程的力量逐步穿越这个迷宫并精心挑选与您相关的信息。
第 2 步:从页面中抓取 HTML 内容
现在您已经了解了您正在使用的内容,现在是开始使用 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。
静态网站
您在本教程中抓取的网站提供静态 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="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 请求时登录网站,然后抓取隐藏在登录名后面的信息。您无需登录即可访问工作板信息,这就是本教程不涉及身份验证的原因。
动态网站
在本教程中,您将学习如何抓取静态网站。静态站点易于使用,因为服务器会向您发送一个 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 代码
您已经成功地从 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>
接下来,您将学习如何缩小此输出范围以仅访问您感兴趣的文本内容。
从 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_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 。它是什么样子的?你认为错误来自哪里?
识别错误情况
当您查看 中的单个元素时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="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 元素的第三级父级。
考虑到这些信息,您现在可以使用 中的元素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())
如果您运行此代码片段,那么您将获得链接文本Learn
而Apply
不是关联的 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>
首先获取<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"]
并将其打印到您的控制台。
在此代码段中,您首先从每个过滤后的职位发布中获取所有链接。然后您提取href
包含 URL的属性,使用["href"]
并将其打印到您的控制台。
在下面的练习块中,您可以找到挑战的说明以优化您收到的链接结果:
练习:优化你的结果显示隐藏
单击解决方案块以阅读本练习的可能解决方案:
解决方案:优化您的结果显示隐藏
您也可以使用相同的方括号表示法来提取其他 HTML 属性。
保持练习
如果您在本教程中编写了代码,那么您可以按原样运行脚本,您将在终端中看到虚假的工作信息弹出。您的下一步是处理现实生活中的工作委员会!要继续练习您的新技能,请使用以下任何或所有站点重新访问网络抓取过程:
链接的网站将其搜索结果作为静态 HTML 响应返回,类似于 Fake Python 工作板。因此,您可以仅使用requests
美丽的汤来刮掉它们。
使用这些其他站点之一从顶部重新开始阅读本教程。您会看到每个网站的结构都不同,您需要以稍微不同的方式重新构建代码以获取所需的数据。应对这一挑战是练习刚刚学到的概念的好方法。虽然它可能会让你经常出汗,但你的编码技能会因此而更强!
在您第二次尝试时,您还可以探索 Beautiful Soup 的其他功能。使用文档作为您的指南和灵感。额外的练习将帮助您更熟练地使用 Python、requests
.. 和 Beautiful Soup进行网页抓取。
为了结束您的网络抓取之旅,您可以对代码进行最终改造并创建一个命令行界面 (CLI)应用程序,该应用程序可以抓取一个工作板并通过您可以在每次执行时输入的关键字过滤结果. 您的 CLI 工具可以让您搜索特定类型的工作或特定位置的工作。
如果您有兴趣学习如何将脚本调整为命令行界面,请查看如何使用 argparse 在 Python 中构建命令行界面。
结论
该requests
库为您提供了一种用户友好的方式来使用 Python 从 Internet 获取静态 HTML。然后,您可以使用另一个名为 Beautiful Soup 的包解析 HTML。这两个软件包都是您的网络抓取冒险值得信赖和有用的伴侣。您会发现 Beautiful Soup 将满足您的大部分解析需求,包括导航和高级搜索。
在本教程中,您学习了如何使用 Python requests
、 和 Beautiful Soup从 Web 抓取数据。您构建了一个从 Internet 获取职位发布的脚本,并从头到尾完成了完整的网络抓取过程。
你学会了如何:
- 通过一个步骤网页抓取管道从开始到结束
- 使用浏览器的开发工具检查目标站点的HTML 结构
- 解密URL 中编码的数据
- 使用 Python 的库下载页面的HTML 内容
requests
- 用Beautiful Soup解析下载的 HTML提取相关信息
- 构建一个从 Web 获取工作机会并在您的控制台中显示相关信息的脚本
考虑到这个广泛的管道和工具包中的两个强大的库,您可以出去看看还有哪些其他网站可以抓取。玩得开心,永远记住要尊重并负责任地使用你的编程技能。
- 点赞
- 收藏
- 关注作者
评论(0)