好的,各位观众老爷们,今天咱们就来聊聊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抓取的高手!