爬虫时通过 requests.get 方法获得 html 源代码后,通常需要从源代码中提取关键信息,这有多种方式,比如使用正则表达式匹配,也可通过 python 的第三方库 Beautiful Soup 实现定位提取关键信息,类似的库还有 lxml 第三方库中的 etree 模块。
目录 1 快速开始 1.1 Beautiful Soup 介绍 1.2 基础信息提取 ① HTML 代码 ② 基础方法汇总 ③ find 简单使用 1.3 解析器介绍 2 find 使用 2.1 标签/tag名定位 2.2 标签+属性定位 2.3 标签+文本值定位 2.4 正则表达式 2.5 find().find() 3 select 使用 3.1 css基本属性定位 3.2 css其他属性定位 3.3 css标签结合其他属性定位 3.4 css层级定位 3.5 css索引定位 3.6 css模糊匹配 4 实战--猫眼榜单 4.1 使用 requests 请求页面 4.2 分析页面及元素定位 1 快速开始 1.1 Beautiful Soup 介绍 简单来说,Beautiful Soup 是 Python 用于解析 HTML 和 XML 文件的第三方库,可以从 HTML 和 XML 文件中提取数据。
官方网址 : Beautiful Soup Documentation 中文文档 : Beautiful Soup 中文文档 安装 :pip install beautifulsoup4 Beautiful Soup 3 目前已经停止开发,官方支持 Beautiful Soup 4,并且将其移植到 BS4,也就是说我们需要 import bs4,本文 beautifulsoup4 版本 = 4.12.2。
Beautiful Soup提供了多种搜索方式,可以轻松定位所需的元素。
它还可以修复已损坏的HTML和XML文件,使其更易于理解和处理。
1.2 基础信息提取 ① HTML 代码 下面是一段官方提供的 html 代码( 爱丽丝梦游仙境的一段内容 ):
The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
...
页面效果如下, 右键选择“检查” 即可查看其 html 规范格式 👇
该 html 包含了诸多元素,比如标题元素(
The Dormouse's story
),其中 p 为其 tag-标签,class 是其属性,“The Dormouse's story”是其文本,其余元素类似,我们正是通过标签和相关属性对各元素进行定位。② 基础方法汇总
from bs4 import BeautifulSoup
html_doc = """
The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
...
"""
#默认解析html,若想要解析xml,需要把lxml换为xml
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.title) #html的标题元素
#
The Dormouse's story
print(soup.title.name) #html的标题元素标签名
# title
print(soup.title.string) #html的标题文本
# The Dormouse's story
print(soup.title.parent.name)#标题元素的父元素的标签名
# head
print(soup.p) #html中标签名为p的第一个元素
#
The Dormouse's story
print(soup.p['class']) #该元素的class属性值
# ['title']
print(soup.a) #html中标签名为a的第一个元素
# Elsie
print(soup.get_text()) #从文档中获取所有文字内容
③ find 简单使用
print(soup.find_all('a')) #找到所有标签名为a的元素
print(soup.find(id="link3")) #找到id属性为link3的元素
# Tillie
for link in soup.find_all('a'): #从文档中找到所有标签的链接
print(link.get('href'))
# http://example.com/elsie
# http://example.com/lacie
# http://example.com/tillie
1.3 解析器介绍
Beautiful Soup 可以使用不同的解析器,例如Python的内置HTML解析器,标准的xml解析器和第三方解析器(如lxml)。
下表列出了主要的解析器,以及它们的优缺点:
官方推荐使用 lxml 作为解析器(pip install lxml),因为效率更高,在 Python2.7.3 之前的版本和 Python3中3.2.2 之前的版本,必须安装 lxml 或 html5lib ,因为那些 Python 版本的标准库中内置的 HTML 解析方法不够稳定。
2 find 使用 为了使用Beautiful Soup解析网页,需要先根据网页的结构找到所需的关键信息。
在此过程中,常用的两个函数是find和find_all,通过它们可以找到并分析网页结构,以获取所需信息。
find_all() 方法将返回文档中符合条件的所有元素,返回结果是值包含一个元素的列表;find() 方法直接返回结果(元素)。
find_all() 方法没有找到目标是返回空列表,find() 方法找不到目标时返回 None。
2.1 标签/tag名定位 比如文档中只有一个
标签,那么使用 find_all() 方法来查找标签就不太合适,使用 find_all 方法并设置 limit=1 参数不如直接使用 find() 方法。语法: soup.find_all("标签名") soup.find_all("标签名", limit = 数量) soup.find_all(name = "标签名", limit = 数量) soup.find("标签名") soup.find(name = "标签名") 下面两行代码是等价的:
soup.find_all('title', limit=1) # 标签名/tag名为title
# [
The Dormouse's story ]
soup.find('title')
#
The Dormouse's story
2.2 标签+属性定位
标签名很容易重复,因此需要搭配属性名来进行准确定位!
下边两行代码作用一致,name 指的就是标签名,千万不要和 name 属性搞混了~ 语法: soup.find_all("标签名", attrs = {'属性':'属性值'}) soup.find("标签名", attrs = {'属性':'属性值'}) 其他属性比如 id、name、href 等等,都可以放到 attrs 的字典里去!
#2标签/tag名定位
soup.find("p",attrs={'class':'title'}) #标签名为p,class属性值为title
soup.find(name="p",attrs={'class':'title'})
#
The Dormouse's story
2.3 标签+文本值定位
同上,不过文本值需要用到 string 这个入参,text 目前已被 deprecated,官方推荐 string。
语法: soup.find_all("标签名", string= "文本") soup.find("标签名", string= "文本")
#3标签+文本值定位
soup.find("a",string="Elsie")
#Elsie
2.4 正则表达式
find 和 find_all 同样支持正则表达式的查找,如找出标签名中含有 title 或者是 p 的元素。
#4正则表达式
tags = soup.find_all(name =re.compile('title|a'))
for i in tags:
print(i)
print('--------------')
控制台输出:
--------------
--------------
Elsie--------------
Lacie--------------
Tillie-------------- 2.5 find().find() find() 定位到的就是元素,该元素依然可能存在子元素,那么就可以继续使用 find() 来进行定位。
from bs4 import BeautifulSoup
html_doc = """
The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
...
"""
soup = BeautifulSoup(html_doc, 'lxml')
story = soup.find('p',attrs={"class":"story"})
Elsie = story.find('a',attrs={"id":"link1"})
#Elsie
3 select 使用
select 和 select_one 类似于 find 和find_all,都是用来进行元素定位,不过它的选取规则依赖于 css 选择器。
3.1 css基本属性定位 css选择器也支持基本属性(tag、id、class)定位方式。
语法: soup.select("标签名") #css+id定位 soup.select("#id值") #css+class定位 soup.select(".class值") #css+标签定位
#1.css基本属性定位
soup.select("title")
#[
The Dormouse's story ]
soup.select("#link1")
#[Elsie]
soup.select(".title")
#[The Dormouse's story
]
3.2 css其他属性定位
另外一些非基础元素也可以通过以下形式进行辅助定位~
语法:
soup.select('[属性名=属性值]') #css+其他属性
soup.select('[属性名1=属性值1][属性名2=属性值2]') #css+多个其他属性
#2.css其他属性定位
soup.select('[href="http://example.com/elsie"]')
#[Elsie]
soup.select('[class="sister"][id="link3"]')
#[Tillie]
3.3 css标签结合其他属性定位
我们也可以通过标签+属性来进行定位
语法:
soup.select('标签名#id值') #标签+id属性定位
soup.select('标签名.class值') #标签+class属性定位
soup.select('标签名[属性名=属性值]') #标签+其他属性定位
#3.css标签结合其他属性定位
soup.select('a#link1')
#[Elsie]
soup.select('p.title')
#[
The Dormouse's story
]
soup.select('a[id=link1]')
#[Elsie]
3.4 css层级定位
在定位某个元素时,若无简洁标签属性可供参考,我们可以先定位到该元素的上级或上上级元素,随后通过层级关系获取该元素的位置。
语法: soup.select('标签1 > 标签2') #标签2为标签1的子标签 soup.select('标签1 > #id值') #id值为标签1的子标签的id值,class也可
#4.css层级定位
soup.select('head > title')
#[
The Dormouse's story ]
soup.select('p > #link1')
#[Elsie]
3.5 css索引定位
如图,该元素下存在3个a标签,像这种有多个相同标签名的元素,可以使用索引定位,通过索引来指定具体哪个 a 标签的元素。
注:与python列表索引的概念不同,此处的标签索引是从1开始;python列表的索引是从0开始。
语法: soup.select('标签:nth-child(n)') #正着数第n个标签 soup.select('标签:nth-last-child(n)') #倒着数第n个标签
#5.css索引定位
soup.select('a.sister:nth-child(1)') #a标签,类名为sister
#[Elsie]
soup.select('a.sister:nth-last-child(2)')
#[Lacie]
3.6 css模糊匹配
有时会遇到属性值过长的情况,此时我们可以通过模糊匹配来处理,只需要属性值的部分内容即可。
语法: soup.select("[属性名~='部分属性值']") #1.属性值由多个空格隔开,匹配其中一个值的方法 soup.select("[属性名^='属性值开头']") #2.匹配字符串开头 soup.select("[属性名$='属性值结尾']") #3.匹配字符串结尾
from bs4 import BeautifulSoup
html_doc = """
The Dormouse's story
The Dormouse's story
Once upon a time there were three little sisters; and their names were
Elsie,
Lacie and
Tillie;
and they lived at the bottom of a well.
...
"""
soup = BeautifulSoup(html_doc, 'lxml')
#5.css模糊匹配
print(soup.select("[class~='test']") )
#[Elsie]
print(soup.select("[class^='titl']"))
#[
The Dormouse's story
]
print(soup.select("[id$='ink3']"))
#[Tillie]
4 实战--猫眼榜单
那么在项目中如何使用 Beautiful Soup 对爬到的网页进行分析并提前关键的字段,本章内容将进行解答。
4.1 使用 requests 请求页面 本文针对 猫眼电影榜单--热映口碑榜 进行实战,该页面如下:
我们使用 requests 第三方库获取页面内容(html),requests 就不多说了,首先获取实际页面的请求头(使用浏览器的开发者工具):
爬虫代码:
import requests
url = "http://maoyan.com/board/7?offset=0"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54',
'Accept': '*/*',
'Host': 'www.maoyan.com',
'Connection': 'keep-alive'
}
resp = requests.get(url,headers=headers)
4.2 分析页面及元素定位
右键检查使用开发者工具,可以看到 标签名为dl的元素(
- )
dl中包含了10个的dd,每个dd里面包含着每一部电影的各种信息,解析dd就能解析出我们需要的属性,因此我们先定位到该元素。
#使用BeautifulSoup进行分析
maoyan_soup = BeautifulSoup(resp.text, "lxml")
wrapper_tag = maoyan_soup.select_one('dl.board-wrapper')
dd_tags = board_wrapper_tag.select('dd')
我们需要采集每条电影记录的排名、电影名、主演和上映时间,各信息对应的元素信息如上图所示,本文我使用 css 选择器进行定位,我将他们放在了 dic 里边。
css_items = {
'index': 'i.board-index',
'name': 'p.name a',
'star': 'p.star',
'time': 'p.releasetime'
}
完整代码如下:
import requests
from bs4 import BeautifulSoup
url = "http://maoyan.com/board/7?offset={}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54',
'Accept': '*/*',
'Host': 'www.maoyan.com',
'Connection': 'keep-alive',
'Cookie': '__mta=255104311.1682493547126.1682498459882.1682498495101.29; uuid_n_v=v1; uuid=A1063700E40211EDB6A41DA4355E52AD975693BE63D7498981D8E398A28DDC69; _csrf=9550a5fdc0d1595e1cd735b8a493bf15d4a988a78c1c3e57a05ccc904bb1e703; _lx_utm=utm_source=bing&utm_medium=organic; _lxsdk_cuid=187bc6e211ac8-026291e2eaf349-7e57547c-e1000-187bc6e211ac8; _lxsdk=A1063700E40211EDB6A41DA4355E52AD975693BE63D7498981D8E398A28DDC69; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1682493547; __mta=255104311.1682493547126.1682493552847.1682493564446.4; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1682498495; _lxsdk_s=187bcb90647-db6-e4a-a90||10'
}
css_items = {
'index': 'i.board-index',
'name': 'p.name a',
'star': 'p.star',
'time': 'p.releasetime'
}
data_list = []
resp = requests.get(url.format(0 * 10),headers=headers)
maoyan_soup = BeautifulSoup(resp, "lxml")
wrapper_tag = maoyan_soup.select_one('dl.board-wrapper')
if wrapper_tag is not None:
dd_tags = wrapper_tag.select('dd')
index, name, star, time ='', '', '', ''
for dd in dd_tags:
index = dd.select_one(css_items.get('index')).text
name = dd.select_one(css_items.get('name')).text
star = dd.select_one(css_items.get('star')).text.strip()
time = dd.select_one(css_items.get('time')).text
data = dict(index=index, name=name, star=star, time=time) #json格式
data_list.append(data)
print(data_list)
考虑到猫眼会进行反扒,下边也提供了
html
直接供大家进行测试:
热映口碑榜 - 猫眼电影 - 一网打尽好电影
2023-04-26已更新
榜单规则:将昨日国内热映的影片,按照评分从高到低排列取前10名,每天上午10点更新。相关数据来源于“猫眼专业版”及“猫眼电影库”。
