python爬虫之bs4模块

June 1, 2018 Python学习 访问: 36 次

美化网页数据

bs4简介

即BeautifulSoup,是python种的一个库,最主要的内容就是从网页中抓取数据。

官方解释:
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。
Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

使用方法

首先我们要导入bs4库

from bs4 import BeautifulSoup

然后我们利用requests或者urllib2来打开一个网址(这里用http://www.qq.com为例子来展开描述的)

import requests
url = "http://www.qq.com"
r = requests.get(url,'lxml')
print r.content

创造一个BeautifulSoup对象

bs_1 = BeautifulSoup(r.text,'lxml')

然后通过这个对象来实现对拔下来的源码进行筛选和处理

print ba_1.prettify() #格式化输出全部内容
print bs_1.[标签名]
有html,head,title,meta,body,script,style等等
分别输出标签对之间的代码

BeautifulSoup四大对象种类

Beautiful Soup 将复杂HTML文档转换成一个复杂的树形结构,每个节点都是 Python 对象,所有对象可以归纳为4种:
  1. Tag
  2. NavigableString
  3. BeautifulSoup
  4. Comment

tag

通俗的来讲就是HTML中的一个个标签,如html,head,title,meta,body,script,style等等,不过有一点是,它查找的是在所有内容中的第一个符合要求的标签,并不是所有的
对于每个标签来说,都一个共性,那就是都含有两个属性:name和attrs,用法如下:
print bs_1.[标签名][name/attrs]
eg:
    print bs_1.div.name    <!--div-->
    print bs_1.div.attrs    <!--{'style': 'display:none;overflow:visible;', 'id': 'QQ_takeover', 'class': ['l_qq_com']}-->
把div的所有属性都打印出来,返回的是一个字典类型的,若想取其中的某一个指,则可用:
print bs_1.div['class']  <!--['l_qq_com']-->

NavigableString

主要作用是把标签内部中的文字提取出来,用法如下:
print bs_1.title     #<title>腾讯首页</title>
print bs_1.title.string   # 腾讯首页
假如一个标签中的内容是注释过的
<!DOCTYPE html>
<html>
<head>
    <title>hahaha</title>
</head>
<body>
<a href="123456"><!--hahahahahah--></a>
</body>
</html>
那么:
print bs_1.a.string
返回的结果就是hahahahahah,并不输出注释符

BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容,和tag有点相似。

Comment

总的来说,Comment是一个特殊的NavigableString对象,当然也不输出注释符。

遍历文档树

标签的.contents可以将会标签的子节点以列表的方式输出
print bs_1.style.contents
输出结果:
[u'\r\n  .leftVideoNews .ft{height:168px;overflow:hidden;}\r\n  #jinrituhua{height:auto;}\r\n  #jinrituhua .bd{padding:12px 0 17px;}\r\n  #jinrituhua .ft{height:224px;overflow:hidden;}\r\n  #jinrituhua li{width:330px;padding:0;background:none;}  \r\n']
如果要取出列表中的某一个值的话,可以用索引来取出
print bs_1.style.contents[0]
结果:
  .leftVideoNews
  .ft{height:168px;overflow:hidden;}
  #jinrituhua{height:auto;}
  #jinrituhua .bd{padding:12px 0 17px;}
  #jinrituhua .ft{height:224px;overflow:hidden;}
  #jinrituhua li{width:330px;padding:0;background:none;}

标签的.children可以返回一个 list 生成器对象,而不是直接返回一个子节点

print bs_1.style.children
结果:
    <listiterator object at 0x0000000006DB9A90>

获取这个之后,我们可以通过遍历获取所有子节点

for child in  bs_1.style.children:
    print child
结果:
  .leftVideoNews .ft{height:168px;overflow:hidden;}
  #jinrituhua{height:auto;}
  #jinrituhua .bd{padding:12px 0 17px;}
  #jinrituhua .ft{height:224px;overflow:hidden;}
  #jinrituhua li{width:330px;padding:0;background:none;}
所有子孙节点---.descendants

.contents 和 .children 属性仅包含标签的直接子节点,.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容。

for child in bs_1.descendants:
    print child

从最大的标签对html开始,一层一层的开始列出所有的子节点

节点内容
单个内容

如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容

多个内容

同样也需要遍历

print bs_1.strings
结果:
<generator object _all_strings at 0x0000000005AF1EE8>
r = requests.get(url)
bs_1 = BeautifulSoup(r.text,'lxml')
for string in bs_1.strings:
    print(repr(string))
若取出来的有多行是空白内容,可以使用stripped_strings
for string in bs_1.stripped_strings:
    print(repr(string))

repr() 函数将对象转化为供解释器读取的形式。

父节点

单个父节点

.parent可以得到上一级的标签,通过标签可以访问上一级标签的name和attrs值

print bs_1.a.parent.name
结果:
    div

多个父节点

.parents可以得到所有的父节点,要输出那就得遍历

bs_1 = BeautifulSoup(r.text,'lxml')
for parent in  bs_1.title.parents:
    print parent.name
结果:
    head
    html
    [document]

兄弟节点(处在本级的节点)

.next_sibling可以获取到下一个的节点

print bs_1.p.next_sibling

.previous_sibling 可以获取到上一个的节点

print bs_1.p.previous_sibling

全部兄弟节点

.next_siblings 之后的兄弟节点
.previous_siblings 之前的兄弟节点

for sibling in bs_1.a.next_siblings:
    print(repr(sibling))

单个前后节点

.next_element可以获取到临近它的下一个节点,而不分层次关系
.previous_element可以获取到临近它的上一个节点

print bs_1.meta.next.element

多有的前后节点

.next_elements 该节点前的所有节点
.previous_elements 该节点后的所有节点

for element in last_a_tag.next_elements:
    print(repr(element))

从整个网站寻找

find_all(name,attrs,recursive,text,**kwargs)
naem
传字符串

print bs_1.find_all('a')
结果:
[<a class="qskinmouseover" href="http://v.qq.com/p/cross/20180524/Wlx2799t.html" id="qskinmouseover" target="_blank" title="">\n<img alt="\u516d\u4e00\u966a\u4f34\u65e5" src="//mat1.gtimg.com/www/qqskin/20180531_children.png"/>\n</a>, <a bosszone="logo" class="qqlogo" href="//www.qq.com" id="tencentlogo" target="_blank">\n<img alt="\u817e\u8baf\u7f51" src="//mat1.gtimg.com/www/qq2018/imgs/qq_logo_2018x2.png"/>\n</a>, <a href="https://www.sogou.com/?pid=sogou-wsse-3f7bcd0b3ea82268-0001" id="sogouLogoLink" onclick="registerZone2({bossZone:'searchlogo',url:''},1);" target="_blank"><img alt="\u641c\u72d7" class="sosoLogo" id="sogouLogo" src="//mat1.gtimg.com/www/images/qq2012/sogouSearchLogo20140629.png" style="width:23px;height:23px;"/></a>, <a bosszone="onekey" class="login" href="javascript:void(0)" id="loginGrayLayout" onclick="userLogin();" target="_self"></a>, <a bosszone="qmail" class="mail" href="http://mail.qq.com" target="_blank"></a>, <a bosszone="qzone" class="qzone" href="http://qzone.qq.com" target="_blank"></a>, <a bosszone="touxiang" class="logined" href="http://ilike.qq.com" id="loginGrayLayoutLogin" target="_blank">\n<img id="loginGrayLayoutImg" src=""/>\n</a>, <a bosszone="touxiang" class="userVipHead" href="http://ilike.qq.com" id="userVipHead" target="_blank"></a>]
传入正则表达式(使用re模块,这里不再详细列出)
传入列表
传入ture
传方法

等等

CSS选择器

css的规则:标签名不加任何修饰,类名前加点,id名前加#,在这里用的方法大致是一样的,用到的方法是bs_1.select(),它的返回值是一个列表list;

通过标签名字

print bs_1.select('title')

通过类名

print bs_1.select('.class')

通过id

print bs_1.select('#a_1')

组合查找

这个和在写css的格式是一样的

print bs_1.select('.class #id')
意思是查找.class类下的id是id的元素

子查询

print bs_1.select('head > title')

属性查找

print bs_1.select(p[class="new"])

总结

这里只是列出了部分的查找方法,还有很多的查找方法,详细请看官网。

添加新评论