HTML Parser
上一期我们为大家介绍了网页爬取的基础内容,包括如何读取网页以及提取相关信息,同时介绍一些相关的Python基础语法和正则表达式知识点,还没看的小伙伴可以先回顾一下:

尽管正则表达式通常来说非常适合模式匹配相关的任务,但有时我们使用专门为解析HTML
页面而设计的HTML解析器
进行处理会让我们的工作变得更轻松。Python
社区中存在许多类似的工具,其中Beautiful Soup
库是一个非常适合入门的网页解析库,让我们从这里开始学起。
Install Beautiful Soup
首先,从终端安装Beautiful Soup
库,这里可以使用pip
指令完成:
$ python -m pip install beautifulsoup4
通过该命令,你将把Beautiful Soup
的最新版本安装到全局的Python
环境中。
Create a BeautifulSoup Object
接下来让我们新建一个脚本文件beauty_soup.py
,输入以下内容:
from bs4 import BeautifulSoup
from urllib.request import urlopen
url = "http://olympus.realpython.org/profiles/dionysus"
page = urlopen(url)
html = page.read().decode("utf-8")
soup = BeautifulSoup(html, "html.parser")
此程序做了以下三件事情:
-
通过
urlopen()
函数打开传入的url
网址 -
从页面中以字符串的形式读取
HTML
并将其赋值给html
变量 -
创建一个
BeautifulSoup
对象并将其赋值给soup
变量
分配给soup
的BeautifulSoup
对象是通过两个参数创建的。第一个参数是待解析的HTML
,第二个参数是相应的解析器选项,Python
内置的默认解析器便是html
.
Use a BeautifulSoup Object
保存并运行上述程序。当它完成运行时,您可以在交互窗口中使用soup
变量以各种方式解析html
的内容。
注意:如果你没有使用
IDLE
,则可以使用-i
标志运行程序以进入交互模式。例如,python -i beauty_soup.py
,它首先会运行程序,然后把你留在REPL
中,在这里你可以尽情探索。
举个例子,BeautifulSoup
对象有一个get_text()
方法,您可以使用该方法从文档中提取所有文本,它会为用户自动删除任何HTML
标签头。
在IDLE
的交互窗口或编辑器的代码末尾输入以下代码,你会得到:
>>> print(soup.get_text())
Profile: Dionysus
Name: Dionysus
Hometown: Mount Olympus
Favorite animal: Leopard
Favorite Color: Wine
在这个输出中有很多空行。它们是HTML文档文本中的换行符的结果。如果需要,可以使用replace()
字符串方法删除它们。另外,如果使用了Beautiful Soup
后,通常结合find()
方法会比使用正则表达式来得更容易。
但是,有些时候HTML
标记本身是指出你想要检索的数据的元素。例如,当我们想检索页面上所有图像的url
,这些链接包含在<img> HTML
标签的src
属性中。在这种情况下,你可以使用find_all()
方法返回该特定标记的所有实例的列表:
>>> soup.find_all("img")
[<img src="/static/dionysus.jpg"/>, <img src="/static/grapes.png"/>]
这将返回HTML
文档中所有带<img>
标记的列表。列表中的对象看起来像是表示标记的字符串,但它们实际上是Beautiful Soup
提供的Tag对象的实例。标记对象为处理它们所包含的信息提供了一个简单的接口。你可以先从列表中解包Tag
对象来探索一下:
>>> image1, image2 = soup.find_all("img")
每个Tag
对象都有一个.name
属性,该属性返回一个包含HTML
标记类型的字符串:
>>> image1.name
'img'
你可以通过将标签对象的名称放在方括号中来访问它们的HTML
属性,就像属性是字典中的键一样。举个例子,就好像 <img src="/static/dionysus.jpg"/>
标签带有一个单独的属性,src
,以及一个相应的值/static/dionysus.jpg
. 同样的,一个诸如<a href="https://realpython.com" target="_blank">
之类的标签也拥有两个不同的属性——href
和target
.
要在Dionysus
配置文件页面中获得图像的来源,我们可以使用上面提到的字典符号访问src
属性:
>>> image1["src"]
'/static/dionysus.jpg'
>>> image2["src"]
'/static/grapes.png'
其实这就跟访问Python
中的字典一样方便。HTML
文档中的某些标记可以通过Tag
对象的属性访问。例如,要获取文档中的<title>
标签,你可以使用.title
属性</span></p></title>
:
>>> soup.title
<title>Profile: Dionysus</title>
如果你通过导航到配置文件页面,右键单击页面,并选择View page source
来查看Dionysus
配置文件的源文件,那么你将注意到<title>
标记是用带有空格的全部大写写的:

Beautiful Soup
通过删除开始标签中的额外空间和结束标签中的多余斜杠/
,自动清理标签。你也可以用Tag
对象的string
属性来检索title
标签之间的字符串:
>>> soup.title.string
'Profile: Dionysus'
Beautiful Soup
的特性之一是能够搜索属性与特定值匹配的特定类型的标记。例如,如果你想找到src
属性等于/static/dionysus.jpg
值的所有<img>
标签,那么你可以向find_all()
提供以下附加参数:
>>> soup.find_all("img", src="/static/dionysus.jpg")
[<img src="/static/dionysus.jpg"/>]
这个例子有些武断,从这个例子中可能看不出这种技术的有用性。如果你花点时间浏览各种网站并查看它们的页面来源,那么你就会注意到许多网站都有极其复杂的HTML
结构。
当使用Python
从网站中抓取数据时,人们通常只对页面的特定部分内容感兴趣。通过花一些时间查看HTML
文档,您可以识别具有惟一属性的标记,可以使用这些属性提取所需的数据,而无需依赖复杂的正则表达式或使用find()
在文档中搜索,而是可以直接访问感兴趣的特定标记并提取所需的数据。
在某些情况下,你可能会发现Beautiful Soup
提供不了你需要的功能。lxml
库在开始使用时有些棘手,但在解析HTML
文档方面提供了比Beautiful Soup
更大的灵活性。当你习惯使用Beautiful Soup
时,你可能会想要看看它。
注意:在定位网页中的特定数据时,像
Beautiful Soup
这样的HTML
解析器可以为你节省大量的时间和精力。然而,有时HTML
编写得非常糟糕,组织混乱,即使是像Beautiful Soup
这样复杂的解析器也不能正确地解释HTML
标记。
在这种情况下,通常只能使用
find(
和正则表达式技术来尝试解析出所需的信息。
Beautiful Soup
非常适合从网站的HTML
中抓取数据,但它不提供任何处理HTML
表单的方法。例如,如果你需要在一个网站上搜索一些查询,然后抓取结果,那么仅使用Beautiful Soup
不会让你一帆风顺,毕竟没有任何工具是万能的。下面我们将为大家介绍如何处理表单数据。
Interact With HTML Forms
到目前为止,你在本教程中使用的urllib
模块非常适合请求web
页面的内容。不过,有时需要与网页进行交互才能获得所需的内容。例如,您可能需要提交一个表单或单击一个按钮来显示隐藏的内容。
注意:本教程改编自《Python Basics: A Practical Introduction to Python3》中的
Web交互
一章,如果你喜欢此内容,不妨读读看。
Python
标准库没有提供交互式处理web
页面的内置方法,但是许多第三方包可以从PyPI
获得。在这些包中,MechanicalSoup
是一个流行且使用起来相对简单的包。
从本质上说,MechanicalSoup
安装的是所谓的headless
浏览器,这是一种没有图形用户界面的网络浏览器。该浏览器是通过Python
程序以编程方式控制的。
Install MechanicalSoup
同以往一样吗,我们可以用pip
在你的终端安装MechanicalSoup
库:
$ python -m pip install MechanicalSoup
注意:你需要关闭并重新启动
IDLE
会话,以便在安装MechanicalSoup
之后加载并被计算机正确识别。
Create a Browser Object
在IDLE
的交互窗口中输入以下内容:
>>> import mechanicalsoup
>>> browser = mechanicalsoup.Browser()
浏览器对象表示的是无头的网页浏览器。你可以通过将URL
传递给它们的get()
方法来使用它们以从Internet
请求页面:
>>> url = "http://olympus.realpython.org/login"
>>> page = browser.get(url)
page
是一个Response
对象,用于存储从浏览器请求URL
的响应:
>>> page
<Response [200]>
这里为大家普及一下,数字200
表示请求返回的状态码。状态码为200
表示请求成功。如果URL
不存在,不成功的请求可能会显示404
状态码,如果发出请求时出现服务器错误,则可能显示500
状态代码。
MechanicalSoup
使用BeautifulSoup
来解析来自请求的HTML
,页面有一个.soup
属性来表示一个BeautifulSoup
对象:
>>> type(page.soup)
<class 'bs4.BeautifulSoup'>
类似的,我们可以通过检查.soup
属性来查看HTML
内容:
>>> page.soup
<html>
<head>
<title>Log In</title>
</head>
<body bgcolor="yellow">
<center>
<br/><br/>
<h2>Please log in to access Mount Olympus:</h2>
<br/><br/>
<form action="/login" method="post" name="login">
Username: <input name="user" type="text"/><br/>
Password: <input name="pwd" type="password"/><br/><br/>
<input type="submit" value="Submit"/>
</form>
</center>
</body>
</html>
注意,这个页面上有一个<form>
标签,其中<input>
元素用于输入用户名和密码。
Submit a Form With MSoup
在浏览器中打开前面例子中的网页,显示如下:

尝试输入随机的用户名和密码组合。如果你输入错了,那么在页面底部将会显示错误信息。但是,如果你提供了正确的登录凭证,那么你会被重定向到/profiles
页面:
Username | Password |
---|---|
zeus | ThunderDude |
在下一个例子中,你将看到如何使用MechanicalSoup
填写并提交此表单!
HTML
代码的重要组成部分是表单登录,即<form>
标签内的所有内容。此页面上的<form>
的name
属性设置为login
。该表单包含两个<input>
元素,一个名为user
,另一个名为pwd
。第三个<input>
元素是Submit
按钮。
现在,你已经了解了登录表单的底层结构以及登录所需的凭据,下面让我们尝试填写并提交表单的程序。在一个新的编辑器窗口中,输入以下程序:
import mechanicalsoup
# st1
browser = mechanicalsoup.Browser()
url = "http://olympus.realpython.org/login"
login_page = browser.get(url)
login_html = login_page.soup
# st2
form = login_html.select("form")[0]
form.select("input")[0]["value"] = "zeus"
form.select("input")[1]["value"] = "ThunderDude"
# st3
profiles_page = browser.submit(form, login_page.url)
保存文件并按F5
运行它。要确认您已经成功登录,请在交互窗口中输入以下内容:
>>> profiles_page.url
'http://olympus.realpython.org/profiles'
现在让我们来一一拆解并分析这个过程究竟发生了什么:
-
首先,我们创建一个
Browser
实例并使用它来请求URL
。紧接着,我么使用了.soup
属性将页面的HTML
内容分配给login_html
变量。 -
随后,
Login_html.select("form")
以列表的形式返回页面上所有<form>
元素。因为页面只有一个元素,所以可以通过检索列表索引0处的元素来访问表单。当一个页面上只有一个表单时,也可以使用login_html.form
。接下来的两行选择用户名和密码输入,并将它们的值分别设置为zeus
和ThunderDude
。 -
最后,我们使用了
browser.submit()
来提交表单。注意,该方法需要传递两个参数,即表单对象和login_page
的URL
,此处可以通过login_page.url
访问它们。
在交互窗口中,当确认提交成功后网站会被重定向(跳转)到/profiles
页面。如果发生了错误,则profiles_page
的值仍然是http://olympus.realpython.org/login
登录页面。
注意:黑客可以使用像上面那样的自动化脚本程序,通过快速尝试许多不同的用户名和密码来强行登录,直到他们找到一个有效的组合。
这除了是严重的非法行为之外,如果你发出太多失败的请求,现如今几乎所有的网站都会锁定并报告你的
IP
地址,所以请勿轻易尝试!
既然已经设置了profiles_page
变量,现在就可以通过编程方式获取/profiles
页面上每个链接的URL
了。
要做到这一点,你再次使用.select()
,这次传递字符串a
来选择页面上所有<a>
的锚元素:
>>> links = profiles_page.soup.select("a")
现在你可以遍历每个链接并打印出它们的href
属性了:
>>> for link in links:
... address = link["href"]
... text = link.text
... print(f"{text}: {address}")
...
Aphrodite: /profiles/aphrodite
Poseidon: /profiles/poseidon
Dionysus: /profiles/dionysus
值得注意的是,每个href
属性中包含的url
都是相对url
,如果你想在以后使用MechanicalSoup
导航到它们,这并不是很有帮助。如果您碰巧知道完整的URL
,那么您可以分配构建完整URL
所需的部分。
在本例中,基础URL
只是http://olympus.realpython.org
。然后你可以将基本URL
与src
属性中的相对URL
连接起来:
>>> base_url = "http://olympus.realpython.org"
>>> for link in links:
... address = base_url + link["href"]
... text = link.text
... print(f"{text}: {address}")
...
Aphrodite: http://olympus.realpython.org/profiles/aphrodite
Poseidon: http://olympus.realpython.org/profiles/poseidon
Dionysus: http://olympus.realpython.org/profiles/dionysus
只使用.get()
、.select()
和.submit()
方法我们便可以做很多事情。当然,MechanicalSoup
的能力远不止这些,要了解更多关于MechanicalSoup
的信息,请查看相应的官方文档。
目前为止,我们已经学会了如何读取网页、解析网页、与网页中的表单进行交互,如果对于这块内容还不熟悉的同学可以按照本文教程一步步的操作练习,毕竟没有人天生就是专家!在本项目的最后,我们将教大家如何实时的进行网页交互,敬请期待,下期不见不散。
原文始发于微信公众号(Pytrick):【Python项目实战】网页爬取(中)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/56406.html