`BeautifulSoup` 与 `lxml`:HTML/XML 解析与 Web 抓取优化

好的,各位观众老爷们,今天咱们就来聊聊Web抓取的两大神器:BeautifulSoup和lxml。别怕,这不是什么高深的魔法,简单来说,就是让Python帮你从网页上扒数据。

开场白:网页,数据的金矿

想象一下,互联网就是一个巨大的金矿,里面埋藏着各种各样的数据,新闻、商品信息、评论、甚至是表情包!而咱们的任务就是化身矿工,用Python这把锄头,把这些金子挖出来。

但是,网页这玩意儿,可不是规规矩矩的表格,而是HTML/XML这些“乱七八糟”的标记语言堆砌起来的。所以,我们需要一种工具,能帮我们把这些标记语言翻译成Python能理解的结构,方便我们提取数据。

这时候,BeautifulSoup和lxml就登场了!

第一部分:BeautifulSoup:温柔的解析器

BeautifulSoup,顾名思义,就是“美丽的汤”。它能把一团乱麻似的HTML/XML文档,变成一棵结构清晰的树,方便我们用各种方法找到想要的数据。

1.1 安装BeautifulSoup

首先,你需要安装这个“美丽的汤”。打开你的命令行或者终端,输入:

pip install beautifulsoup4

1.2 一个简单的例子:煮一碗美味的“汤”

假设我们有下面这段HTML代码,我们要把它变成“汤”:

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
</body>
</html>
"""

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')  # 使用Python自带的html.parser
# 或者,如果你安装了lxml,也可以这样用:
# soup = BeautifulSoup(html_doc, 'lxml')

这段代码做了什么?

  • 我们定义了一个包含HTML代码的字符串 html_doc
  • 我们从 bs4 模块导入了 BeautifulSoup 类。
  • 我们创建了一个 BeautifulSoup 对象 soup,并将HTML代码和解析器类型传递给它。 这里使用了html.parser,也可以使用lxml

1.3 用BeautifulSoup找东西:像寻宝一样!

现在,我们的“汤”已经煮好了,可以开始寻宝了!

  • 找到第一个 <p> 标签:
first_p = soup.find('p')
print(first_p)
# 输出: <p class="title"><b>The Dormouse's story</b></p>
  • 找到所有 <a> 标签:
all_a = soup.find_all('a')
print(all_a)
# 输出: [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
  • 根据属性查找:
# 找到所有 class 为 "sister" 的 <a> 标签
sisters = soup.find_all('a', class_='sister')  # 注意class是Python的关键字,所以要写成class_
print(sisters)
# 输出: [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

# 找到 id 为 "link2" 的 <a> 标签
link2 = soup.find(id="link2")
print(link2)
# 输出: <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
  • 获取标签的内容:
# 获取第一个 <a> 标签的文本内容
first_a = soup.find('a')
print(first_a.text) # 或者 print(first_a.get_text())
# 输出: Elsie

# 获取第一个 <a> 标签的 href 属性
print(first_a['href']) # 或者 print(first_a.get('href'))
# 输出: http://example.com/elsie

1.4 CSS选择器:更灵活的寻宝方式

BeautifulSoup还支持CSS选择器,这让我们可以像写CSS样式一样查找元素,更加灵活!

# 找到所有 class 为 "sister" 的 <a> 标签
sisters = soup.select('a.sister')
print(sisters)
# 输出: [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

# 找到 id 为 "link2" 的 <a> 标签
link2 = soup.select_one('#link2') # select_one 返回第一个匹配的元素
print(link2)
# 输出: <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>

1.5 总结BeautifulSoup的优点和缺点

特点 优点 缺点
易用性 非常容易上手,API设计友好,适合初学者 解析速度相对较慢,特别是处理大型文档时
解析容错性 容错能力强,能处理不规范的HTML代码 某些情况下,对不规范HTML的处理可能不是最佳,需要根据具体情况调整解析策略
解析器选择 可以选择不同的解析器(html.parser, lxml, html5lib 不同的解析器在性能和容错性上有所差异,需要根据需求选择

第二部分:lxml:速度与激情的解析器

lxml 是一个基于 C 语言实现的 XML 和 HTML 的解析库。这意味着什么?速度快!如果你对性能有要求,lxml 绝对是你的首选。

2.1 安装lxml

pip install lxml

2.2 用lxml解析HTML:风驰电掣!

from lxml import etree

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
</body>
</html>
"""

tree = etree.HTML(html_doc)

这段代码做了什么?

  • 我们从 lxml 模块导入了 etree 类。
  • 我们使用 etree.HTML() 函数将HTML代码解析成一个 etree 对象 tree

2.3 用lxml找东西:XPath的艺术

lxml 最大的特点就是支持 XPath。XPath 是一种在 XML 文档中查找信息的语言,功能非常强大。

  • 找到第一个 <p> 标签:
first_p = tree.xpath('//p')  # // 表示从根节点开始查找所有 p 标签
print(first_p[0].text) # 输出第一个p标签的内容
# 输出: The Dormouse's story
  • 找到所有 <a> 标签:
all_a = tree.xpath('//a')
print(all_a)
# 输出: [<Element a at 0x...>, <Element a at 0x...>, <Element a at 0x...>]
# 注意:这里输出的是 Element 对象,而不是 BeautifulSoup 的 Tag 对象
  • 根据属性查找:
# 找到所有 class 为 "sister" 的 <a> 标签
sisters = tree.xpath('//a[@class="sister"]') # 注意xpath中的class要加引号
print(sisters)
# 输出: [<Element a at 0x...>, <Element a at 0x...>, <Element a at 0x...>]

# 找到 id 为 "link2" 的 <a> 标签
link2 = tree.xpath('//a[@id="link2"]')
print(link2)
# 输出: [<Element a at 0x...>]
  • 获取标签的内容和属性:
# 获取第一个 <a> 标签的文本内容
first_a = tree.xpath('//a')[0] # 先找到第一个a标签
print(first_a.text)
# 输出: Elsie

# 获取第一个 <a> 标签的 href 属性
print(first_a.get('href'))
# 输出: http://example.com/elsie

2.4 XPath语法速成班

XPath 语法比较复杂,这里给大家介绍一些常用的语法:

语法 含义 例子
/ 从根节点选取 /html/body/p
// 从文档中任何位置选取 //p
. 选取当前节点 .//a (从当前节点开始查找所有a标签)
.. 选取当前节点的父节点 ../div
@ 选取属性 //a[@href]
[] 谓语,用于筛选节点 //a[@class="sister"]
text() 获取元素的文本内容 //p/text()

2.5 总结lxml的优点和缺点

特点 优点 缺点
速度 非常快,适合处理大型文档和需要高性能的场景 学习曲线陡峭,XPath 语法相对复杂
功能 功能强大,支持 XPath,可以灵活地定位元素 容错性相对较弱,对不规范的HTML代码处理可能出错
内存占用 内存占用相对较小

第三部分:实战演练:爬取豆瓣电影Top250

光说不练假把式,咱们来个实战,爬取豆瓣电影Top250的电影名称和评分。

3.1 准备工作

首先,我们需要安装 requests 库,用于发送HTTP请求:

pip install requests

3.2 爬取网页

import requests
from bs4 import BeautifulSoup

url = 'https://movie.douban.com/top250'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
} # 伪装成浏览器,防止被反爬虫

response = requests.get(url, headers=headers)
html = response.text

这段代码做了什么?

  • 我们使用 requests.get() 函数发送一个GET请求,获取网页内容。
  • 我们设置了 User-Agent 头部,伪装成浏览器,防止被豆瓣的反爬虫机制拦截。

3.3 用BeautifulSoup解析网页

soup = BeautifulSoup(html, 'html.parser')
movies = soup.find_all('div', class_='item') # 找到所有电影条目
#print(movies)
for movie in movies:
    # 找到电影名称
    title = movie.find('span', class_='title').text
    # 找到电影评分
    rating = movie.find('span', class_='rating_num').text

    print(f'电影名称:{title},评分:{rating}')

3.4 用lxml解析网页

import requests
from lxml import etree

url = 'https://movie.douban.com/top250'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}

response = requests.get(url, headers=headers)
html = response.text

tree = etree.HTML(html)
movies = tree.xpath('//div[@class="item"]')

for movie in movies:
    # 找到电影名称
    title = movie.xpath('.//span[@class="title"]/text()')[0]
    # 找到电影评分
    rating = movie.xpath('.//span[@class="rating_num"]/text()')[0]

    print(f'电影名称:{title},评分:{rating}')

3.5 分页爬取

豆瓣电影Top250 分布在多个页面,我们需要分页爬取。

import requests
from bs4 import BeautifulSoup

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}

for i in range(0, 250, 25): # 每次爬取25个电影
    url = f'https://movie.douban.com/top250?start={i}&filter='
    response = requests.get(url, headers=headers)
    html = response.text

    soup = BeautifulSoup(html, 'html.parser')
    movies = soup.find_all('div', class_='item')

    for movie in movies:
        title = movie.find('span', class_='title').text
        rating = movie.find('span', class_='rating_num').text

        print(f'电影名称:{title},评分:{rating}')

第四部分:选择哪个?看情况!

那么,BeautifulSoup 和 lxml 到底该选哪个呢? 就像选对象一样,没有绝对的好坏,只有适不适合。

  • 如果你是新手,或者对性能要求不高,BeautifulSoup 是个不错的选择。 它的API简单易懂,容错性强,能让你快速上手。
  • 如果你对性能有要求,或者需要处理复杂的XML文档,lxml 绝对是你的首选。 它的速度快,功能强大,能让你事半功倍。

总结一下,可以参考下表:

选择依据 BeautifulSoup lxml
学习曲线 简单易学 相对陡峭
性能 较慢 非常快
容错性 相对较弱
功能 基本的HTML/XML解析 强大的HTML/XML解析,支持XPath
适用场景 小型项目,对性能要求不高,新手入门 大型项目,对性能要求高,需要处理复杂XML文档

彩蛋:一些小技巧

  • 善用开发者工具: 浏览器自带的开发者工具是Web抓取的利器,可以让你方便地查看网页的HTML结构,找到需要的数据。
  • 注意反爬虫: 很多网站都有反爬虫机制,比如限制访问频率、验证码等。我们需要采取一些措施来应对,比如设置 User-Agent、使用代理IP、添加延时等。
  • 遵守规则: Web抓取要遵守网站的规则,不要过度抓取,以免给网站带来负担,甚至被封IP。

结尾:祝大家挖金愉快!

好了,今天的讲座就到这里。希望大家通过今天的学习,能够掌握Web抓取的基本技能,从互联网这个巨大的金矿中挖到自己想要的数据。 记住,学习是一个循序渐进的过程,多练习,多实践,你也能成为Web抓取的高手!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注