正文
(注:网页解析库的代码都比较通俗易懂,看理论讲解不如直接看代码,自己多写就能对常用方法了然于胸。本文是从整体框架上进行总结,更适合在对库有基本的了解之后再详细跟着文章思路查缺补漏。所以建议本文阅读顺序为:先不看文字,挑代码来看(这时挑简单的来看,看不懂的代码不要管),知道那些代码都是做什么的,自己动手写,多试错,然后看后面的实战。了解库的基本使用流程之后,能自己解析一些东西了,再跟着文章的思路把细节补上,之后就能运用自如了。)
beautifulsoup库应该是初学爬虫听的最多的一个解析库了,本文就来讲解一下这个库的用法。
本文分为如下几个部分
-
解析html流程说明
-
识别标签
-
提取内容
-
查看标签信息
-
其他提取细节
解析html流程说明
爬取网页信息,可以理解成从html代码中抽取我们需要的信息,而html代码由众多标签组成,我们要做的就分为两个部分
先说第二个,在找到对应标签位置后,我们要的信息一般会存储在标签的两个位置中
比如下面这行标签
<p><a href='www.wenzi.com'>文字</a></p>
一般要提取“文字”部分,或者那个链接
www.wenzi.com
部分
那么如何精确定位到标签呢?只能依靠标签名和标签属性来识别。下面一个例子提供识别的大致思路,之后会具体总结
<title>标题</title>
<body>
<ul class='list1'>
<li>列表1第1项</li>
<li>列表1第2项</li>
</ul>
<p class='first'>文字1</p>
<p class='second'>文字2</p>
<ul class='list2'>
<li>列表2第1项</li>
<li>列表2第2项</li>
</ul>
</body>
-
如果要提取“标题”,只需要使用
title
标签名来识别,因为只出现过一次
title
标签
-
如果要提取“文字1”,不能只使用
p
标签,因为“文字2”也对应了
p
标签,所以要用
p
标签且
class
属性是
'second'
来识别
-
如果“文字1”和“文字2”都要,就可以循环获取所有
p
标签提取内容
-
如果想提取列表1中的两项,就不能直接循环获取
li
标签了,因为列表2中也有
li
标签。此时需要先识别其父节点,先定位到
<ul class='list1'>
这个标签上(通过
ul
标签和
class
属性是
list1
定位)。此时在这个标签里,所有
li
都是我们想要的了,直接循环获取就可以了
这里展示一下使用beautifulsoup实现上述提取的代码,先对这个库的提取思路有一个大概的印象
a = '''<title>标题</title>
<body>
<ul class='list1'>
<li>列表1第1项</li>
<li>列表1第2项</li>
</ul>
<p class='first'>文字1</p>
<p class='second'>文字2</p>
<ul class='list2'>
<li>列表2第1项</li>
<li>列表2第2项</li>
</ul>
</body>'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(a, "html.parser")
soup.title.text # '标题'
soup.find('p', attrs={'class':'first'}).text # '文字1'
soup.find_all('p') # [<p class="first">文字1</p>, <p class="second">文字2</p>], 再分别从中提取文字
soup.find('ul', attrs={'class':'list1'}).find_all('li') # [<li>列表1第1项</li>, <li>列表1第2项</li>]
识别的大致思路就是这样。
从细节上看,一个完整解析库需要实现三个部分的功能
从提取内容上看
从识别标签上看
-
只根据标签来识别
-
找到名为a的标签(只有一个a标签时)
-
找到所有名为a的标签
-
找到名为a或b的标签
-
根据正则表达式提取标签
-
同时根据标签和属性识别
-
(要求在能识别标签的前提下,能相似地识别属性)比如
-
找到 标签名为a且B属性的值是b 的标签
-
找到 标签名为a且B属性的值是b且C属性是c 的标签
-
找到 标签名为a且B属性的值是b或c 的标签
-
找到 标签名为a且(不)包含B属性 的标签
-
找到 标签名为a且B属性的值包含b字符串(各种正则式匹配) 的标签
-
根据标签内内容来识别,也是类似如上划分,最好能用正则表达式匹配内容
-
根据位置识别
提供一些查看当前标签信息的方法,方便调试
-
获得标签名
-
获得标签所有属性及值
-
检查标签是否有某属性
除此之外这个库还有一些提取细节
-
多层嵌套标签问题
-
find_all
的简写方法
-
find_all
的其他参数
下面按照上述实现的功能顺序来讲解
(其实到最后会发现这个库唯一一个要重点掌握的方法是
find_all
)
首先加载库
from bs4 import BeautifulSoup
识别标签
本节分为如下几个部分
-
只根据标签来识别
-
根据标签和属性识别
-
根据标签内内容来识别
-
根据位置识别
只根据标签来识别
这部分分为如下几种情况
-
找到名为a的标签(查找唯一标签)
-
找到所有名为a的标签
-
找到名为a或b的标签
-
根据正则表达式提取标签
查找唯一标签时,beautifulsoup库中提供三种方法
-
对象的属性调用,直接提取该名字的标签,但是如果有很多该标签只能找到第一个
-
find
方法,也只能找到第一个
-
find_all
方法,找到所有该标签,返回一个list,如果只找到一个也是返回list,用
[0]
提取
a = '''
<h1>标题1</h1>
<h2>标题2</h2>
<h2>标题3</h2>
'''
# 将提取到的字符串转化为beautifulsoup的对象
soup = BeautifulSoup(a, "html.parser")
# 提取唯一标签
soup.h1
soup.find('h1')
soup.find_all('h1')[0]
# 上面三条结果都是
# <h1>标题1</h1>
如果要找到所有某标签,或者某几种标签及根据正则表达式提取,只能用
find_all
,返回一个列表
soup.find_all('h2')
# [<h2>标题2</h2>, <h2>标题3</h2>]
soup.find_all(['h1','h2'])
# [<h1>标题1</h1>, <h2>标题2</h2>, <h2>标题3</h2>]
# 使用正则表达式
import re
soup.find_all(re.compile('^h'))
# [<h1>标题1</h1>, <h2>标题2</h2>, <h2>标题3</h2>]
根据标签和属性识别
这部分分为如下几种情况
-
(要求在能识别标签的前提下,能用相似的方法识别属性)比如
-
找到 标签名为a且B属性的值是b 的标签
-
找到 标签名为a且B属性的值是b且C属性是c 的标签
-
找到 标签名为a且B属性的值是b或c 的标签
-
找到 标签名为a且(不)包含B属性 的标签
-
找到 标签名为a且B属性的值包含b字符串(各种正则式匹配) 的标签
要参考属性提取标签时,只有
find
和
find_all
两种方法,
find
总是返回找到的第一个,而
find_all
会返回所有,如果想要第一个就提取即可,因此这里全用
find_all
来讲,其实只是加一个
attrs
参数