使用 Python 的 lxml 进行网页抓取:初学者教程
使用 Python 的 lxml 进行网页抓取的分步指南。
Python 有很多 很棒的图书馆 在网页抓取中使用最多的库,lxml 就是其中之一。在本指南中,您将了解为什么应该选择 Python 的 lxml 库进行网页抓取、如何准备项目以及如何通过实际示例构建强大的 lxml 抓取工具。您还将找到一些通用技巧来帮助您获得更多成功的请求。
什么是在 Python 中使用 lxml 进行 Web 抓取?
为什么选择 Python 的 lxml 进行网页抓取?
选择 lxml 有几个原因:
- 可扩展。 lxml 封装了两个 C 库 - libxml2 和 libxalt - 使解析器具有高度可扩展性。它包括速度、本机 Python API 的简单性和 XML 特性等功能。
- 指定 XML 结构。 该库支持三种模式语言,可帮助指定 XML 结构。此外,它还可以完全实现 XPath。它是一种有用的 网页抓取技术 用于识别 XML 文档中的元素。
- 允许遍历数据。 lxml 能够浏览不同的 XML 和 HTML 结构。该库可以遍历子元素、同级元素和其他元素,而 Beautiful Soup 等解析器则无法做到这一点。
- 轻资源。 lxml 的一个优点是它不占用太多内存。这使得该库运行速度很快,适合解析大量数据。
但 lxml 并不适合所有解析任务。如果你的目标网页的 HTML 写得不好或有问题,这个库很可能会失败。在这种情况下,它包括一个回退到 美丽的汤.
使用 Python 构建 lxml 解析器的步骤
步骤 1:选择合适的工具
要使用 lxml 构建功能齐全的抓取工具,您还需要其他 Python 依赖项。
首先,选择一个 HTTP 客户端来获取数据。您可以使用 Python 的库,如 Requests、HTTPX 或 aiohttp。选择哪个取决于您的偏好和您心中的网页抓取项目。如果您不熟悉任何一个,请选择 Requests - 它是最古老的一个,并且非常可定制。
如果您决定通过抓取动态网站(例如某些电子商务或社交媒体网站)来提升自己的技能,那么 HTTP 客户端和解析器是不够的。您需要一个可以渲染 JavaScript 的工具。在这种情况下,请使用无头浏览器库,例如 硒 处理异步和 AJAX 请求。
第 2 步:确定目标网页
现在您已准备好工具,可以识别目标网页。例如,您可能希望抓取 Google 搜索结果以监控您和竞争对手的 Google 排名,或者从亚马逊等电子商务商店收集产品信息。
有很多项目供你尝试——我们甚至准备了一个单独的 关于一些网络抓取想法的指导。 否则,你可以 练习你的技能 在虚拟网站上——它们提供了掌握网页抓取过程所需的所有必要元素。
步骤 3:查看网页抓取指南
如果你不知道一些 网页抓取挑战,这个过程可能会变成一个真正的麻烦。从 CAPTCHA 提示到 IP 地址禁令,这些障碍可能会阻碍你的项目成功。所以,如果你对你的项目很认真,我建议你 旋转代理服务器 它将根据需要代表您更改您的 IP 地址和位置。
无论目标是什么(网络抓取沙箱除外),您都应该知道网站通常有名为 robots.txt 的说明来管理机器人流量。这是一套规则,规定了您可以和不能抓取哪些页面以及您可以抓取的频率。无论如何,网络抓取包括遵循这些和其他准则 - 花点时间熟悉 网络抓取最佳实践.
此外,浏览器发出的请求包含带有设备信息的标头。你为什么要关心?如果用户代理字符串(一个重要的标头)缺失或格式错误,页面可能不允许你的抓取工具获取数据。大多数 HTTP 客户端都会发送其用户代理标头,因此在抓取真实目标时你需要更改它。但别担心——我稍后会告诉你如何操作。
使用 Python 的 lxml 进行 Web 抓取:分步教程
在本分步教程中,我们将从 IMDb 抓取本月最受欢迎的电影(及其所有信息)。由于 Python 的 lxml 是一个 Web 解析库,我们还将使用 Requests 下载数据。
该平台提供了更多有趣的列表供抓取 - 但请记住检查 robots.txt 并遵守其他网络抓取指南!
硬件需求
- Python 3。 检查您的设备上是否安装了最新版本。否则,请从 Python.org 下载。
- 要求。 填写
pip install requests在您的操作系统终端中。 - lxml。 通过运行添加
pip install lxml. - 代码编辑器。 您可以选择任何代码编辑器。例如,Notepad++、Visual Studio Code 或操作系统的文本编辑器。
导入库
步骤 1。 首先,让我们导入必要的库。
import requests
from lxml import etree
步骤 2。 然后,定义您要抓取的 URL。
url = "https://www.imdb.com/chart/moviemeter/"
添加指纹(标题)
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://www.google.com/",
"Sec-Ch-Ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"Windows\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
}
获取 HTML 内容
步骤 1。 定义主要的网页抓取功能。
def request():
步骤 2。 然后,向 IMDb 发出请求并随附您的指纹。
response = requests.get(url, headers=headers)
步骤 3。 检查状态代码。查找 200 – 表示请求成功。
print(response.status_code)
步骤 4。 收到响应后,使用以下方法解析 HTML 内容 etree.HTML() lxml 提供的解析器。
tree = etree.HTML(response.text)
提取电影数据
步骤 1。 创建一个新列表来保存您要抓取的电影信息。
movies_list = []
步骤 2。 要查找要抓取的元素,请右键单击页面上的任意位置,然后按 检查。 寻找 标签,从 HTML 和其中的所有列表项中获取内容。
list_elem = tree.xpath(".//ul[contains(@class,'ipc-metadata-list')]/li")
让我们仔细看看代码:
- ul – 无序列表标签。
- li – 列表项。此选择器查找 包含一个元素 ipc 元数据列表t 类。您还需要 元件。
- 标签 – 合并选定的列表项并将结果分配给新变量。
- 列表元素 – 所有列表项对象的列表。
第三步: 现在,您可以遍历所有列表项。
for list_item in list_elem:
步骤 4。 创建一个 JSON 对象来存储信息。
json = {}
步骤 5。 现在,我们来获取电影信息排名和标题。
1)首先,获取电影信息排名。查找 元素类别 米常数排名 使用 XPath 并将其分配给变量 movie_rank_elem.
movie_rank_elem = list_item.xpath(".//div[contains(@class, 'meter-const-ranking')]")
2)的 movie_rank_elem 本身也是一个 lxml 元素,所以我们可以使用 xpath() 函数来选择此元素。从元素中获取文本,由于函数返回包含单个项目的列表,因此只需从列表中取出第一个项目并将其分配给 JSON 字典。
json['rank_num'] = movie_rank_elem[0].xpath(".//text()")[0]
3)让我们继续提取标题。这里的区别在于,您将选择一个 带有一类的标签 ipc-title__text 并直接将文本分配给 标题 键入你的字典。
json['title'] = list_item.xpath(".//h3[@class='ipc-title__text']/text()")[0]
步骤 6。 现在,获取有关发布日期、长度和内容分级的信息。这个过程略有不同,因为这些数据点缺乏特定的类别来直接选择它们。相反,您需要选择具有相同标签和类别的元素,从而得到一个仅包含 1 到 3 个项目的列表。
movie_metadata_elems = list_item.xpath(".//span[contains(@class, 'cli-title-metadata-item')]")
注意: 作为 movie_metadata_elems 列表包含 lxml 元素,您可以通过它们的索引访问每个元素。
1)发布日期是第一项,因此其索引为 0,长度为 1,内容评级为 2。
注意: 并非每部电影都有内容评级,因此请检查长度以确保有内容评级。通过索引访问每个元素后,使用 XPath 获取文本值并将其分配给字典。
json['release_date'] = movie_metadata_elems[0].xpath(".//text()")[0]
json['length'] = movie_metadata_elems[1].xpath(".//text()")[0]
if len(movie_metadata_elems) > 2:
json['content_rating'] = movie_metadata_elems[2].xpath(".//text()")[0]
else:
json['content_rating']: None
2)要获取每部电影的评分和投票数,使用与之前类似的方法,选择每个元素并获取其中的文本。
注意: 并非每部电影都具有这些元素中的一个或两个。要处理这个问题,请添加一个“if”语句,以便在将结果添加到字典之前返回一个列表。
movie_rating_elem = list_item.xpath(".//span[contains(@class, 'ipc-rating-star')]/text()")
if movie_rating_elem:
json['rating'] = movie_rating_elem[0]
movie_vote_count_elem = list_item.xpath(".//span[contains(@class, 'ipc-rating-star--voteCount')]/text()")
if movie_vote_count_elem:
json['vote_count'] = movie_vote_count_elem[1]
json['url'] = "https://www.imdb.com/"+list_item.xpath(".//div[contains(@class, 'ipc-title')]/a/@href")[0]
步骤 7。 然后,添加 JSON 字典 电影列表 列表并让脚本继续解析下一部电影的列表项。
movies_list.append(json)
步骤 8。 最后,打印出输出。
print (movies_list)
if __name__ == "__main__":
request()
这是完整的代码:
import requests
from lxml import etree
url = "https://www.imdb.com/chart/moviemeter/"
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
"Referer": "https://www.google.com/",
"Sec-Ch-Ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"Windows\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
}
def request():
response = requests.get(url, headers=headers)
print(response.status_code)
tree = etree.HTML(response.text)
movies_list = []
list_elem = tree.xpath(".//ul[contains(@class,'ipc-metadata-list')]/li")
for list_item in list_elem:
json = {}
movie_rank_elem = list_item.xpath(".//div[contains(@class, 'meter-const-ranking')]")
json['rank_num'] = movie_rank_elem[0].xpath(".//text()")[0]
json['title'] = list_item.xpath(".//h3[@class='ipc-title__text']/text()")[0]
movie_metadata_elems = list_item.xpath(".//span[contains(@class, 'cli-title-metadata-item')]")
json['release_date'] = movie_metadata_elems[0].xpath(".//text()")[0]
json['length'] = movie_metadata_elems[1].xpath(".//text()")[0]
if len(movie_metadata_elems) > 2:
json['content_rating'] = movie_metadata_elems[2].xpath(".//text()")[0]
else:
json['content_rating'] = None
movie_rating_elem = list_item.xpath(".//span[contains(@class, 'ipc-rating-star')]/text()")
if movie_rating_elem:
json['rating'] = movie_rating_elem[0]
movie_vote_count_elem = list_item.xpath(".//span[contains(@class, 'ipc-rating-star--voteCount')]/text()")
if movie_vote_count_elem:
json['vote_count'] = movie_vote_count_elem[1]
json['url'] = "https://www.imdb.com/" + list_item.xpath(".//div[contains(@class, 'ipc-title')]/a/@href")[0]
movies_list.append(json)
print(movies_list)
if __name__ == "__main__":
request()