Beautiful Soup 和 lxml 一样,是Python的一个HTML/XML的解析库,它可以借助网页的结构和属性等特性来解析网页。它和lxml有相当的功能,也有着不同之处:lxml只会局部遍历,而Beautiful Soup 是基于HTML DOM(Document Object Model)的,会载入整个文档来解析DOM树,因此时间和内存开销都会大很多,性能要低于lxml。
如下为一个解析器对比(包含正则):
解析工具 | 解析速度 | 使用难度 |
Beautiful Soup | 最慢 | 最简单 |
lxml | 快 | 简单 |
正则 | 最快 | 最难 |
即便如此,Beautiful Soup 的解析使用相对简单,其API非常人性化,支持CSS选择器、Python标准库HTML解析器,同时也支持 lxml 的 XML解析器,甚至容错性最好的html5lib解析器。所以说,利用好Beautiful Soup可以省去很多工作,提高解析效率。
安装方法
使用pip进行安装:pip install bs4
基本使用
1.使用基础
使用前需要先导入BeautifulSoup模块(属于bs4库),然后使用Beautiful(html,parse)实例化对象,传入的参数分别为待解析的html源码文本和解析器种类(如lxml,html.parse,xml或html5lib)。当实例化对象soup后,接下来就可以调用其各个方法和属性解析HTML代码了。
html = """ <table class="tablelist" cellpadding="0" cellspacing="0"><tbody> <tr class="even"> <td class="l square"><a target="_blank" href="position_detail.php?id=44279&keywords=python&tid=0&lid=0">28607-113 微信支付大数据平台开发工程师(深圳)</a></td> <td>技术类</td> <td>1</td> <td>深圳</td> <td>2018-09-18</td> </tr> <tr class="odd"> <td class="l square"><a target="_blank" href="position_detail.php?id=44274&keywords=python&tid=0&lid=0">OMG097-自然语言处理工程师(北京)</a></td> <td>技术类</td> <td>3</td> <td>北京</td> <td>2018-09-18</td> </tr> <tr class="even"> <td class="l square"><a target="_blank" href="position_detail.php?id=44263&keywords=python&tid=0&lid=0">18428-银行业务测试工程师(深圳)</a><span class="hot"> </span></td> <td>技术类</td> <td>1</td> <td>深圳</td> <td>2018-09-18</td> </tr> </tbody></table> """
##前期准备 from bs4 import BeautifulSoup # 实例化对象 soup = BeautifulSoup(html, 'lxml')# 美观地输出HTML源代码文本print
(bs.prettify())
2.find()与find_all()方法
这两个都是Soup解析方法中较为常用的用于检索获取特定节点的方法,其中find()方法用于获取一个节点,返回值为单个的Tag标签(如果存在,否则为None,当有多个满足时只返回第一个),而find_all()则是在find()的基础上返回所有符合条件的节点列表(没有满足的情况下返回空列表[])。
通常情况下,find_all()用得更为频繁,基本语法为:object = soup.find_all(Tag),下面以一系列示例来说明其用法。
##使用find_all()检索获取节点标签 from bs4 import BeautifulSoup html="""如上示例""" soup = BeautifulSoup(html, 'lxml') # 示例1——获取所有tr标签 trs = soup.find_all('tr') for tr in trs: print(type(tr)) #<class 'bs4.element.Tag'> print(tr) # 示例2——获取第2个tr标签 tr = soup.find_all('tr',limit=2)[1] #参数limit用于限制返回个数为2个,由于是列表结构,第二个下标为1 print(tr) # 示例3——获取所有class为even的tr标签 trs = soup.find_all('tr',attrs={'class':'even'}) #参数attrs用于属性过滤 # 示例4——将所有id等于test,class也等于test的a标签提取出来 aList = soup.find_all('a',attrs={'class':'test','id':'test'}) # 示例5——获取所有a标签的href属性 aList = soup.find_all('a') for a in aList: href = a['href'] # 或href = a.attrs['href'],attrs为其属性字典 print(href)
3.select()方法
此方法为CSS选择器方法,即通过元素CSS选择表达式来筛选符合条件的节点,其返回的也是为一个列表。使用此方法前需要对CSS有一定的了解,在爬虫中我们只需要掌握以下几类基本的用法即可,其他具体的可以查阅相关网络资料(比如,http://www.w3school.com.cn/cssref/css_selectors.ASP)
选择器 | 构成法 | 使用说明 | 示例 | 描述 |
通过标签名选择 | tag | 直接输入待选择的标签名 | div | 选择所有的div标签 |
通过类名选择 | .class | 在类名前面加一个. | p.text | 选择所有类为text的p标签 |
通过id选择 | #id | 在id名前加一个#号 | #username | 选择id为username的标签 |
组合选择1(全局选择) | 将多个选择器方法叠加一起,同时用空格分开 | p #poster | 选择id为poster的p标签 | |
组合选择2(特定选择) | 当需要使用组合来选择直接子节点时,需要用’>’连接 | div > input | 选择div下的直接input标签 | |
通过属性选择 | [attrs=value] | 通过在[]中指定属性表达式来选择,但不适用多属性 | a[id=”link”] | 选择id为link的a标签 |
在熟悉CSS选择器基本语法规则后,我们便可以通过soup的select(选择表达式)方法来获取相应的节点标签了。以上的 select 方法返回的结果都是列表形式,可以遍历或通过下标获取节点元素,然后用使用 get_text()(返回子孙非标签字符串) 方法来获取其文本内容。
下面仍以上述html代码为例,介绍select()的使用方法。
##使用select()方法检索获取节点 from bs4 import BeautifulSoup html="""如上示例""" soup = BeautifulSoup(html, 'lxml') # 示例1——获取所有tr标签 trs = soup.select('tr') for tr in trs: print(tr) # 示例2——获取第2个tr标签 tr = soup.select('tr')[1] print(tr) # 示例3——获取所有class为even的tr标签 trs = soup.select('tr[class="even"]') for tr in trs: print(tr) # 示例4——将所有id等于test,class也等于test的a标签提取出来 #由于CSS选择器无法直接选取同时满足两种条件的标签,故此目标无法完成,但可以通过二次选择来获取 # 示例5——获取所有a标签的href属性 aList= soup.select('a') for a in aList: href = a['href'] print(href)
4.获取文本内容
在soup获取后的节点对象中,如要获取其文本内容,除了上文中提到的get_text()方法外,还可以使用如下的属性:
(1)string:获取某个标签下的非标签字符串,返回的为字符串。
(2)strings:获取某个标签下的子孙非标签字符串,返回的为生成器。
(3)stripped_string:获取某个标签下的子孙非标签字符串,会去掉空白字符,返回的为生成器。
仍以上述为例,使用示例如下:
##获取文本内容 tr2 = soup.select('tr')[1] # 获取非标签字符串 print(tr2.string) #None # 获取子孙非标签字符串 print(list(tr2.strings)) #[' ', 'OMG097-自然语言处理工程师(北京)', ' ', '技术类', ' ', '3', ' ', '北京', ' ', '2018-09-18', ' '] # 获取除去空白的子孙非标签字符串 print(list(tr2.stripped_strings)) #['OMG097-自然语言处理工程师(北京)', '技术类', '3', '北京', '2018-09-18']
# 获取子孙非标签字符串,以普通字符串形式返回 print(tr2.get_text()) """ Output: OMG097-自然语言处理工程师(北京) 技术类 3 北京 2018-09-18 """
补充内容
1.Beautiful Soup中四个常用对象:
(1)Tag
即HTML中的一个个节点标签,如head、body、title、div、p和input等等。
(2)NavigatableString
获取标签后,我们利用其属性(如string属性)从标签得到它的内容所返回的这个对象就时NavigatableString。
(3)BeautifulSoup
BeautifulSoup 对象表示的是一个文档的全部内容,大部分时候可以把它当作 Tag 对象,其支持遍历文档树和搜索文档树中描述的大部分的方法,比如我们之前使用soup就是这种对象。
(4)Comment
Tag , NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是有一类特殊对象——注释,而Comment就是用于获取这些内容的对象。实际上,Comment 对象是一个特殊的 NavigableString 对象。
html = "<p><!--Hello World--></p>" soup = BeautifulSoup(html) comment = soup.b.string print(comment) # ‘Hello World’
2.contents和children属性遍历文档树
在我们获取标签节点后,还可以通过contents和children两个属性来获取其下的子节点,二者的区别在于contents返回所有子节点列表,而children返回的是一个迭代器。
tr2 = soup.find_all('tr')[1] # 返回所有子节点的列表 print(tr2.contents) """ Output: [' ', <td class="l square"><a href="position_detail.php?id=44274&keywords=python&tid=0&lid=0" target="_blank">OMG097-自然语言处理工程师(北京)</a></td>, ' ', <td>技术类</td>, ' ', <td>3</td>, ' ', <td>北京</td>, ' ', <td>2018-09-18</td>, ' '] """ # 返回所有子节点的迭代器 print(tr2.children) """ Output: <list_iterator object at 0x00000224FFF01518> """