Python 从零开始爬虫(二)——BeautifulSoup解析网页

学了requests,了解了伪装技巧后,终于能爬到些比较正常的网页源码(html文档)了,但这离结果还差最后和是最重要的一步——筛选。这个过程就像在泥沙中淘金一样,没有合适的筛子,你就会把有价值的漏掉了,或者做了无用功把没用的也筛了出来。
淘金者看土质,做筛子。对应到爬虫领域就是观察html,定制筛选器

稍稍了解HTML

信息都在网页源码里,浏览器通过解析源码来加载我们所看到的东西,那我们是不是也应该学下如何看源码呢?——是的

但不要方,这不是html语法课,做爬虫的,只需了解一下html的原理和标签关系就行了,这跟认亲戚一样简单,你会看家族树的话根本不成问题。

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

    <book>
      <title lang="en">Harry Potter</title>
      <author>J K. Rowling</author> 
      <year>2005</year>
      <price>29.99</price>
    </book>

</bookstore>


有时候会堆成一行,影响观察但不影响使用,丢到排版器排一下就好了
<bookstore><book><title lang="en">Harry Potter</title><author>J K. Rowling</author<year>2005</year><price>29.99</price></book></bookstore>

这是个非常短的示例html,但足以解释所有节点关系。

  • <bookstore>,<book>这些有尖括号的叫做标签(或节点),成对存在。bookstore,book是标签名,标签间可以放字符串。
  • 标签可以拥有属性,属性在尖括号里,如title标签有名为lang的属性,属性值为"en"。
  • A节点被B节点包起来,A就是B的子,或B是A的父。如book和title都是是bookstore的子,但是book是bookstore的直接子(只有一层包含关系)
  • 有同一个直接父的标签互相为兄弟,如title,author,year,price互为兄弟。

BeautifulSoup

惯例贴上官方文档
煲靓汤嘛!
把html文档转换为可定位的树结构,并提供索引,查找,修改(对爬虫没什么用)功能。
图片描述

安装

煲汤模块

  • ubuntu下:apt-get install Python-bs4
  • win下:pip install beautifulsoup4

如果你还需要使用第三方解释器lxml或html5lib,那也安装一下

  • apt-get install Python-lxml(/html5lib)
  • pip install lxml(/html5lib)

标签索引

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是一个对象,所有对象可以归纳为4种: Tag(标签) , NavigableString(字符串), BeautifulSoup(汤) , Comment(注释) , 知道就行.

  • BeautifulSoup类接收2个参数,第一个是html文档,第二个是解释器名,不写的话会自动选择。实例化后生成树结构,这里顺便附上各种解释器比较
    图片描述

    from bs4 import BeautifulSoup#注意不是直接导入beautifulsoup4
    r = requests(......)
    soup = BeautifulSoup(r.text,"lxml")
  • Tag(标签)类,包含该标签的所有内容,有多种方法用于索引元素/获取字符串(下面的tag,tag1指的是标签名soup相当于最大的Tag对象
  • 定位:标签定位,previous_sibling/next_sibling

    tag2 = soup.tag1.tag2#定位到tag1下的tag2,并将其返回
    tag4 = tag2.tag3.tag4,#标签对象可以继续向下定位
    brother_tag = tag.previous_sibling/next_sibling#定位到该tag的上一个/下一个兄弟标签并将其返回
    brother_tag = tag.previous_siblings/next_siblings#返回的是生成器,内含多个兄弟标签。
  • 索引: attrs,中括号索引

    dict1 = tag.attrs #以字典形式返回该tag下的元素及其对应值
    lang = tag["lang"] #返回该tag下的lang属性的值,和字典索引用法一样
  • 获取字符串: string,strings,get_text()

    string = tag.string #仅限于夹在该tag的字符串,不包子的字符串,将每段字符串无缝连接后返回。
    #如对`<a>A1<b>BB</b>A2<c>CC</c>A3<d>DD</d>A4</a>`使用a.string返回的是"A1A2A3A4"
    
    strings = tag.strings #以生成器形式返回该tag下(**不包括子**)的**每段字符串**,供for循环使用。
    strings = tag.stripped_strings #来先去除空行/空格再返回生成器
    
    text = tag.get_text(分隔符,strip=False) #返回该tag下(包括所有子孙)的字符串,同一个tag下的字符串无缝连接,不同tag下字符串间以指定分隔符连接,strip默认为False,改为True时自动去除空行/空格。
    
  • 以上的方法都是单次定位的,如果有多个符合标准的定位则以出现的第一个为准!!!
  • 索引和获取字符串的方法一定是最后用的,因为它不再返回Tag对象!!!

find_all&find方法

将它们单独出来是因为它真的太重要太灵活了,它们们能实现某个范围内的条件定位(不通过父子兄弟关系),返回Tag对象供使用各种索引方法。

  • find()的作用是实现范围内的单次条件定位
  • find_all()的作用是以可迭代的形式返回范围内多个符合要求的定位但是你要确保这个标签对你要的信息具有唯一性。

信息有时是分块的,这时定位也应分块,再对每个分块使用单次定位,下面是一个最常用的筛选器。

for each in soup.find_all(.......):#通常第一句就是用find_all进行分块
    string = each.find().string #然后在该分块中使用条件定位,再使用索引方法
    id = each.find()['id']
    ........

find和find_all接收的参数是一样的,现以find_all为例:Tag.find_all(name,attrs,recursive,limit,text,*kwards)

  • name:标签名,定位到指定标签名的节点如'a'

    • 想匹配多种标签名可传列表如['div','a','b']
    • 想自定标签名规则可传正则表达式如re.compile(规则)【正则后面再讲】
  • **kwards:其实是标签里的属性及其值,定位到有指定属性,属性值的标签,如.find_all(lang='en')

    • 如果属性叫class,为防止与类的“class”冲突要在后面加下横线,如.find_all(class_='sakura')
    • 可以设立多个条件如.find_all(class_='sakura',lang='en')
    • 属性值可以用传正则表达式如.find_all(lang=re.compile(r''))
  • recursive:默认True,检索当前tag下的所有子孙标签。设置为False时只检索当前tag下的直接子标签
  • limit:设置匹配上限,接收自然数,决定了有效定位的数量上限。
  • text:搜索文档中的字符串内容,返回匹配字符串的列表,可接收字符串,字符串列表和正则表达式(这个极少用到,通常不理)

到这里BeautifulSoup的主要用法都总结完了,全是文字没有实例可能会有些难啃,但是下一篇会放出几个实例出来让大家体验一下它的用法,到时就比较好理解了。
动手能力强的话,现在就可以开是捣鼓些小爬虫出来啦,加油,頑張って。
!!!!!!!!!!!!!记得常按F12看html源码啊!!!!!!!!!!!!!!

阅读 10.2k

推荐阅读

基于python3.X,主要使用requests进行自由定制,适合兴趣向的爬虫教学

63 人关注
11 篇文章
专栏主页