面向监狱编程的开始,bs4的简单使用
Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
前置工作准备一个简单的html文档soup.html,并读取其中的内容
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet"
type="text/css"/>
<title>百度一下,你就知道 </title>
</head>
<body link="#0000cc">
<div id="wrapper">
<div id="head">
<div class="head_wrapper">
<div id="u1">
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新闻--></a>
<a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻</a>
<a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123</a>
<a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图</a>
<a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频</a>
<a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧</a>
<hdfs class="nameNode">Hadoop_Hdfs</hdfs>
<sql id="baseResultVo">自定义</sql>
<a class="bri" href="//www.baidu.com/more/soup.html" name="tj_briicon" style="display: block;">更多产品 </a>
<address>自定义</address>
<hdfs class="nameNode" size="1T">Hadoop_Hdfs</hdfs>
<hdfs class="dataNode" size="1T">Hadoop_Hdfs</hdfs>
</div>
<div id="u2">
<hdfs id="cdh" class="dataNode" size="1T">Hadoop_Hdfs</hdfs>
</div>
</div>
</div>
</div>
</body>
</html>
定义个列表遍历的公共函数,后续会用到一堆for很烦
def printList(targetList):
for item in targetList:
print(item)
将文档读取到内存中
# 以rb的方式读取文件
baiduFile = open("soup.html", "rb")
# 将流用UTF-8进行解码,获取文档内容
html = baiduFile.read().decode("UTF-8")
1 引入beautifulSoup依赖,并使用beautifulSoup解析文档对象
# 引入依赖
from bs4 import BeautifulSoup
# 使用html.parser解析器进行解析
bs4 = BeautifulSoup(html, "html.parser")
2 beautifulSoup 四个主要对象的使用
Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,所有对象可以归纳为 4 种:Tag
,NavigableString
,BeautifulSoup
和Comment
2.1 Tag
Tag 就是html文档中的一个个标签
print(bs4.title)
print(type(bs4.title))
<title>百度一下,你就知道 </title>
<class 'bs4.element.Tag'>
我们可以通过BeautifulSoup对象点对应的标签名获取到整个标签,打印类型可以看到类型是bs4.element.Tag对象,就是我们的标签,但是需要知道的是,每次只会返回匹配到的第一个标签
Tag对象有两个重要的属性,name和attrs,name就是标签名,演示其使用方式
# 输出meta的标签名
print(bs4.meta.name)
# 获取meta标签的所有属性
attrs = bs4.meta.attrs
# 查看返回值的类型
print(type(attrs))
# 输出所有属性
print(attrs)
meta
<class 'dict'>
{'content': 'text/html;charset=utf-8', 'http-equiv': 'content-type'}
看到控制态输出第一行为meta的标签名称,这里是直接选择了meta标签进行输出,意义不大,但是在文档遍历中,有时候会获取标签的名称进行判断,进行文档的查找;第二行为返回值对象的类型,是一个字典,最后一行为所有的属性和值,k-v形式呈现
2.2 NavigableString
NavigableString直译可遍历的字符串,前面我们已经可以获取到对应的标签了,我们可以通过便签的.string方法获取标签内容,返回的是一个bs4.element.NavigableString
对象
# 获取NavigableString对象
titleString = bs4.title.string
# 输出其类型
print("titleString的类型为:", type(titleString))
# 打印对应的值
print("titleString的值为:", titleString)
titleString的类型为: <class 'bs4.element.NavigableString'>
titleString的值为: 百度一下,你就知道
2.3 BeautifulSoup
BeautifulSoup其实就我们整个html文档对象
# 查看其类型
print(type(bs4))
# 查看标签名称
print(bs4.name)
# 查看所有属性
print(bs4.attrs)
<class 'bs4.BeautifulSoup'>
[document]
{}
BeautifulSoup的名称就是document, 可以把它理解为一个特殊的Tag,因为没有属性值,所以是一个空的字典
2.4 Comment
Comment是一个特殊的NavigableString,获取的方式和NavigableString一样是通过Tag.string获取,与NavigableString不同的是他会把标签内容的注释部分去掉,我们找一个带注释和不带注释的标签演示一下:
# 带注释的a标签
print(bs4.a)
print(type(bs4.a.string))
# 不带注释的title标签
print(bs4.title)
print(type(bs4.title.string))
<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新闻--></a>
<class 'bs4.element.Comment'>
<title>百度一下,你就知道 </title>
<class 'bs4.element.NavigableString'>
可以看到我们输出的都是标签内容,但是<a>标签里面的原始内容是"<! --新闻-->"但是在获取后输出的内容是把注释符去掉了的,并且它的类型是bs4.element.Comment
;而title标签因为原始内容没有添加注释,输出的事原始内容,并且类型是bs4.element.NavigableString
3 文档的遍历 contents
contents 可以获取某个Tag下的所有子标签,并且返回一个列表,我们获取<head>标签下的所有子便签进行演示:
# 文档遍历 contents
contents = bs4.head.contents
# 查看返回类型
print(type(contents))
# 螺旋for
for item in contents:
print(item)
<class 'list'>
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
<title>百度一下,你就知道 </title>
当然BeautifulSoup提供了大量的文档遍历的方法,例如获取父节点,兄弟节点.孙节点等,因为bs4可能使用更多的是文档搜索,所以这里就不一一展示了
4 文档搜索
4.1 字符串过滤 find_all("str")
字符串过滤会查找与字符串完全匹配的所有标签,上方通过标签名查找Tag的时候,只会返回匹配的第一个,并不会把所有的标签返回,而字符串过滤find_all则会返回完全命中的所有标签,我们以查找所有的<hdfs>
标签进行演示
# 字符串过滤,查找所有名称为hdfs的标签
aList = bs4.find_all("hdfs")
print(type(aList))
printList(aList )
<class 'bs4.element.ResultSet'>
<hdfs class="nameNode">Hadoop_Hdfs</hdfs>
<hdfs class="nameNode" size="1T">Hadoop_Hdfs</hdfs>
<hdfs class="dataNode" size="1T">Hadoop_Hdfs</hdfs>
<hdfs class="dataNode" id="cdh" size="1T">Hadoop_Hdfs</hdfs>
4.2 正则表达式搜索 find_all("re")
find_all()方法除了可以使用字符串进行查找外,还可以使用正则表达式进行搜索,因为要使用到正则表达式,所有需要先引入re库
# 引入re库
import re
# 正则表达式搜索,查找标签名称包含有q的字符的标签,find_all(regexp)
regex = re.compile("q")
qList = bs4.find_all(regex)
print(qList)
[<sql id="baseResultVo">自定义</sql>]
find_all()方法,无论是字符串匹配,还是正则表达式匹配,匹配的都是标签名称
4.3 自定义函数查找(个人觉得了解即可) find_all(method)
字符串查找和正则查找,都是通过简单的标签名匹配进行查找,如果想通过一些别的条件进行查找,我们可以使用自定义的函数来满足我们复查的查询逻辑
# 自定义函数查找,查找包含有size属性的标签
def findNameNode(tag):
return tag.has_attr("size")
# find_all入参替换为我们定义的函数
sizeList = bs4.find_all(findNameNode)
printList(sizeList)
<hdfs class="nameNode" size="1T">Hadoop_Hdfs</hdfs>,
<hdfs class="dataNode" size="1T">Hadoop_Hdfs</hdfs>,
<hdfs class="dataNode" id="cdh" size="1T">Hadoop_Hdfs</hdfs>
4.4 标签属性查找
find_all方法除了可以进行标签名查找之外,还可以进行属性的查找,可以针对标签是否包含某个属性名或者属性名加属性值进行查找
# 属性值查找,查找class值为nameNode的标签
nameNodeList = bs4.find_all(class_="nameNode")
printList(nameNodeList)
<hdfs class="nameNode">Hadoop_Hdfs</hdfs>
<hdfs class="nameNode" size="1T">Hadoop_Hdfs</hdfs>
# 属性包含查找,查找含有content属性的标签
contentList = bs4.find_all(content=True)
printList(contentList)
<meta content="text/html;charset=utf-8" http-equiv="content-type"/>
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
<meta content="always" name="referrer"/>
4.5 text参数查找(标签值查找)
除了标签名,标签属性查找,bs4还提供了text参数,对标签的值(字符串)进行查找o( ̄ヘ ̄o#)
# 查找包含某个字符串的的标签文本内容
textList = bs4.find_all(text="新闻")
printList(textList)
新闻
新闻
# 找标签文本中包含字符列表中某个元素值的文本
sourceList = ["地图", "视频"]
sourceResultList = bs4.find_all(text=sourceList)
printList(sourceResultList)
地图
视频
text="字符串"
是精准匹配的,完全匹配上才会返回命中的结果,这时我们会有疑问,我都知道了标签的内容,我还需要查找吗?真正的应用中并不会使用某个字符串对标签的文本内容进行查找,而是配合正则表达式进行text参数的查找,我们通过查找标签文本中包含数字的文本进行演示
# 查找满足正则表达式匹配规则的标签文本
textRegexp = re.compile("\d")
textRegexpList = bs4.find_all(text=textRegexp)
printList(textRegexpList)
hao123
\d
表示匹配0-9的数字,返回的结果是hao123
4.5 limit参数
limit见名知意截取,经常写sql的不会陌生,limit的参数与查询的规则无关,只控制结果集的大小,limit=2
就表示只返回前两个命中的结果,这里就不进行演示了
4.6 css选择器 select()
jQuery里的css选择器具备通过标签,或者属性,属性值快速定位某个标签的功能,BeautifulSoup同样具备这样的功能,bs4里面使用选择器就不是使用find_all()
方法了,而是通过select()
方法实现的,下面演示几个通过css选择器查找便签的小例子
通过标签名查找
# 通过标签名查找
tagNameList = bs4.select("title")
printList(tagNameList)
<title>百度一下,你就知道 </title>
通过类名查找
# 通过类名查找
dataNodeList = bs4.select(".dataNode")
printList(dataNodeList)
<hdfs class="dataNode" size="1T">Hadoop_Hdfs</hdfs>
<hdfs class="dataNode" id="cdh" size="1T">Hadoop_Hdfs</hdfs>
通过id进行查找
# 通过id进行查找
cdhList = bs4.select("#baseResultVo")
printList(cdhList)
<sql id="baseResultVo">自定义</sql>
通过属性进行查找
# 查找hdfs标签中,class属性为nameNode的标签
attrList = bs4.select("hdfs[class='nameNode']")
printList(attrList)
<hdfs class="nameNode">Hadoop_Hdfs</hdfs>
<hdfs class="nameNode" size="1T">Hadoop_Hdfs</hdfs>
通过子标签进行查找
# 通过子标签进行查找 >, 查找head子标签中的link标签
sonList = bs4.select("head > link")
printList(sonList)
<link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
通过兄弟标签进行查找
# 通过兄弟标签进行查找 ~,查找和名为meta,content=always的标签同层级的title标签
broList = bs4.select("meta[content='always'] ~ title")
printList(broList)
<title>百度一下,你就知道 </title>
ps:个人感觉select方法和find_all()配合正则的方式最好用
Beautiful Soup的api还有很多,本文只演示了较为常用和基础的api,bs4是需要好好熟悉的一个库,因为如何定位和查找你想要的标签是整个爬虫中提取信息非常重要的途径,相当于爬虫基础,值得我们好好花时间去熟悉.整体的api难度不是很大,比起Jsoup简单太多了,嗯...感觉后续还是需要多练习