【Python项目实战】网页爬取(中)

HTML Parser

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

【Python项目实战】网页爬取(中)
【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")

此程序做了以下三件事情:

  1. 通过urlopen()函数打开传入的url网址

  2. 从页面中以字符串的形式读取HTML并将其赋值给html变量

  3. 创建一个BeautifulSoup对象并将其赋值给soup变量

分配给soupBeautifulSoup对象是通过两个参数创建的。第一个参数是待解析的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">之类的标签也拥有两个不同的属性——hreftarget.

要在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>标记是用带有空格的全部大写写的:

【Python项目实战】网页爬取(中)
profile page

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

在浏览器中打开前面例子中的网页,显示如下:

【Python项目实战】网页爬取(中)
login

尝试输入随机的用户名和密码组合。如果你输入错了,那么在页面底部将会显示错误信息。但是,如果你提供了正确的登录凭证,那么你会被重定向到/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'

现在让我们来一一拆解并分析这个过程究竟发生了什么:

  1. 首先,我们创建一个Browser实例并使用它来请求URL。紧接着,我么使用了.soup属性将页面的HTML内容分配给login_html变量。

  2. 随后,Login_html.select("form")以列表的形式返回页面上所有<form>元素。因为页面只有一个元素,所以可以通过检索列表索引0处的元素来访问表单。当一个页面上只有一个表单时,也可以使用login_html.form。接下来的两行选择用户名和密码输入,并将它们的值分别设置为zeusThunderDude

  3. 最后,我们使用了browser.submit()来提交表单。注意,该方法需要传递两个参数,即表单对象和login_pageURL,此处可以通过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。然后你可以将基本URLsrc属性中的相对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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!