06>>>beautiful Soup4模块
解析库beautiful Soup
beautiful Soup是一款可以从HTML或XML文件中提取数据的python库,简称BS。它能够通过你喜欢的转换器实现管用的文档导航、查找、寻该文档的方式,BS会帮你节省数小时甚至数天的工作时间。
简单来说:BS可以帮助我们筛选网络中需要的数据。
官网推荐我们使用Beautiful Soup4。
照例打开pycharm下载就可以了。
pip3 install beautifulsoup4(没有空格,一定要写全名)
光有解析库还不够,bs4需要配合解析器一起使用。
解析器可以简单理解为启动bs4的钥匙。在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定。
官网推荐使用lxml作为解析器,因为效率更高。
pip3 install lxml
bs4模块基本使用
既然bs4模块是帮助我们从网站上筛选数据的,那我们可以先构造一个网页数据,熟悉一下它是怎么使用的。
</--!构造一个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> <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>
假设我们从网络上爬下了一个html文件,内容如上。那么该怎么用bs4模块筛选出我们需要的数据呢?
首先,当然是导入模块。
from bs4 import BeautifulSoup
1.构造一个bs4解析器对象
语法: soup = BeautifulSoup(待解析的文本,'解析器名称') 实际: soup = BeautifulSoup(html_doc,'lxml')
其实不加上解析器也可以,这样python会提示你没有选择解析器,使用默认的。
莫慌,虽然用的是红色字体,但这并不是报错。这段话就是在提醒你没有选定解析器,所以使用了默认的解析器。只要我们把解析器加上,就正常运作了。
2.利用对象的内置方法完成一系列操作
1.获取从上往下的第一个标签
print(soup.a) # 获取从上往下的第一个a标签
可想而知,该方式的筛选精确度不高,不过很基础很好懂。
2.获取从上往下的第一个标签里的文本内容
print(soup.a.text) # 获取从上往下的第一个a标签里的文本内容 print(soup.p.text) # 获取从上往下的第一个p标签里的文本内容,包含内部所有的后代标签文本
p标签里还包括了b标签的内容,这个语句可以把标签嵌套里的语句一并筛选出来。
3.获取从上往下的第一个标签里的文本内容,不包括后代标签文本
print(soup.p.string) # None p标签下的文本只有一个时取值,否则返回None
4.获取从上往下的第一个标签内部所有的属性,以字典数据类型显示
print(soup.a.attrs) # 获取从上往下的第一个a标签内部所有的属性,以字典数据类型显示
想要获取内部的数据就很方便,可以和字典的内置方法进行对照。
print(soup.a.attrs.get('href')) # 获取标签字典内指定的数据
这个方法可以简写,省略掉attrs参数一样能用。
print(soup.a.get('href')) # 可以直接简写,省略掉attrs参数
不过这么写会让其他人难于辨别语法,所以不是很推荐使用,了解即可。
5.获取标签内部所有的子标签(需要循环取值)
print(soup.p.children) # 获取p标签内部所有的子标签
生成的结果看不懂?莫慌,这一串字符本身是个迭代器,需要循环取值才能看到结果。
for i in soup.p.children: print(i)
许多时候,一个标签内部含有复数个子标签,所以这个语法中也用的是复数的children。只是范例中只有一个子标签,所以最后返还的结果也就只有一个。
6.获取标签内部所有的元素
print(soup.p.contents) # 获取p标签内部所有的元素
与上一个用法相比,这个倒是很直接了,直接到连空行都以换行符的形式展示给你看。
7.获取标签的父标签
既然可以追溯子孙,那么自然也可以追溯祖宗。
print(soup.p.parent) # 获取p标签的父标签
使用这个方法,不光是能追溯到父亲,还能继续往前追溯到祖父。
还可以继续追溯,查询曾祖父……一直查到整个嵌套的最表层。这就很像阿拉伯人的命名方式,只要愿意就可以从自己一路追溯到人类始祖阿丹(亚当)身上。
很显然,也可以直接追溯到所有的祖宗。不过直接生成的还是一个迭代器,需要循环取值才能看到“族谱”。
print(soup.p.parrents) # 获取标签的所有祖先标签
8.获取标签的同级标签
soup.p.next_siblings # 获取p标签的同级标签
与前面找子孙找祖宗的方法一样,找兄弟姐妹生成的也是一个迭代器。
bs4核心操作(重点)
基础操作讲完了,接下来就该了解bs4模块经常使用的重点功能了。主要有三种:find_all、find、CSS选择器。
1.find方法,该方法的返回结果是一个标签对象
1.查找指定标签名的标签
soup.find(name='a') #查找指定标签名为a的标签,默认只找第一个
2.查找具有某个特定属性的标签
soup.find(name='a',id='link2') # 查找具有某个特定属性的标签,默认只找符合条件的第一个
看起来似乎没问题了,我们试着找一下class为title的p标签:
报错了,原因是关键字冲突。
在python面向对象中我们学过类的概念,英语是class。这里我们要筛选的是html中的class。现在的问题就在于:电脑无法识别究竟是要找python中的类还是html中的类。
解决这一问题也很简单,只要在class后面加一条下划线就可以了。
soup.find(name='p',class_='title' # 为了解决关键字冲突,会加下划线区分
除此以外,还可以使用attrs参数输入键值对,直接避免关键字冲突。
soup.find(name='p',attrs={'class':'title'}) # 使用attrs参数输入键值对,直接避免关键字冲突。
3.成员运算
soup.find(name='a',attrs={'class':'sister'}) # class属性查找属于成员运算,有就行
soup.find(attrs={'class':'sister'}) # name字段不写则表示查找所有符合后续条件的标签
总结:find方法的共通缺点是只能找符合条件的第一个。
2.find_all
优势:查找所有符合条件的标签。
1.查找所有指定标签名的标签
soup.find_all(name='a') soup.find_all('a') # name字段可以省略,查找结果是一个列表
不难看出find_all使用方式与find一致,只是获取数量不一样。
3.select方法(需要使用css选择器)该方法的返回结果是一个列表
这个方法是利用了标签的层级关系来查找标签。
现在举个例子:
<p></p> <div> <p> <a></a> </p> <div><p></p></div> </div> <p></p> <p></p>
现在想要找到<div>下面一行的<p>,该怎么做?
1.标签选择器
直接书写标签名即可。
2.id选择器
d1 # 相当于写了id='d1'
3.class选择器
.c1 # 相当于写了class='c1'
4.儿子选择器(大于号),选择器可以混合使用
div>p # 查找div标签内部所有的子标签p
5.后代选择器(空格),选择器可以混合使用
div p # 查找div标签内部所有的后代p
练习
1.print(soup.select('.title')) # 查找class中含有title的标签
2.print(soup.select('.sister span')) # 查看类型中含有sister标签内部所有的后代span
3.print(soup.select('#link1')) # 查找id等于link1的标签
4.print(soup.select('#link1 span')) # 查找id等于link1标签内部所有的后代span
5.print(soup.select('#list-2 .element')) # 查找id等于list-2标签内部所有class为element的标签
6.print(soup.select('#list-2')[0].select('.element')) # 可以一直select,但其实没必要,一条select就行了
爬取红牛分公司数据
接下来我们来尝试使用bs4模块实战演练一下。
地址:http://www.redbull.com.cn/about/branch
需求:获取红牛所有分公司的详细数据。包括:名称、地址、邮箱、电话
思路:
研究网页
1.查看数据加载方式,得知是直接加载的。
2.朝该网页发送请求获取页面数据之后筛选即可。
bs4模块
起手势还是导入模块:
import requests from bs4 import BeautifulSoup
1.发送get请求获取页面内容
res = requests.get('http://www.redbull.com.cn/about/branch')
2.解析页面数据
soup = BeautifulSoup(res.text,'lxml')
3.研究标签特性,精确查找
1.获取分公司名称数据
h2_tag_list = soup.find_all('h2') # 查找到所有的h2标签对象 for tag in h2_tag_list: print(tag.text)
既然可以,那就可以把这三步合并在一起写成列表生成式。
h2_tag_list = soup.find_all('h2') com_list = [tag.text for tag in h2_tag_list]
下面的数据只要如法炮制就可以了。
2.获取分公司地址数据
addr_tag_list = soup.find_all(name='p',attrs={'class':'mapIco'}) for addr in addr_tag_list: print(addr.text)
同样可以写成列表生成式。
addr_tag_list = soup.find_all(name='p',attrs={'class':'mapIco'}) addr_list = [tag.text for tag in h2_tag_list]
3.获取分公司邮箱数据
mail_tag_list = soup.find_all(name='p',attrs={'class':'mailIco'}) for mail in mail_tag_list: print(mail.text)
写成列表生成式:
mail_tag_list = soup.find_all(name='p',attrs={'class':'mailIco'}) mail_list = [tag.text for tag in mail_tag_list]
4.获取分公司电话数据
tele_tag_list = soup.find_all(name='p',attrs={'class':'telIco'}) for tele in tele_tag_list: print(tele.text)
写成列表生成式:
tele_tag_list = soup.find_all(name='p',attrs={'class':'telIco'}) tele = [tag.text for tag in tele_tag_list]
到这一步所有的数据都已经获取到了,不过我们应该整理一下方便查看。
5.整合数据
for i in range(len(title_list)): print(""" 公司名称:%s 公司地址:%s 公司邮箱:%s 公司电话:%s """% (comm[i],addr[i],mail[i],tele[i]))