python 网络爬虫 - BeautifulSoup 爬取网络

作者: 查德笔记 | 来源:发表于2018-02-25 08:10 被阅读126次

    0. 前言

    在介绍BeautifulSoup模块前, 我们先分析一下我们要爬取的网页结构是什么样的。通常网页都包含层叠样式表(英文全称:Cascading Style Sheets),例如。 推荐使用谷歌浏览器或者火狐浏览器方便快捷查看网页结构。例如在chrome中百度首页右击,选择'inspect',即可查看到网页结构,以及各个标签层级关系。

    image

    1. 创建爬虫爬取网页

    爬取网站:url =www.pythonscraping.com/pages/warandpeace.html

    网页如图所示,有红色和绿色字体。在绿色字体处右键选择“inspect”分析标签结构可知。绿色字体均包含在标签GreenText当中。

    image

    1.1 抓取网页

    
    from urllib.request import urlopen
    
    from bs4 import BeautifulSoup
    
    url ='http://www.pythonscraping.com/pages/warandpeace.html'
    
    html= urlopen(url) #抓取了该url网页
    
    soup = BeautifulSoup(html) #使用BeautifulSoup对网页进行解析
    
    name_list = soup.find_all("span",{'class': 'green'})#find_all抓取所有绿色字体,返回list
    
    for name in name_list:
    
        print(name.get_text()) #get_text()函数剔除字符串中所有tag符号只保留tag中包含的文本
    
    

    2. find() 和 find_all()

    推荐有能力的各位查看BeautifulSoup官方文档,这里简单讲解一下。
    请看以下比较:

      find_all(tag, attributes, recursive, text,limit, keywords)
    # find_all(标签, 属性, 递归, 文本,限制查询数量, 关键字)
      find(tag,attributes, recursive, text,keywords)
    #find 相当于find_all(,limit=1)
    

    绝大多数的情况我们只会遇到前两个参数,tag和attributes。tag和attributes都可以查找多个值。

    
    from urllib.request import urlopen
    
    from bs4 import BeautifulSoup
    
    url ='http://www.pythonscraping.com/pages/warandpeace.html'
    
    html= urlopen(url) #抓取了该url网页
    
    soup = BeautifulSoup(html) #使用BeautifulSoup对网页进行解析
    
    hs = soup.find_all({'h1', 'h2'})#find_all抓取所有绿色字体,返回list
    
    print(hs)
    
    

    得到结果:

    [<h1>War and Peace</h1>, <h2>Chapter 1</h2>]
    

    同理,属性参数也可以包含多个属性。例如需要查找所有绿色和红色的文本:

    ....
    words = soup.find_all('span', {'class':{'green', 'red'}})
    print(len(words))
    

    有兴趣的朋友可以看看绿色和红色的tag分别有多少个。

    关键字参数可以用来选择包含特定属性的是标签,比如:

    all_text = soup.find_all(id = 'text')
    print(all_text[0].get_text()
    

    细心的朋友可能会注意到,其实关键字参数匹配完全可以用属性参数替换。

    soup.find_all(id='text')
    soup.find_all("",{"id":"text"})
    soup.find_all(class="green")
    soup.find_all('',{'class':'green'})
    

    注意: 在BeautifulSoup4版本中find_all 和findAll 是一样的。find_all是新版本的写法,findAll是旧版本的写法,但是目前二者在版本4中通用。

    3. 1子节点和子孙节点

    soup.body.h1# 选中body 标签下的h1,这个h1 标签是body标签的子节点
    

    同理,soup.div.find_all('img')会找到所有div里面的img标签。
    .children 和.descendants
    对比代码如下:

    html = urlopen('http://www.pythonscraping.com/pages/page3.html')
    soup = BeautifulSoup(html, 'lxml')
    children = soup.find('table',{'id':'giftList'}).children
    descendants = soup.find('table',{'id':'giftList'}).descendants
    sum = 0
    for child in children:
        print(child)
        sum +=1
    print(sum)
    sum2 = 0
    for descendant in descendants:
        sum2+=1
        print(descendant)
    print(sum2)
    

    运行结果可知 sum = 13, sum2 = 86
    取descendants的第一部分作比较可以发现

    <tr><th>#=============<tr>是soup.find('table',{'id':'giftList'})的子节点====
    Item Title
    </th><th>
    Description
    </th><th>
    Cost
    </th><th>
    Image
    </th></tr>#============<tr>是soup.find('table',{'id':'giftList'})的子节点====
    <th>        #============<th>是<tr>的子节点,('table',{'id':'giftList'})的子孙节点==
    Item Title
    </th>       #============<th>是<tr>的子节点,('table',{'id':'giftList'})的子孙节点==
    
    Item Title#=========文本是<th>标签的内容,也是子孙节点================
    
    <th>#============同上====================
    Description
    </th>
    
    Description
    
    <th>
    Cost
    </th>
    
    Cost
    ....
    

    对比可知,children只列出了<tr>标签所包含的内容。而descendants列出了所有包含的标签节点以及文本,即<tr>子标签中的所有子子孙孙标签都会查找返回。

    3.2 父节点

    通常情况下我们更经常查找子节点,而在某些特定情况下会用到查询父节点,.parents 和 .parent。

    from urllib.request import urlopen
    from bs4 import BeautifulSoup
    
    html = urlopen('http://www.pythonscraping.com/pages/warandpeace.html')
    soup = BeautifulSoup(html)
    print(soup.find('img', {'src':'../img/gifts/img1.jpg'}).parent.previous_sibling.get_text())
    

    分析一下代码是如何工作的。

    <tr>
    --<td>
    --<td>(3)
        --"$15.00"(4)
    --s<td>(2)
        --<img src="../img/gifts/img1.jpg">(1)
    

    1.首先定位到含src="../img/gifts/img1.jpg"的标签img。
    2.选中img标签的父节点s<td>.
    3.选中s<td>的上一个同层级标签<td>
    4.选取<td>标签中的文字

    4. 处理同辈节点和父辈节点
    BeautifulSoup的next_siblings()函数非常适用于表格查找,尤其是带有标题的表格。

    image.png
    from urllib.request import urlopen
    from bs4 import BeautifulSoup
    
    
    html = urlopen("http://www.pythonscraping.com/pages/page3.html")
    soup = BeautifulSoup(html, 'lxml')
    
    siblings = soup.find("table",{'id':'giftList'}).tr.next_siblings
    sum = 0
    for sibling in siblings:
        print(sibling)
        sum+=1
    print(sum)
    

    结果为:

    
    
    <tr class="gift" id="gift1"><td>
    Vegetable Basket
    </td><td>
    This vegetable basket is the perfect gift for your health conscious (or overweight) friends!
    <span class="excitingNote">Now with super-colorful bell peppers!</span>
    </td><td>
    $15.00
    </td><td>
    <img src="../img/gifts/img1.jpg"/>
    </td></tr>
    
    
    <tr class="gift" id="gift2"><td>
    Russian Nesting Dolls
    </td><td>
    Hand-painted by trained monkeys, these exquisite dolls are priceless! And by "priceless," we mean "extremely expensive"! <span class="excitingNote">8 entire dolls per set! Octuple the presents!</span>
    </td><td>
    $10,000.52
    </td><td>
    <img src="../img/gifts/img2.jpg"/>
    </td></tr>
    
    ...
    
    11
    0
    [Finished in 2.2s]
    

    代码输出产品表中的所有产品,除了首行标题。因为:
    1. 查找对象本身不是自己的同辈,因此使用sibling相关函数时查找对象都会被跳过。
    2.代码使用的是next siblings,因此会返回查找对象的下一个(些)同辈节点。

    补充:除了next_siblings,记住previous_siblings经常用来查找已知最后一行容易定位且不需要抓取的情况。当然,next_sibling 和 previous_sibling 可以用来查找一个同辈节点。

    相关文章

      网友评论

        本文标题:python 网络爬虫 - BeautifulSoup 爬取网络

        本文链接:https://www.haomeiwen.com/subject/twgkxftx.html