1 html = """ 2 <html><head><title>The Dormouse's storytitle>head> 3 <body> 4 <p class="title" name="dromouse"><b>The Dormouse's storyb>p> 5 <p class="story">Once upon a time there were three little sisters; and their names were 6 <a href="http://example.com/elsie" class="sister" id="link1">a>, 7 <a href="http://example.com/lacie" class="sister" id="link2">Laciea> and 8 <a href="http://example.com/tillie" class="sister" id="link3">Tilliea>; 9 and they lived at the bottom of a well.p> 10 <p class="story">...p> 11 """
我们在写 CSS 时,标签名不加任何修饰,类名(class="className"引号内即为类名)前加点,id名(id="idName"引号前即为id名)前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list
(1)通过标签名查找
print soup.select('title') #[The Dormouse's story ] print soup.select('a') #[, Lacie, Tillie] print soup.select('b') #[The Dormouse's story]
(2)通过类名查找
print soup.select('.sister') #[, Lacie, Tillie]
(3)通过 id 名查找
print soup.select('#link1') #[]
(4)组合查找
组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开
print soup.select('p #link1') #[]
直接子标签查找
print soup.select("head > title") #[The Dormouse's story ]
(5)属性查找
查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。
print soup.select("head > title") #[The Dormouse's story ] print soup.select('a[href="http://example.com/elsie"]') #[]
同样,属性仍然可以与上述查找方式组合,不在同一节点的空格隔开,同一节点的不加空格
print soup.select('p a[href="http://example.com/elsie"]') #[]
BeautifulSoup支持CSS选择器是令人惊喜的,这大大方便了我们对特定标签的选取。那么接下来就来试验一下。
比如现在我们需要选取搜狗主页中导航栏中导向“知乎”的链接。
分析文档结构大概有这几个比较容易区分的层级特点:
<div class="top-nav">
<ul>
...
<li class="cur"><span>网页span>li>
href="http://zhihu.sogou.com/" uigs-id="nav_zhihu" id="zhihu">知乎a>li>
<li><a onclick="st(this,'40030500','pic')" href="http://pic.sogou.com" uigs-id="nav_pic" id="pic">图片a>li>
...
ul>
div>
鉴于如此的结构,我们可以使用类选择器选择“.top-nav”然后使用后代选择其选择“ul”标签和“li”标签,于是整个选择器可以写为“.top-nav ul li”,在代码中实现如下:
li = bs.select('.top-nav ul li')
print("result len is", len(li))
for tag in li:
print(tag)
得到如下输出:
result len is 10
<li><a href="http://news.sogou.com" id="news" onclick="st(this,'40030300','news')" uigs-id="nav_news">新闻a>li>
<li class="cur"><span>网页span>li>
<li><a href="http://weixin.sogou.com/" id="weixinch" onclick="st(this,'73141200','weixin')" uigs-id="nav_weixin">微信a>li>
<li><a href="http://zhihu.sogou.com/" id="zhihu" onclick="st(this,'40051200','zhihu')" uigs-id="nav_zhihu">知乎a>li>
<li><a href="http://pic.sogou.com" id="pic" onclick="st(this,'40030500','pic')" uigs-id="nav_pic">图片a>li>
...
但里面出现了“网页”这个没有链接到其他地址的项,要如何将之过滤掉呢。可以结合filter函数进行处理,如下:
def hasHttpLink(tag):
"""此时的tag为标签,需要取得其中的再来进行判断"""
return tag.a is not None and tag.a.get('href').startswith("http")
for tag in filter(hasHttpLink, li):
print(tag)
结果中已经过滤掉“网页”这项了:
<li><a href="http://news.sogou.com" id="news" onclick="st(this,'40030300','news')" uigs-id="nav_news">新闻a>li>
<li><a href="http://weixin.sogou.com/" id="weixinch" onclick="st(this,'73141200','weixin')" uigs-id="nav_weixin">微信a>li>
<li><a href="http://zhihu.sogou.com/" id="zhihu" onclick="st(this,'40051200','zhihu')" uigs-id="nav_zhihu">知乎a>li>
<li><a href="http://pic.sogou.com" id="pic" onclick="st(this,'40030500','pic')" uigs-id="nav_pic">图片a>li>
...
其中filter高阶函数进行过滤时还可以使用正则来进行匹配筛选。
不过一般情况下仅仅使用selector就能比较精确的获取结果集了,因此filter也不用写得很复杂。