天天看點

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

Beautiful Soup是python的另一個HTML或XML解析庫,可以很友善的提取網頁中的資料,利用它可以省去很多繁瑣的提取工作,提高了解析效率。

首先從一個執行個體出發:

html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse'story</b></p>
<p class="story">Once upon a  time there were three little sisters; and their names were
<a href="http://example.com/elsie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link1"><!--- Elsie --></a>,
<a href="http://example.com/lacie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup =BeautifulSoup(html,'lxml')
print(soup.prettify())
print(soup.title.string)
           

運作結果:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

代碼分析:這裡首先聲明了html,并不是一個完整的HTML字元串,然後調用BeautifulSoup并初始化對象,調用prettify()方法把要解析的字元串以标準的縮進格式輸出。然後調用soup.title.string輸出HTML中title節點的文本内容。

節點選擇器

直接調用節點的名稱就可以選擇節點元素,在調用string屬性就可以得到節點内的文本了。

  1. 選擇元素
    print(soup.title)
    print(type(soup.title))
    print(soup.title.string)
    print(soup.head)
    print(soup.p)
               
    運作結果如下:當有多個節點時,列印第一個節點。
python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

2. 提取資訊

# 提取資訊\

#節點名稱
print(soup.title.name)
#節點屬性
print(soup.p.attrs)
print(soup.p["name"])
print(soup.p["class"])
# 節點内容
print(soup.p.string)
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

3. 嵌套選擇

上面的例子中,每一個傳回結果都是bs4.element.Tag類型,它同樣可以繼續調用節點進行下一步的選擇。

# 嵌套選擇
print(soup.head.title)
print(soup.head.title.string)
           

4. 關聯選擇

在做選擇的時候不能一步就選到想要的節點,需要選擇一個節點,然後以他為基準再選擇它的子節點、父節點、兄弟節點等。

(1) 子節點和子孫節點

選擇節點元素之後,調用contents屬性擷取它的直接子節點。

html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<p class="story">
Once upon a  time there were three little sisters; and their names were
<a href="http://example.com/elsie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link1"><!--- Elsie --></a>,
<a href="http://example.com/lacie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup =BeautifulSoup(html,'lxml')
#直接子節點
print(soup.p.contents)
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

可以看到,傳回結果是清單形式,p節點直接字典帶你既包含文本,又包含節點。可以調用children屬性得到響應的結果:

for i,child in enumerate(soup.p.children):
    print(i,child)
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

調用descendants屬性擷取所有子孫節點:

for i,child in enumerate(soup.p.descendants):
    print(i,child)
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

(2) 父節點和祖先節點

parent屬性可以擷取節點的父節點。

print(soup.a.parent)
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

擷取第一個a節點的父節點p。

parents屬性可以擷取節點的祖先節點。

print(type(soup.a.parents))
print(list(enumerate(soup.a.parents)))
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

(3) 兄弟節點

next_sibling和previous_sibling屬性擷取節點的下一個和上一個兄弟元素,next_siblings和previous_siblings屬性傳回節點所有前面的和後面的兄弟節點的生成器。

5. 提取資訊

文本string,屬性attrs。

html = """
<html><head><title>The Dormoues's story</title></head>
<body>
<p class="story">
Once upon a  time there were three little sisters; and their names were
<a href="http://example.com/lacie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link2">Lacie</a><a href="http://example.com/tillie" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup =BeautifulSoup(html,'lxml')
print('-'*15,'擷取資訊','-'*15)
# 擷取資訊
print(type(soup.a.next_sibling))
print(soup.a.next_sibling)
print(soup.a.next_sibling.string)
print(type(soup.a.parents))
print(list(soup.a.parents)[0])
print(list(soup.a.parents)[0].attrs['class'])
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

如果傳回的是單個節點,可以直接調用string、attrs等屬性擷取文本和屬性值;如果傳回結果是多個節點的生成器,則可以轉為清單取出某個元素,再調用string、attrs等屬性。

方法選擇器

前面所講的選擇方法都是通過屬性來選擇的,這種方法非常快,但是如果進行比較複雜的選擇就比較繁瑣,find_all()、find()方法可以靈活查詢。

1. find_all( )

查詢所有符合條件的元素,傳入一些屬性或文本,就可以的東岸符合條件的元素。

API:find_all(name,attrs,recursive,text,**kwargs)

html = """
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>
"""
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(name="ul"))
print(type(soup.find_all(name="ul")[0]))
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

# 同樣可以嵌套選擇

for ul in soup.find_all(name="ul"):
    print(ul.find_all(name="li"))
    for li in ul.find_all(name="li"):
        print(li.string)
        print(li.attrs)
           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

也可以通過屬性來選擇:

print(soup.find_all(attrs={'id':'list-1'}))
print(soup.find_all(attrs={'name':'elements'}))
           
python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

對于一些常用屬性,如id和class等,我們可以不用attrs來傳遞。

print(soup.find_all(id="list-1"))
print(soup.find_all(class_="element"))
           

Text參數可用來比對節點的文本,傳入的形式可以是字元串,也可以是正規表達式,

html = """
<div class="panel">
<div class="panel-body">
<a>Hello, this is a link</a>
<a>Hello, this is a link too</a>
"""
import re
soup = BeautifulSoup(html,'lxml')
print(soup.find_all(text=re.compile('link')))
           
python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

2. find( )

傳回第一個元素。

CSS選擇器

BeautifulSoup提供了CSS選擇器,隻需要調用select()方法傳入相應的CSS選擇器即可。

html = """
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1" name="elements">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>
"""
print('-'*15,'CSS選擇器','-'*15)
soup = BeautifulSoup(html,'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select("#list-2 .element"))
print(soup.select('ul')[0])

           

運作結果如下:

python3爬蟲學習筆記之解析庫的使用----BeautifulSoup篇(六)

這裡調用了3次CSS選擇器,傳回的結果是符合CSS選擇器節點組成的清單。#表示id選擇器,.表示class選擇器。

1. 嵌套選擇

for ul in soup.select('ul'):
    print(ul.select('li'))
           

2. 擷取屬性

for ul in soup.select('ul'):
    print(ul['id'])
    print(ul.attrs['id'])
           

3. 擷取文本

for li in ul.select('li'):
print(li.string)
print(li.get_text())
           

以上就是BeautifulSoup的學習,接下來可以通過一個作業來實踐一下:

利用requests和BeautifulSoup爬取某個靜态網頁的内容,這裡以上面那個例子為例子(上面是XPath解析庫解析)。(全部代碼見news.py)

網頁位址為:https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8

第一步和第二步檢視網頁和網頁源碼見上篇部落格。

第三步,完整代碼

import requests
from lxml import etree

def get_html(url):
    headers={"User-Agent": 
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0"}
    response = requests.get(url,headers=headers)
    if response.status_code==200:
        return response.text
    return None

from bs4 import BeautifulSoup    

def main1():
    url = 'https://3w.huanqiu.com/a/b1c82f/9CaKrnKmLPE?agt=8'
    html = get_html(url)
    soup = BeautifulSoup(html,'lxml')
    #标題
    print(soup.select('h1.a-title strong')[0].string)
    #時間
    print(soup.select('.a-info span.time')[0].string)
    #責編
    print(soup.select('.a-edit')[0].string)
    #内容
    #html
    print(soup.select('.content-a')[0].select('.a-con')[0])
    # text
    for p in soup.select('.content-a')[0].select('.a-con')[0].select('p'):
        print(p.string)
    
if __name__=='__main__':
    main1()
           

如果對你有用,點個贊  手動笑臉(*_*)

繼續閱讀