互联网上令人难以置信的数据量是任何研究领域或个人兴趣的丰富资源。为了有效地收集这些数据,您需要熟练掌握网络抓取。Python 库和 Beautiful Soup 是完成这项工作的强大工具。
了解:
网页抓取是从互联网上收集信息的过程。甚至复制和粘贴您最喜欢的歌曲的歌词也是一种网络抓取形式!但是,“网络抓取”一词通常是指涉及自动化的过程。一些网站不喜欢自动抓取工具收集数据,而另一些网站则不介意。
如果出于教育目的恭敬地抓取页面,那么您不太可能遇到任何问题。不过,在开始大型项目之前,最好自己做一些研究,并确保自己没有违反任何服务条款。
网页抓取的原因假设您是一名冲浪者,无论是在网上还是在现实生活中,您正在寻找工作。但是,您不是在找任何工作。以冲浪者的心态,您正在等待一个完美的机会来滚动!
有一个工作网站可以准确地提供您想要的工作类型。不幸的是,一个新职位在蓝月亮中只出现一次,而且该网站不提供电子邮件通知服务。你每天都想检查一下,但这听起来不像是消磨时间的最有趣和最有成效的方式。
值得庆幸的是,世界提供了其他方法来应用冲浪者的心态!与其每天查看工作网站,不如使用 Python 来帮助自动化求职的重复部分。自动网络抓取可以成为加快数据收集过程的解决方案。你只写一次代码,它就会从许多页面中多次获得你想要的信息。
相反,当您尝试手动获取所需的信息时,您可能会花费大量时间单击、滚动和搜索,尤其是当您需要来自定期更新新内容的网站的大量数据时。手动抓取网页可能需要大量时间和重复。
网络上的信息太多了,而且新的信息也在不断增加。你可能至少会对其中的一些数据感兴趣,其中大部分只是在那里。无论您是真的在找工作,还是想下载您最喜欢的艺术家的所有歌词,自动网络抓取都可以帮助您实现目标。
网页抓取的挑战网络已经从许多来源有机地发展起来。它结合了许多不同的技术、风格和个性,并一直发展到今天。换句话说,网络是一团糟!因此,您在抓取 Web 时会遇到一些挑战:
网页抓取的替代方案:API一些网站提供商提供应用程序编程接口 (API),允许您以预定义的方式访问其数据。使用 API,可以避免解析 HTML。相反,您可以使用 JSON 和 XML 等格式直接访问数据。HTML 主要是一种以视觉方式向用户呈现内容的方式。
当您使用 API 时,该过程通常比通过 Web 抓取收集数据更稳定。这是因为开发人员创建的 API 是供程序而不是人眼使用的。
网站的前端呈现可能会经常更改,但网站设计的这种更改不会影响其 API 结构。API 的结构通常更持久,这意味着它是站点数据的更可靠来源。
但是,API 也可以更改。多样性和持久性的挑战都适用于 API,就像它们适用于网站一样。此外,如果提供的文档缺乏质量,则自己检查 API 的结构要困难得多。
使用 API 收集信息所需的方法和工具不在本教程的讨论范围之内。要了解更多信息,请查看 Python 中的 API 集成。
抓取网站步骤 1:检查数据源在编写任何 python 代码之前,您需要了解要抓取的网站。这应该是您想要处理的任何网络抓取项目的第一步。
浏览网站点击浏览网站并与之互动,就像任何典型的求职者一样。例如,您可以滚动浏览网站的主页:
可以看到许多卡片格式的招聘信息,每个招聘信息都有两个按钮。如果单击“应用”,则会看到一个新页面,其中包含所选作业的更详细描述。您可能还会注意到,当您与网站交互时,浏览器地址栏中的 URL 会发生变化。
破译 URL 中的信息程序员可以在 URL 中对大量信息进行编码。如果您首先熟悉 URL 的工作原理及其组成,您的网络抓取之旅将会容易得多。例如,您可能会发现自己位于具有以下 URL 的详细信息页面上:
https://realpython.github.io/fake-jobs/jobs/senior-python-developer-0.html
可以将上述 URL 解构为两个主要部分:
URL 可以保存更多信息,而不仅仅是文件的位置。某些网站使用查询参数对执行搜索时提交的值进行编码。您可以将它们视为发送到数据库以检索特定记录的查询字符串。
可以在 URL 的末尾找到查询参数。例如,如果您转到 Indeed 并通过他们的搜索栏在“澳大利亚”中搜索“软件开发人员”,您会看到 URL 发生变化,以包含这些值作为查询参数:
https://au.indeed.com/jobs?q=software developer&l=Australia
此 URL 中的查询参数为 。查询参数由三部分组成:?q=software developer&l=Australia
可以将 URL 的查询参数拆分为两个键值对:
更改这些值以观察 URL 中的更改。
https://au.indeed.com/jobs?q=developer&l=perth
在网站的搜索框中更改并提交值,则它将直接反映在 URL 的查询参数中,反之亦然。如果您更改其中任何一个,那么您将在网站上看到不同的结果。
使用开发人员工具检查开发人员工具可以帮助您了解网站的结构。所有现代浏览器都安装了开发人员工具。在本节中, 了解如何使用 Chrome 中的开发者工具。该过程将与其他现代浏览器非常相似。
在 macOS 上的 Chrome 中,您可以通过选择“查看”→“开发人员”→“开发人员工具”来打开开发人员工具。在 Windows 和 Linux 上,您可以通过单击右上角的菜单按钮 () 并选择“更多工具”→“开发人员工具”来访问它们。您还可以通过右键单击页面并选择“检查”选项或使用键盘快捷键来访问开发人员工具:⋮
开发人员工具允许您以交互方式浏览网站的文档对象模型 (DOM),以更好地了解您的来源。若要深入了解页面的 DOM,请在开发人员工具中选择“元素”选项卡。您将看到一个包含可点击的 html 元素的结构。您可以直接在浏览器中展开、折叠甚至编辑元素:
右边的 HTML 表示您可以在左边看到的页面结构。
可以将浏览器中显示的文本视为该页面的 HTML 结构。如果你有兴趣,那么你可以在CSS-TRICKS上阅读更多关于DOM和HTML之间的区别。
右键单击页面上的元素时,可以选择“检查”以缩放到它们在 DOM 中的位置。您还可以将鼠标悬停在右侧的 HTML 文本上,然后看到页面上的相应元素亮起。
第 2 步:从页面中抓取 HTML 内容在安装任何外部包之前,请为项目创建虚拟环境。激活新的虚拟环境,然后在终端中键入以下命令以安装外部库:requests
(venv) $ python -m pip install requests
文本编辑器中打开一个新文件。检索 HTML 所需的只是几行代码:
import requests
URL = "https://realpython.github.io/fake-jobs/"
page = requests.get(URL)
print(page.text)
静态网站
<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 格式,但它总是值得一试。
某些页面包含隐藏在登录名后面的信息。这意味着您需要一个帐户才能从页面中抓取任何内容。从 Python 脚本发出 HTTP 请求的过程与从浏览器访问页面的方式不同。仅仅因为您可以通过浏览器登录页面并不意味着您可以使用 Python 脚本抓取它。
动态网站对于动态网站,服务器可能根本不会发回任何 HTML。相反, 可以接收 JavaScript 代码作为响应。
浏览器中发生的情况与脚本中发生的情况不同。您的浏览器将努力执行从服务器接收的 JavaScript 代码,并在本地为您创建 DOM 和 HTML。但是,如果您在 Python 脚本中请求动态网站,则不会获得 HTML 页面内容。
对于动态网站,您最终会得到一些 JavaScript 代码而不是 HTML。从您收到的 JavaScript 代码到您感兴趣的内容的唯一方法是执行代码,就像您的浏览器一样。库无法为您做到这一点,但还有其他解决方案可以。requestsrequests
例如,requests-html 是由库的作者创建的项目,它允许您使用类似于 中的语法来呈现 JavaScript。它还包括在后台使用 Beautiful Soup 来分析数据的功能。requestsrequests
第 3 步:解析 HTML 代码已经成功地从互联网上抓取了一些HTML,但当你看到它时,它似乎是一团糟。到处都是大量的 HTML 元素,成千上万的属性散落在各处——难道不是也混入了一些 JavaScript 吗?是时候在 Python 的帮助下解析这个冗长的代码响应了,使其更易于访问并挑选出您想要的数据。
Beautiful Soup 是一个用于解析结构化数据的 Python 库。它允许您以与使用开发人员工具与网页交互的方式与 HTML 进行交互。该库公开了几个直观的函数,您可以使用这些函数来浏览您收到的 HTML。首先,使用您的终端安装 Beautiful Soup:
(venv) $ 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 对象,该对象将 ,即您之前抓取的 HTML 内容作为其输入。page.content
第二个参数 , 确保对 HTML 内容使用适当的解析器。"html.parser"
按 ID 查找元素在 HTML 网页中,每个元素都可以分配一个属性。顾名思义,该属性使元素在页面上唯一可识别。您可以通过按 ID 选择特定元素来开始解析页面。idid
切换回开发人员工具,并确定包含所有招聘信息的 HTML 对象。通过将鼠标悬停在页面的某些部分并使用右键单击来检查来浏览。
<div id="ResultsContainer">
<!-- all the job listings -->
</div>
Beautiful Soup 允许您通过其 ID 查找该特定 HTML 元素:
蟒
results = soup.find(id="ResultsContainer")
可以在打印时美化任何 Beautiful Soup 对象。如果你调用你刚才在上面分配的变量,那么你会看到所有 HTML 包含在 :.prettify()results<div>
print(results.prettify())
使用元素的 ID 时,可以从 HTML 的其余部分中挑选出一个元素。现在,您只能使用页面 HTML 的这一特定部分。看起来汤有点稀了!但是,它仍然非常密集。
按 HTML 类名查找元素可以使用名为新对象并仅选择其中的职位发布。 :<div>card-contentresults
job_elements = results.find_all("div", class_="card-content")
调用一个 Beautiful Soup 对象,该对象返回一个可迭代对象,其中包含该页面上显示的所有工作列表的所有 HTML。.find_all()
看看所有这些:
for job_element in job_elements:
print(job_element, end="\n"*2)
您可以使用以下命令从每个招聘信息中挑选出这些子元素:.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_elementBeautifulSoup()results
<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 已为您准备好。您可以向 Beautiful Soup 对象添加以仅返回该对象包含的 HTML 元素的文本内容:.text
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()
运行上面的代码片段,你将看到显示的每个元素的文本。但是,您也可能获得一些额外的空格。由于您现在正在使用 Python 字符串,因此您可以使用多余的空格。您还可以应用任何其他熟悉的 Python 字符串方法来进一步清理文本:.strip()
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>
python_jobs = results.find_all("h2", string="Python")
此代码查找所包含的字符串完全匹配的所有元素。请注意,您是在第一个变量上直接调用该方法。如果您继续将上述代码片段的输出到您的控制台,那么您可能会感到失望,因为它将是空的:<h2>"Python"resultsprint()
>>> print(python_jobs)
搜索结果中有一个 Python 作业,为什么它没有显示?
当您按照上述方式使用时,您的程序会准确地查找该字符串。拼写、大小写或空格的任何差异都会阻止元素匹配。在下一节中,您将找到一种使搜索字符串更通用的方法。string=
将函数传递给 Beautiful Soup 方法除了字符串之外,有时还可以将函数作为参数传递给 Beautiful Soup 方法。您可以更改上一行代码以改用函数:
python_jobs = results.find_all(
"h2", string=lambda text: "python" in text.lower()
)
正在将一个匿名函数传递给参数。lambda 函数查看每个元素的文本,将其转换为小写,并检查是否在任何地方找到子字符串。您可以检查是否设法使用这种方法识别了所有 Python 作业:string=<h2>"python"
>>> print(len(python_jobs))
10
程序已找到匹配的职位发布,这些职位在其职位名称中包含该词!10"python"
根据元素的文本内容查找元素是过滤 HTML 响应以获取特定信息的有效方法。Beautiful Soup 允许您使用精确字符串或函数作为参数来筛选 Beautiful Soup 对象中的文本。
这似乎是运行循环并打印您确定的 Python 作业的标题、位置和公司信息的好时机:for
# .
python_jobs = results.find_all(
"h2", string=lambda text: "python" in text.lower()
)
for job_element in python_jobs:
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()
AttributeError: 'NoneType' object has no attribute 'text'
此消息是一个常见错误,当您从 Internet 抓取信息时会遇到很多错误。检查列表中元素的 HTML。它是什么样子的?您认为错误来自哪里?python_jobs
识别错误条件当您查看 中的单个元素时,您会看到它仅包含包含职位的元素: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().textNone
要查找的文本嵌套在筛选器返回的元素的同级元素中。Beautiful Soup 可以帮助您选择每个 Beautiful Soup 对象的同级、子级和父级元素。<h2>
访问父元素访问您需要的所有信息的一种方法是从您确定的元素开始在 DOM 的层次结构中升级。再看一下单个招聘信息的 HTML。查找包含职位名称的元素,以及包含您感兴趣的所有信息的最接近的父元素:<h2><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>
具有该类的元素包含所需的所有信息。它是您使用筛选器找到的 title 元素的第三级父元素。<div>card-content<h2>
牢记此信息后,您现在可以使用其中的元素并获取其曾祖元素,以访问所需的所有信息: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>
使用每个 Beautiful Soup 对象附带的属性,可以直观地逐步执行 DOM 结构并处理所需的元素。还可以以类似的方式访问子元素和同级元素。有关详细信息,请阅读导航树。.parent
从 HTML 元素中提取属性此时,您的 Python 脚本已经抓取了网站并过滤其 HTML 以查找相关的职位发布。*好!但是,仍然缺少的是申请工作的链接。
当您检查页面时,您会在每张卡片的底部找到两个链接。如果您以处理其他元素的方式处理链接元素,则不会获得您感兴趣的网址:
蟒
for job_element in python_job_elements:
# -- snip --
links = job_element.find_all("a")
for link in links:
print(link.text.strip())
如果运行此代码片段,则将获得链接文本,而不是关联的 URL。LearnApply
这是因为该属性只保留 HTML 元素的可见内容。它去除了所有 HTML 标记,包括包含 URL 的 HTML 属性,只留下链接文本。若要改为获取 URL,需要提取其中一个 HTML 属性的值,而不是丢弃它。.text
链接元素的 URL 与该属性相关联。您要查找的特定 URL 是单个招聘信息的 HTML 底部第二个标记的属性值:hrefhref<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")
结论
该库为您提供了一种用户友好的方式,可以使用 Python 从 Internet 获取静态 HTML。然后,您可以使用另一个名为 Beautiful Soup 的包来解析 HTML。这两个软件包都是您网络抓取冒险的值得信赖和有用的伴侣。
应该学习什么Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved