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

好的,各位观众老爷们,今天咱们来聊聊Python里抓取网页的两位好基友:BeautifulSoup和lxml。别看名字都挺学术范儿,其实它们干的活儿特别接地气,就是帮咱们把网页上的乱七八糟的代码变成咱们看得懂、能操作的数据。

开场白:网页抓取那些事儿

话说,互联网上的信息就像大海里的沙子,多到数不清。但是,如果咱们想从这片信息海洋里捞点有用的东西,光靠肉眼那是肯定不行的。这时候,就需要咱们的网页抓取技术了。

网页抓取,简单来说,就是用程序模拟浏览器,把网页的内容扒下来,然后从里面提取咱们需要的信息。这听起来挺简单,但实际操作起来,你会发现网页的代码千奇百怪,格式五花八门,简直就是一场噩梦。

所以,咱们需要一些工具来帮咱们处理这些乱七八糟的代码,把它们变成结构化的数据,方便咱们进行分析和提取。而BeautifulSoup和lxml,就是咱们的救星!

第一回合:BeautifulSoup闪亮登场

BeautifulSoup,顾名思义,就是“美丽的汤”。这名字起得,让人感觉特别舒服。它的主要作用,就是把HTML或XML文档转换成一个树形结构,然后咱们就可以像操作树一样,轻松地找到咱们想要的节点和数据。

1. 安装BeautifulSoup

要使用BeautifulSoup,首先得把它装到你的电脑里。打开你的命令行,输入以下命令:

pip install beautifulsoup4

如果你的网络环境不太好,可以考虑使用国内的镜像源:

pip install beautifulsoup4 -i https://pypi.tuna.tsinghua.edu.cn/simple

2. 初试牛刀:解析HTML

咱们先来个简单的例子,解析一段HTML代码:

from bs4 import BeautifulSoup

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>
"""

soup = BeautifulSoup(html_doc, 'html.parser')

print(soup.prettify())

这段代码做了什么呢?

  • 首先,咱们导入了BeautifulSoup类。
  • 然后,咱们定义了一段HTML代码,赋值给html_doc变量。
  • 接着,咱们用BeautifulSoup类创建了一个soup对象,并指定了使用html.parser作为解析器。
  • 最后,咱们用prettify()方法把解析后的HTML代码格式化输出,看起来更舒服。

运行这段代码,你会看到BeautifulSoup把HTML代码转换成了一个漂亮的树形结构。

3. 查找元素:像探险一样

有了这个树形结构,咱们就可以像探险一样,去寻找咱们想要的元素了。BeautifulSoup提供了很多方法来查找元素,比如:

  • find():查找第一个符合条件的元素。
  • find_all():查找所有符合条件的元素。
  • find_parent():查找父元素。
  • find_next_sibling():查找下一个兄弟元素。
  • find_previous_sibling():查找上一个兄弟元素。

咱们来举几个例子:

# 查找title标签
title = soup.find('title')
print(title)  # <title>The Dormouse's story</title>
print(title.text)  # The Dormouse's story

# 查找所有a标签
links = soup.find_all('a')
for link in links:
    print(link.get('href'))  # 获取href属性
    # http://example.com/elsie
    # http://example.com/lacie
    # http://example.com/tillie

# 查找class为sister的a标签
sisters = soup.find_all('a', class_='sister')  # 注意class是Python的关键字,所以要加个下划线
for sister in sisters:
    print(sister.text)
    # Elsie
    # Lacie
    # Tillie

# 通过id查找
link1 = soup.find(id='link1')
print(link1.text) # Elsie

这些例子展示了BeautifulSoup强大的查找功能。你可以根据标签名、属性、文本内容等条件来查找元素。

4. 获取属性和文本

找到元素之后,咱们通常需要获取元素的属性和文本内容。BeautifulSoup提供了简单的方法来实现:

  • get(attribute):获取指定属性的值。
  • text:获取元素的文本内容。

上面的例子已经展示了如何使用get()text方法。

BeautifulSoup的优点:

  • 简单易用: 语法简单直观,容易上手。
  • 容错性好: 即使HTML代码不规范,也能解析。
  • 支持多种解析器: 可以选择不同的解析器来解析HTML/XML文档。

BeautifulSoup的缺点:

  • 速度较慢: 相比于lxml,解析速度较慢。
  • 依赖解析器: 不同的解析器对HTML/XML的容错性有所不同。

第二回合:lxml重磅出击

lxml是一个高性能的XML和HTML解析器,它是用C语言实现的,所以速度非常快。如果你对解析速度有较高要求,那么lxml绝对是你的首选。

1. 安装lxml

和BeautifulSoup一样,要使用lxml,也需要先安装它:

pip install lxml

同样,如果网络环境不好,可以使用国内镜像源:

pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple

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>
"""

tree = etree.HTML(html_doc)

print(etree.tostring(tree, pretty_print=True).decode())

这段代码和BeautifulSoup的例子很像,但是有一些区别:

  • 咱们导入的是lxml.etree模块。
  • 咱们使用etree.HTML()方法来解析HTML代码。
  • 咱们使用etree.tostring()方法把解析后的树形结构转换成字符串,并格式化输出。

3. XPath:精准定位

lxml最大的优势在于它支持XPath。XPath是一种强大的查询语言,可以用来在XML和HTML文档中查找节点。使用XPath,你可以非常精确地定位到你想要的元素。

咱们来举几个例子:

# 查找title标签
title = tree.xpath('//title/text()')
print(title)  # ['The Dormouse's story']

# 查找所有a标签的href属性
links = tree.xpath('//a/@href')
print(links)
# ['http://example.com/elsie', 'http://example.com/lacie', 'http://example.com/tillie']

# 查找class为sister的a标签的文本内容
sisters = tree.xpath('//a[@class="sister"]/text()')
print(sisters)
# ['Elsie', 'Lacie', 'Tillie']

# 通过id查找
link1 = tree.xpath('//a[@id="link1"]/text()')
print(link1) # ['Elsie']

这些例子展示了XPath的强大之处。你可以使用XPath表达式来选择节点、属性和文本内容。

XPath语法速览:

语法 描述
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
text() 选取文本内容
[] 谓语,用于筛选节点

lxml的优点:

  • 速度快: 用C语言实现,解析速度非常快。
  • 支持XPath: 可以使用XPath进行精准定位。
  • 功能强大: 提供了丰富的功能,可以处理各种XML和HTML文档。

lxml的缺点:

  • 学习曲线陡峭: XPath语法比较复杂,需要一定的学习成本。
  • 容错性稍差: 对HTML代码的规范性要求较高。

第三回合:实战演练:抓取豆瓣电影Top250

光说不练假把式,咱们来个实战演练,用BeautifulSoup和lxml抓取豆瓣电影Top250的信息。

1. 准备工作

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

pip install requests

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库,用于发送HTTP请求。
  • 咱们定义了要抓取的URL。
  • 咱们设置了User-Agent,模拟浏览器发送请求,避免被豆瓣反爬虫。
  • 咱们使用requests.get()方法发送GET请求,获取网页内容。
  • 咱们把网页内容赋值给html变量。

3. 使用BeautifulSoup解析HTML

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解析HTML,并提取电影名和评分:

  • 咱们使用BeautifulSoup类创建了一个soup对象。
  • 咱们使用find_all()方法查找所有div标签,且classitem的元素,这些元素包含了每个电影的信息。
  • 咱们遍历每个电影元素,使用find()方法查找电影名和评分,并打印出来。

4. 使用lxml解析HTML

from lxml import etree
import requests

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('./div[@class="info"]/div[@class="hd"]/a/span[@class="title"]/text()')[0]
    rating = movie.xpath('./div[@class="info"]/div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()')[0]
    print(f'电影名:{title},评分:{rating}')

这段代码使用lxml解析HTML,并提取电影名和评分:

  • 咱们使用etree.HTML()方法解析HTML代码。
  • 咱们使用XPath表达式//div[@class="item"]查找所有div标签,且classitem的元素。
  • 咱们遍历每个电影元素,使用XPath表达式查找电影名和评分,并打印出来。

性能对比

在实际项目中,特别是数据量大的情况下,性能差异会更加明显。lxml通常比BeautifulSoup快很多,因为它使用了C语言实现。但BeautifulSoup在处理一些不规范的HTML时,可能表现更好。

如何选择:BeautifulSoup vs lxml?

特性 BeautifulSoup lxml
速度
易用性 简单易用 学习曲线稍陡峭,需要学习XPath
容错性 较好,能处理一些不规范的HTML 稍差,对HTML规范性要求较高
功能 提供了基本的HTML/XML解析功能 功能强大,支持XPath,可以进行精准定位
适用场景 小型项目,对速度要求不高,HTML代码可能不规范 大型项目,对速度要求高,HTML代码比较规范

总结:英雄惜英雄

BeautifulSoup和lxml都是非常优秀的HTML/XML解析器。它们各有优缺点,适用于不同的场景。

  • 如果你是新手,或者只需要处理一些简单的HTML代码,那么BeautifulSoup可能更适合你。
  • 如果你对速度有较高要求,或者需要处理复杂的XML/HTML文档,那么lxml是更好的选择。

当然,你也可以把它们结合起来使用。比如,先用BeautifulSoup把HTML代码转换成树形结构,然后再用lxml的XPath来查找元素。

总而言之,选择哪个工具,取决于你的具体需求。希望今天的讲解能够帮助你更好地理解BeautifulSoup和lxml,并在你的网页抓取项目中发挥它们的作用。 各位观众老爷们,咱们下期再见!

发表回复

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