python爬蟲之Beautiful Soup基礎知識+例項

Monste發表於2020-08-12

python爬蟲之Beautiful Soup基礎知識

Beautiful Soup是一個可以從HTML或XML檔案中提取資料的python庫。它能通過你喜歡的轉換器實現慣用的文件導航,查詢,修改文件的方式。

需要注意的是,Beautiful Soup已經自動將輸入文件轉換為Unicode編碼,輸出文件轉換為utf-8編碼。因此在使用它的時候不需要考慮編碼方式,僅僅需要說明一下原始編碼方式就可以了。

一、安裝Beautiful Soup庫

使用pip命令工具安裝Beautiful Soup4庫

pip install beautifulsoup4

二、BeautifulSoup庫的主要解析器

解析器 使用方法 條件
bs4的html解析器 BeautifulSoup(markup, 'html.parser') 安裝bs4庫
lxml的html解析器 BeautifulSoup(markup, 'lxml') pip install lxml
lxml的lxml解析器 BeautifulSoup(markup, 'lxml') pip install lxml
html5lib的解析器 BeautifulSoup(markup, 'html5lib') pip install html5lib

具體操作:

html = 'https://www.baidu.com'
bs = BeautifulSoup(html, 'html.parser')

三、BeautifulSoup的簡單使用

提取百度搜尋頁面的部分原始碼為例:

<!DOCTYPE html>
<html>
<head>
  <meta content="text/html;charset=utf-8" http-equiv="content-type" />
  <meta content="IE=Edge" http-equiv="X-UA-Compatible" />
  <meta content="always" name="referrer" />
  <link
href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.
css" rel="stylesheet" type="text/css" />
  <title>百度一下,你就知道 </title>
</head>
<body link="#0000cc">
 <div id="wrapper">
  <div id="head">
    <div class="head_wrapper">
     <div id="u1">
      <a class="mnav" href="http://news.baidu.com" name="tj_trnews">新聞
</a>
      <a class="mnav" href="https://www.hao123.com"
name="tj_trhao123">hao123 </a>
      <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地圖 </a>
      <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">視訊 </a>
      <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">貼吧
</a>
      <a class="bri" href="//www.baidu.com/more/" name="tj_briicon"
style="display: block;">更多產品 </a>
     </div>
    </div>
  </div>
 </div>
</body>
</html>

綜合requests和使用BeautifulSoup庫的html解析器,對其進行解析如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text

bs = BeautifulSoup(html, 'html.parser')

print(bs.prettify())    # prettify 方式輸出頁面

結果如下:

<!DOCTYPE html>
<!--STATUS OK-->
<html>
 <head>
  <meta content="text/html;charset=utf-8" http-equiv="content-type"/>
  <meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
  <meta content="always" name="referrer"/>
  <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css"/>
  <title>
   百度一下,你就知道
  </title>
 </head>
 <body link="#0000cc">
  <div id="wrapper">
   <div id="head">
    <div class="head_wrapper">
     <div class="s_form">
      <div class="s_form_wrapper">
       <div id="lg">
        <img height="129" hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270"/>
       </div>
       <form action="//www.baidu.com/s" class="fm" id="form" name="f">
        <input name="bdorz_come" type="hidden" value="1"/>
        <input name="ie" type="hidden" value="utf-8"/>
        <input name="f" type="hidden" value="8"/>
        <input name="rsv_bp" type="hidden" value="1"/>
        <input name="rsv_idx" type="hidden" value="1"/>
        <input name="tn" type="hidden" value="baidu"/>
        <span class="bg s_ipt_wr">
         <input autocomplete="off" autofocus="autofocus" class="s_ipt" id="kw" maxlength="255" name="wd" value=""/>
        </span>
        <span class="bg s_btn_wr">
         <input autofocus="" class="bg s_btn" id="su" type="submit" value="百度一下"/>
        </span>
       </form>
      </div>
     </div>
     <div id="u1">
      <a class="mnav" href="http://news.baidu.com" name="tj_trnews">
       新聞
      </a>
      <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">
       hao123
      </a>
      <a class="mnav" href="http://map.baidu.com" name="tj_trmap">
       地圖
      </a>
      <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">
       視訊
      </a>
      <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">
       貼吧
      </a>
      <noscript>
       <a class="lb" href="http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1" name="tj_login">
        登入
       </a>
      </noscript>
      <script>
       document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登入</a>');
      </script>
      <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">
       更多產品
      </a>
     </div>
    </div>
   </div>
   <div id="ftCon">
    <div id="ftConw">
     <p id="lh">
      <a href="http://home.baidu.com">
       關於百度
      </a>
      <a href="http://ir.baidu.com">
       About Baidu
      </a>
     </p>
     <p id="cp">
      ©2017 Baidu
      <a href="http://www.baidu.com/duty/">
       使用百度前必讀
      </a>
      <a class="cp-feedback" href="http://jianyi.baidu.com/">
       意見反饋
      </a>
      京ICP證030173號
      <img src="//www.baidu.com/img/gs.gif"/>
     </p>
    </div>
   </div>
  </div>
 </body>
</html>

四、BeautifulSoup類的基本元素

BeautifulSoup將複製的HTML文件轉換成一個複雜的樹型結構,每個節點都是python物件,所有物件可以歸納為四種Tag,NavigableString,Comment,Beautifulsoup

基本元素 說明
Tag 標籤,最基本的資訊組織單元,分別用<>和</>標明開頭和結尾,格式:bs.a或者bs.p(獲取a標籤中或者p標籤中的內容)。
Name 標籤的名字,格式為.name.
Attributes 標籤的屬性,字典形式,格式:.attrs.
NavigableString 標籤內非屬性字串,<>...</>中的字串,格式:.string.
Comment 標籤內的註釋部分,一種特殊的Comment型別。

Tag

任何存在於HTML語法中的標籤都可以bs.tag訪問獲得,如果在HTML文件中存在多個相同的tag對應的內容時,bs.tag返回第一個。示例程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')
# 獲取第一個a標籤的所有內容
print(bs.a)	# <a class="mnav" href="http://news.baidu.com" name="tj_trnews">新聞</a>
print(type(bs.a))	# <class 'bs4.element.Tag'>

在Tag標籤中最重要的就是html頁面中的nam和attrs屬性,使用方法如下:

print(bs.a.name)    # a
# 把a標籤的所有屬性列印輸出出來,返回一個字典型別
print(bs.a.attrs)   # {'href': 'http://news.baidu.com', 'name': 'tj_trnews', 'class': ['mnav']}
# 等價 bs.a.get('class')
print(bs.a['class'])    # ['mnav']
bs.a['class'] = 'newClass'  # 對class屬性的值進行修改
print(bs.a) # <a class="newClass" href="http://news.baidu.com" name="tj_trnews">新聞</a>
del bs.a['class']   # 刪除class屬性
print(bs.a) # <a href="http://news.baidu.com" name="tj_trnews">新聞</a>

NavigableString中的string方法用於獲取標籤內部的文字,程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

print(bs.title.string)  # 百度一下,你就知道
print(type(bs.title.string))    # <class 'bs4.element.NavigableString'>

Comment

Comment物件是一個特殊型別的NavigableString物件,其輸出的內容不包括註釋符號,用於輸出註釋的內容。

from bs4 import BeautifulSoup

html = """<a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新聞--></a>"""
bs = BeautifulSoup(html, 'html.parser')

print(bs.a.string)  # 新聞
print(type(bs.a.string))    # <class 'bs4.element.Comment'>

BeautifulSoup

bs物件表示的是一個文件的全部內容,大部分時候,可以把它當作Tag物件,支援遍歷文件樹和搜尋文件中描述的大部分方法。

因為Beautifulsoup物件並不是真正的HTML或者XML的tag,所以它沒有name和attribute屬性。所以BeautifulSoup物件一般包含值為"[document]"的特殊屬性.name

print(bs.name)	# [document]

五、基於bs4庫的HTML內容的遍歷方法

在HTML中有如下特定的基本格式,也是構成HTML頁面的基本組成成分。

而在這種基本的格式下有三種基本的遍歷流程

  • 下行遍歷
  • 上行遍歷
  • 平行遍歷

三種遍歷方式分別是從當前節點出發,對之上、之下、平行的格式以及關係進行遍歷。

下行遍歷

下行遍歷分別有三種遍歷屬性,如下所示:

屬性 說明
.contents 子節點的列表,將所有兒子節點存入列表。
.children 子節點的迭代型別,用於迴圈遍歷兒子節點。
.descendants 子孫節點的迭代型別,包涵所有子孫節點,用於迴圈遍歷。

程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

# 迴圈遍歷兒子節點
for child in bs.body.children:
    print(child)

# 迴圈遍歷子孫節點
for child in bs.body.descendants:
    print(child)

# 輸出子節點,以列表的形式
print(bs.head.contents)
print(bs.head.contents[0])  # 用列表索引來獲取它的某一個元素

上行遍歷

上行遍歷有兩種方式,如下所示:

屬性 說明
.parent 節點的父親標籤。
.parents 節點先輩標籤的迭代型別,用於迴圈遍歷先輩節點,返回一個生成器。

程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

for parent in bs.a.parents:
    if parent is not None:
        print(parent.name)

print(bs.a.parent.name)

平行遍歷

平行遍歷有四種屬性,如下所示:

屬性 說明
.next_sibling 返回按照HTML文字順序的下一個平行節點標籤。
.previous_sibling 返回按照HTML文字順序的上一個平行節點標籤。
.next_siblings 迭代型別,返回按照HTML文字順序的所有後續平行節點標籤。
.previous_siblings 迭代型別,返回按照HTML文字順序的前序所有平行節點標籤。

程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

for sibling in bs.a.next_siblings:
    print(sibling)

for sibling in bs.a.previous_siblings:
    print(sibling)

其它遍歷

屬性 說明
.strings 如果Tag包含多個字串,即在子孫節點中有內容,可以用此獲取,然後進行遍歷。
.stripped_strings 與strings用法一致,可以去除掉那些多餘的空白內容。
.has_attr 判斷Tag是否包含屬性。

六、檔案樹搜尋

使用bs.find_all(name, attires, recursive, string, **kwargs)方法,用於返回一個列表型別,儲存查詢的結果。

屬性 說明
name 對標籤的名稱的檢索字串。
attrs 對標籤屬性值的檢索字串,可標註屬性檢索。
recursive 是否對子孫全部檢索,預設為True。
string 用與在資訊文字中特定字串的檢索。

name引數

如果是指定的字串:會查詢與字串完全匹配的內容,程式碼如下:

a_list = bs.find_all("a")
print(a_list)

使用正規表示式:將會使用BeautifulSoup4中的search()方法來匹配,程式碼如下:

import requests
from bs4 import BeautifulSoup
import re

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

t_list = bs.find_all(re.compile("p"))
for item in t_list:
    print(item)

傳入一個列表:Beautifulsoup4將會與列表中的任一元素匹配到的節點返回,程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

t_list = bs.find_all(["meta", "link"])
for item in t_list:
    print(item)

傳入一個函式或方法:將會根據函式或者方法來匹配,程式碼如下:

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')


def name_is_exists(tag):
    return tag.has_attr("name")


t_list = bs.find_all(name_is_exists)
for item in t_list:
    print(item)

attrs引數

並不是所有的屬性都可以使用上面這種方法進行搜尋,比如HTML的data屬性,用與指定屬性搜尋。

import requests
from bs4 import BeautifulSoup

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

t_list = bs.find_all(attrs={"class": "mnav"})


for item in t_list:
    print(item)

string引數

通過string引數可以搜尋文件中的字串內容,與name引數的可選值一樣,string引數接受字串,正規表示式,列表。

import requests
from bs4 import BeautifulSoup
import re

# 使用requests庫載入頁面程式碼
r = requests.get('https://www.baidu.com')
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

t_list = bs.find_all(attrs={"class": "mnav"})
for item in t_list:
    print(item)

# text用於搜尋字串
t_list = bs.find_all(text="hao123")
for item in t_list:
    print(item)

# text可以通其它引數混合使用用來過濾tag
t_list = bs.find_all("a", text=["hao123", "地圖", "貼吧"])
for item in t_list:
    print(item)

t_list = bs.find_all(text=re.compile("\d\d"))
for item in t_list:
    print(item)

使用find_all()方法,常用到的正規表示式形式import re程式碼如下:

bs.find_all(string = re.compile('python'))	# 指定查詢內容

# 或者指定使用正規表示式要搜尋的內容
string = re.compile('python')	# 字元為python
bs.find_all(string)	# 呼叫方法模版

七、常用的find()方法如下

方法 說明
<>find() 搜尋且只返回一個結果,字串型別,同.find_all()引數。
<>find_parent() 在先輩節點中返回一個結果,字串型別,同.find_all()引數。
<>.find_parents() 在先輩節點中搜尋,返回列表型別,同.find_all()引數。
<>.find_next_sibling() 在後續平行節點中返回一個結果,同.find_all()引數。
<>.find_next_siblings() 在後續平行節點中搜尋,返回列表型別,同.find_all()引數。
<>.find_previous_sibling() 在前序平行節點中返回一個結果,字串型別,同.find_all()引數。
<>.find_previous_siblings() 在前序平行節點中搜尋,返回列表型別,同.find_all()引數。

八、爬取京東電腦資料

爬取的例子直接輸出到螢幕。

(1)要爬取京東一頁的電腦商品資訊,下圖所示:

(2)所爬取的網頁連線:https://search.jd.com/search?keyword=macbook pro&qrst=1&suggest=5.def.0.V09&wq=macbook pro

(3)我們的目的是需要獲取京東這一個頁面上所有的電腦資料,包括價格,名稱,ID等。具體程式碼如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import requests
from bs4 import BeautifulSoup

headers = {
        'User-agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) "
                      "Chrome/66.0.3359.139 Safari/537.36"
    }

URL = "https://search.jd.com/search?keyword=macbook%20pro&qrst=1&suggest=5.def.0.V09&wq=macbook%20pro"

r = requests.get(URL, headers=headers)
r.encoding = r.apparent_encoding
html = r.text
bs = BeautifulSoup(html, 'html.parser')

all_items = bs.find_all('li', attrs={"class": "gl-item"})

for item in all_items:
    computer_id = item["data-sku"]
    computer_name = item.find('div', attrs={'class': 'p-name p-name-type-2'})
    computer_price = item.find('div', attrs={'class': 'p-price'})
    print('電腦ID為:' + computer_id)
    print('電腦名稱為:' + computer_name.em.text)
    print('電腦價格為:' + computer_price.find('i').string)
    print('------------------------------------------------------------')

部分結果如下圖所示:

相關文章