解析库与Beautiful Soup   通过request库,我们已经能够抓取网页信息了,但要怎么提取包含在Html代码里面的有效信息呢?谈到匹配有效信息你肯定会想到正则表达式,这里就不讨论了,实际上关于正则表达式已经可以写一本书了,并且由于网页特殊的层级结构,也没必要使用正则表达式。python提供更好用的html和xml的解析库Beautiful Soup 和XPath等。
什么是Beautiful Soup Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间
———Beautiful Soup中文文档
安装 1 2 pip3 install beautifulsoup4 
 
学习准备 为了学习和测试Beautiful Soup,我写了一个简单的程序框架,包含3个函数获取页面,解析页面,和主函数,测试的时候只要修改解析函数的部分代码就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import  requestsfrom  bs4 import  BeautifulSoupdef  getHtml (url) :        try :                    r = requests.get(url,timeout = 30 )         r.encoding = r.apparent_encoding;          return  r.text     except :         return  ""  def  soupHtml (html) :       print(html)     soup = BeautifulSoup(html,'lxml' )           if  __name__ == '__main__' :    url = "http://www.baidu.com"      html = getHtml(url)     soupHtml(html)      
 
可以看到爬取的是百度的首页,之后的代码,添加进去就能运行了
基本用法 1 2 BeautifulSoup (content,"html.parser" /"lxml" ) 
 
两个参数cnetent表示待解析的网页内容,后面的参数可以理解为待解析的格式。lxml解析器有解析HTML和XML的功能,而且速度快,容错能力强,所以推荐使用它。
选择元素 根据标签名来选择元素
1 2 3 4 5 6 soup = BeautifulSoup(html ,'lxml' ) print (soup.title) print (soup.head) print (soup.div) >>> <title>百度一下,你就知道</title> 
 
连带标签一起输出,当有多个标签匹配时,只返回第一个
获取内容 标签的string方法
1 2 3 4 5 6 soup = BeautifulSoup(html ,'lxml' ) print (soup.title.string) print (soup.head.string) print (soup.div.string) >>> 百度一下,你就知道 
     
获取标签的属性 html中的标签有很多属性,例如,id,name,class,href等等,可以通过attrs 获取标签的属性
1 2 3 4 5 soup = BeautifulSoup(html ,'lxml' ) print (soup.link.attrs) >>> {'rel' : ['stylesheet' ], 'type' : 'text/css' , 'href' : 'http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css' } 
 
获取属性的值 通过返回的值,可以知道它是字典类型的结构,这样就可以通过键获取他的值
1 2 3 4 5 6 7 8 9 10 soup = BeautifulSoup(html ,'lxml' ) print (soup.p) >>> <p  id="lh" ><a  href="http://www.baidu.com/cache/sethelp/help.html"  target="_blank" >把百度设为主页</a><a  href="http://home.baidu.com" >关于百度</a><a  href="http://ir.baidu.com" >About Baidu</a></p> print (soup.p.attrs) >>> {'id' : 'lh' } print (soup.p.attrs['id' ]) >>>lh 
 
关联选择 在做选择的时候,有时候不能做到一步就选到想要的节点元素,需要先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等
1.子节点和子孙节点 
1 2 3 4 5 6 7 8 9 10 11 12 for i,content in enumerate(soup.head.contents):     print(i,content) for i,child in enumerate(soup.head.children):     print(i,child) >>>0 <meta  content ="text/html;charset=utf-8"  http-equiv ="content-type" />  1 <meta  content ="IE=Edge"  http-equiv ="X-UA-Compatible" />  2 <meta  content ="never"  name ="referrer" />  3 <title > 百度一下,你就知道</title >  4 <style > html ,body {height :100% }html {overflow-y :auto}body {font :12px  arial;background :#fff }body ,p ,form ,ul ,li {margin :0 ;padding :0 ;list-style :none}body ............ 
 
输出head标签的子节点,contents和children是等效的
如果要得到所有的子孙节点的话,可以调用descendants属性
1 2 3 4 for  i ,child in  enumerate(soup.body .descendants ):    print(i ,child)      
 
2.父节点和祖先节点 如果要获取某个节点元素的父节点,可以调用parent属性,获取其祖先节点调用其parents属性。
3.兄弟节点 next_sibling和previous_sibling分别获取节点的下一个和上一个兄弟元素,next_siblings和previous_siblings则分别返回所有前面和后面的兄弟节点的生成器。
查询方法 1.find_all() find_all查询所有符合条件的元素。给它传入一些属性或文本,就可以得到符合条件的元素了。
API接口如下:
1 2 3 find_all(name  , attrs , recursive , text , **kwargs) 
 
1 2 3 4 5 6 7 8 9 10 11 12 for  i,input in  enumerate(soup.fiind_all(name ='input' )):    print (i,input) >>> 0 <input name ="bdorz_come"  type ="hidden"  value ="1" /> 1 <input name ="ie"  type ="hidden"  value ="utf-8" /> 2 <input name ="f"  type ="hidden"  value ="8" /> 3 <input name ="rsv_bp"  type ="hidden"  value ="1" /> 4 <input name ="rsv_idx"  type ="hidden"  value ="1" /> 5 <input name ="tn"  type ="hidden"  value ="baidu" /> 6 <input autocomplete ="off"  autofocus =""  class ="s_ipt"  id ="kw"  maxlength ="255"  name ="wd"  value ="" /> 7 <input class ="bg s_btn"  id ="su"  type ="submit"  value ="百度一下"  
 
找出所有的input标签
1 2 3 4 5 6 7 8 9 10 11 12 13      for  i ,tag in  enumerate(soup.find_all(attrs={'class' :'mnav' })):    print(i ,tag)      >>> 0  <a  class="mnav"  href="http://www.nuomi.com" >糯米</a>     1  <a  class="mnav"  href="http://news.baidu.com" >新闻</a>     2  <a  class="mnav"  href="http://www.hao123.com" >hao123</a>     3  <a  class="mnav"  href="http://map.baidu.com" >地图</a>     4  <a  class="mnav"  href="http://v.baidu.com" >视频</a>     5  <a  class="mnav"  href="http://tieba.baidu.com" >贴吧</a>     6  <a  class="mnav"  href="https://passport.baidu.com/v2/?login&tpl=mn&u=https%3A%2F%2Fwww.baidu.com%2F" >登录</a>     7  <a  class="mnav"  href="http://www.baidu.com/gaoji/preferences.html" >设置</a>      
 
找出所有class属性为”mnav”的标签
1 2 3 4 5 6 7 import  re# 导入正则匹配库 print (soup.find_all(text =re.compile('百' )))['百度一下,你就知道' , '把百度设为主页' , '关于百度' , '使用百度前必读' ] 
     
2.find() find()方法与find_all(),方法一致,只是find()返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表。这里就不讨论了
CSS选择器 就是专门用于筛选指定样式的标签,通过select方法
1 2 3 4 5 6 7 8 9 10 11 12 print (soup.select('.mnav' )  )for  i ,tag in  enumrate(soup.select('.mnav' )):    print(i ,tag) >>>0  <a  class="mnav"  href="http://news.baidu.com"  name="tj_trnews" >新闻</a> 1  <a  class="mnav"  href="http://www.hao123.com"  name="tj_trhao123" >hao123</a>2  <a  class="mnav"  href="http://map.baidu.com"  name="tj_trmap" >地图</a>3  <a  class="mnav"  href="http://v.baidu.com"  name="tj_trvideo" >视频</a>4  <a  class="mnav"  href="http://tieba.baidu.com"  name="tj_trtieba" >贴吧</a>
 
通过结果我们发现其实完全可以通过class属性来查找,应为通过CSS来查找本质就是通过class属性来查找
程序实现 爬取最好大学网2018年排名信息,并格式化输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <tbody class  ="hidden_zhpm"  style="text-align: center;" >   <tr class  ="alt" >    <td>     1     </td>     <td>     <div align="left">      清华大学     </ div>   </td>     <td>     北京    </ td>   <td>     95.3     </td>     <td class="hidden-xs need-hidden indicator5">     100.0    </ td>    
          
1.所有大学信息包含在tbody 的子标签中,即整个表格
2.每一所大学在一个tr 标签中,即表格中的一行
3.各大学的排名,名称,综合评分,在td 标签中,即表格的一列
request + BeautifkuSoup实现 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import  requestsfrom  bs4 import  BeautifulSoupimport  bs4def  getUnivtext (url) :    try :         r=requests.get(url,timeout=30 );         r.raise_for_status()         r.encoding=r.apparent_encoding;         return  r.text     except :         return  ""  def  fillUnivList (ulist,html) :    soup=BeautifulSoup(html,"html.parser" )     for  tr in  soup.find('tbody' ).children:         if  isinstance(tr,bs4.element.Tag):             tds=tr('td' )             ulist.append([tds[0 ].string,tds[1 ].string,tds[3 ].string]) def  printUnivList (ulist,num) :    print("{:^10}\t{:<10}\t{:<10}\t" .format("排名" ,"大学名称" ,"评分" ))     for  i in  range(num):         u=ulist[i]         print("{:^10}\t{:^6}\t{:^10}\t" .format(u[0 ],u[1 ],u[2 ]))       if  __name__ == '__main__' :    Uinfo=[]     url="http://www.zuihaodaxue.com/zuihaodaxuepaiming2018.html"      html=getUnivtext(url)     fillUnivList(Uinfo,html)     printUnivList(Uinfo,310 )      
         
运行效果 
投喂我 写文不易,如果本文对你有帮助,点击Donate,微信和支付宝投喂我