02_BeautifulSoup的使用1

參考資料地址:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/#id28

練習(xí)數(shù)據(jù)準(zhǔn)備

  • 獲取個(gè)人簡(jiǎn)書(shū)首頁(yè)的html頁(yè)面,并寫(xiě)入一個(gè)html文件中:
import codecs

from bs4 import BeautifulSoup
from selenium import webdriver


base_url = 'http://www.itdecent.cn'
user = '/u/39cef8a56bf9'

driver = webdriver.PhantomJS()
driver.get(base_url + user)
html = driver.page_source

bsobj = BeautifulSoup(html)
bsobj.encode('utf-8')
with codecs.open('index.html', 'w+', encoding='utf-8') as f:
    f.write(bsobj.prettify())
  • 說(shuō)明:本來(lái)想用requests獲取頁(yè)面的html的,但是簡(jiǎn)書(shū)的反爬機(jī)制應(yīng)該比較厲害,在headers中添加瀏覽器信息搞不定,所以選擇了用selenium+phantomJS獲取頁(yè)面html。
  • 如果想用requests可以試試如下方式:
import codecs

import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent

base_url = 'http://www.itdecent.cn'
user = '/u/39cef8a56bf9'
ua = UserAgent()
headers = {
    'User_Agent': ua.random
}

r = requests.get(base_url + user, headers=headers, verify=False)
r.encoding = 'utf-8'
with codecs.open('index.html', 'w+', encoding='utf-8') as f:
    f.write(r.text)

BeautifulSoup學(xué)習(xí)

前面已經(jīng)將一個(gè)html頁(yè)面以beautifulsoup對(duì)象的格式保存在了index.html中,接下來(lái)將用這個(gè)html文件用作示例練習(xí)(PS:這個(gè)時(shí)候就不要去訪問(wèn)網(wǎng)站了,直接讀取保存好的文件)。

1、對(duì)象的種類(lèi)

要掌握BeautifulSoup中對(duì)象操作,需要了解html的結(jié)構(gòu):http://www.runoob.com/html/html-elements.html。

image.png

  • 1.標(biāo)簽 tag
import codecs

from bs4 import BeautifulSoup

bsobj = BeautifulSoup(codecs.open('index.html', 'r', 'utf-8'))
get_tag = bsobj.link
print(get_tag)

獲得link標(biāo)簽的結(jié)果:

<link  media="all" rel="stylesheet"/>
  • 2.標(biāo)簽Tag有很多屬性,比如:name和attributes。
  • 3.Name:每個(gè)Tag都有名字,通過(guò).name獲取,比如:a、p、title等都是名字
get_title = bsobj.title
print(get_title)
print(get_title.name)

結(jié)果:

<title>
   樂(lè)大爺L - 簡(jiǎn)書(shū)
  </title>
title
  • 4.屬性Attributes:一個(gè)標(biāo)簽會(huì)包含多個(gè)屬性,屬性在開(kāi)始標(biāo)簽中,tag中屬性的操作方法與字典的操作方法一樣,并且支持增刪改查
get_tag = bsobj.link
print(get_tag)
# 查
print(get_tag['href'])
print(get_tag.attrs)
# 增、刪、改
get_tag['new'] = 1
del get_tag['rel']
get_tag['href'] = 'be changed'
print(get_tag)

結(jié)果:

# 修改前
<link  media="all" rel="stylesheet"/>
# href屬性的值
//cdn2.jianshu.io/assets/web-454c23dafcf6c3369b7c.css
# 標(biāo)簽中所有屬性的值,以字典格式輸出
{'href': '//cdn2.jianshu.io/assets/web-454c23dafcf6c3369b7c.css', 'media': 'all', 'rel': ['stylesheet']}
# 修改后的標(biāo)簽
<link href="be changed" media="all" new="1"/>
  • 5.多值屬性:tag中的屬性支持多值屬性,常見(jiàn)的多值屬性是class,多值屬性的返回結(jié)果是列表
get_tag = bsobj.link
get_tag['class'] =['duo', 'zhi', 'shu', 'xing']
print(get_tag)
print(get_tag['class'])
print(type(get_tag['class']))

結(jié)果:

<link class="duo zhi shu xing"  media="all" rel="stylesheet"/>
['duo', 'zhi', 'shu', 'xing']
<class 'list'>
  • 6.可遍歷的字符串:字符串包含在tag內(nèi),通過(guò).string獲取,字符串的內(nèi)容不能被編輯,只能通過(guò)replace_with()進(jìn)行替換。
get_title = bsobj.title
# 改變前的title標(biāo)簽
print(get_title)
# 獲取標(biāo)簽中的內(nèi)容
print(get_title.string)
print(type(get_title.string))
# 采用replace_with改變標(biāo)簽內(nèi)容
get_title.string.replace_with("wo gai bian le, 樂(lè)大爺?shù)暮?jiǎn)書(shū)")
print(get_title)

結(jié)果:

<title>
   樂(lè)大爺L - 簡(jiǎn)書(shū)
  </title>

   樂(lè)大爺L - 簡(jiǎn)書(shū)
  
<class 'bs4.element.NavigableString'>
  
<title>wo gai bian le, 樂(lè)大爺?shù)暮?jiǎn)書(shū)</title>

2、遍歷文檔樹(shù)

遍歷文檔樹(shù)可以獲得文檔中的子節(jié)點(diǎn)、父節(jié)點(diǎn)、兄弟節(jié)點(diǎn)等標(biāo)簽。

  • 子節(jié)點(diǎn):tag.name、tag.contents、tag.string、tag.strings、tag.stripped_strings等
  • 父節(jié)點(diǎn):tag.parent、tag.parents
  • 兄弟節(jié)點(diǎn):next_sibling、previous_sibling、next_siblings、previous_siblings
  • 回退和前進(jìn):.next_elements、.previous_elements、next_element 和 .previous_element
1、子節(jié)點(diǎn)

要獲取子節(jié)點(diǎn),首先要分析子節(jié)點(diǎn)中的內(nèi)容,一個(gè)tag標(biāo)簽中,通常會(huì)包含多個(gè)字符串或者多個(gè)其他的tag標(biāo)簽。由于字符串沒(méi)有子節(jié)點(diǎn),是不具備遍歷屬性的。

  • 1)、獲取所有的link標(biāo)簽:前面提到bsobj.link可以獲取link標(biāo)簽信息,但是這種方式只能獲取到第一條link信息,要獲取文檔中全部的link標(biāo)簽信息,可以用bsobj.find_all('link'),返回的結(jié)果是一個(gè)列表。
links = bsobj.find_all('link')
for link in links:
    print(link)

# 結(jié)果信息
<link  .../>
<link href="http://cdn2.jianshu.io/assets/web/pages/users/show/entry-8e775d8bb60eed8.../>
<link href="http://cdn2.jianshu.io/assets/favicons/favicon.../>
...
  • 2)、獲取.contents和.children屬性:

  • .contents:獲取一個(gè)標(biāo)簽中的所有內(nèi)容,以列表的格式輸出。當(dāng)然,由于contents中可能包含子節(jié)點(diǎn)信息,則所有的子節(jié)點(diǎn)信息都會(huì)在列表中輸出。

get_title = bsobj.body.div.a
print(get_title)
print(get_title.contents)

print(len(get_title.contents))
print(get_title.contents[1])

結(jié)果:

<a class="logo" href="/">
<img alt="Nav logo" src="http://cdn2.jianshu.io/assets/web/nav-logo-4c7bbafe27adc892f3046e6978459bac.png"/>
</a>
['\n', <img alt="Nav logo" src="http://cdn2.jianshu.io/assets/web/nav-logo-4c7bbafe27adc892f3046e6978459bac.png"/>, '\n']

3
<img alt="Nav logo" src="http://cdn2.jianshu.io/assets/web/nav-logo-4c7bbafe27adc892f3046e6978459bac.png"/>
  • .children:這是一個(gè)迭代器,可以對(duì)tag標(biāo)簽的子節(jié)點(diǎn)進(jìn)行循環(huán)獲取。比如,contents是獲取到一個(gè)標(biāo)簽之間的所有內(nèi)容,同一層級(jí)的多個(gè)子節(jié)點(diǎn)在contents中算作列表中的一個(gè)元素。此時(shí),可以通過(guò).children將子節(jié)點(diǎn)中的同一層級(jí)的標(biāo)簽進(jìn)行分割。
get_title = bsobj.body.div.li
li_tag = get_title.contents[1]
print(li_tag)
print(type(li_tag.children))
child_list = []
for child in li_tag.children:
    child_list.append(child)
print(child_list)

結(jié)果:

<a href="/">
<span class="menu-text">
          首頁(yè)
         </span>
<i class="iconfont ic-navigation-discover menu-icon">
</i>
</a>
<class 'list_iterator'>
['\n', <span class="menu-text">
          首頁(yè)
         </span>, '\n', <i class="iconfont ic-navigation-discover menu-icon">
</i>, '\n']
  • 3)、.descendants:獲取子節(jié)點(diǎn)和子孫節(jié)點(diǎn)
    .children和.contents只會(huì)包含tag的直接子節(jié)點(diǎn),對(duì)直接子節(jié)點(diǎn)中的子孫節(jié)點(diǎn)不會(huì)分離出來(lái)。.descendants返回的結(jié)果是一個(gè)生成器。
get_title = bsobj.body.div.li
li_tag = get_title.contents[1]
print(len(list(li_tag.children)))
print(list(li_tag.children))
print(len(list(li_tag.descendants)))
print(list(li_tag.descendants))

結(jié)果:首頁(yè)這個(gè)內(nèi)容,相當(dāng)于是span的子節(jié)點(diǎn),.descendants會(huì)把它當(dāng)成子孫節(jié)點(diǎn)處理,其他子孫節(jié)點(diǎn)標(biāo)簽同理。

5
['\n', <span class="menu-text">
          首頁(yè)
         </span>, '\n', <i class="iconfont ic-navigation-discover menu-icon">
</i>, '\n']
7
['\n', <span class="menu-text">
          首頁(yè)
         </span>, '\n          首頁(yè)\n         ', '\n', <i class="iconfont ic-navigation-discover menu-icon">
</i>, '\n', '\n']
  • 4)、.string:獲取NavigableString 類(lèi)型子節(jié)點(diǎn)
    當(dāng)一個(gè)tag只有一個(gè)NavigableString 類(lèi)型子節(jié)點(diǎn)時(shí),可以采用.string獲取,但是當(dāng)有多個(gè)子節(jié)點(diǎn)時(shí),.string無(wú)法得知獲取哪一個(gè),會(huì)直接返回None。
  • 5)、.strings 和 stripped_strings:獲取一組NavigableString 類(lèi)型子節(jié)點(diǎn)
    .strings獲取的信息中,包含空行,stripped_strings可以去除前后所有的空行以及為空的內(nèi)容
get_title = bsobj.body.div
for sting in get_title.stripped_strings:
    print(sting)

結(jié)果:

寫(xiě)文章
注冊(cè)
登錄
夜間模式
開(kāi)
關(guān)
...
2、父節(jié)點(diǎn)

每個(gè)tag都會(huì)包含父節(jié)點(diǎn),比如前面采用bsobj.body.div獲取節(jié)點(diǎn)信息,body就是div的一個(gè)父節(jié)點(diǎn)。

  • 1)、parent
get_title = bsobj.body.div.ul
print(get_title.parent)
print(get_title.parent.name)

結(jié)果:

<div class="collapse navbar-collapse" id="menu">
<ul class="nav navbar-nav">
<li class="tab ">
<a href="/">
....
</li>
</ul>
</div>
div
  • 2)、parents:通過(guò)元素的 .parents 屬性可以遞歸得到元素的所有父輩節(jié)點(diǎn)
get_title = bsobj.body.div.ul
for parent in get_title.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)

結(jié)果:

div
div
div
nav
body
html
[document]
3、兄弟節(jié)點(diǎn)

標(biāo)簽處于同一層的節(jié)點(diǎn),他們的父節(jié)點(diǎn)是同一個(gè),這樣的節(jié)點(diǎn)稱(chēng)為兄弟節(jié)點(diǎn)。

  • 1)、.next_sibling 和 .previous_sibling 屬性來(lái)查詢(xún)兄弟節(jié)點(diǎn),實(shí)際文檔中的tag的 .next_sibling 和 .previous_sibling 屬性通常是字符串或空白。
get_title = bsobj.head.meta
print(get_title)
print(get_title.previous_sibling)
print(get_title.next_sibling)
  • 2)、.next_siblings 和 .previous_siblings:通過(guò) .next_siblings 和 .previous_siblings 屬性可以對(duì)當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)迭代輸出。


    image.png
get_title = bsobj.head.meta
for sibling in get_title.next_siblings:
    print(repr(sibling))

結(jié)果:

'\n'
<meta content="IE=Edge" http-equiv="X-UA-Compatible"/>
'\n'
<meta content="width=device-width, initial-scale=1.0,user-scalable=no" .../>
...
4、回退和前進(jìn)

讓解析的屬性指向上一個(gè)被解析對(duì)象或下一個(gè)被解析對(duì)象。

  • 1)、.next_element 和 .previous_element:.next_element 屬性指向解析過(guò)程中下一個(gè)被解析的對(duì)象(字符串或tag),結(jié)果可能與 .next_sibling 相同,但通常是不一樣的。
get_title = bsobj.body.div.ul.li.span
print(get_title)
print(get_title.nex_sibling)
print(get_title.next_element)

結(jié)果:

<span class="menu-text">
          首頁(yè)
         </span>
None

          首頁(yè)
  • 2)、.next_elements 和 .previous_elements:通過(guò) .next_elements 和 .previous_elements 的迭代器就可以向前或向后訪問(wèn)文檔的解析內(nèi)容,就好像文檔正在被解析一樣
get_title = bsobj.body.div.ul.li.span
for element in get_title.next_elements:
    print(repr(element))

總結(jié)

  • 本節(jié)學(xué)習(xí)了beautifulsoup的tag對(duì)象、遍歷文檔樹(shù)的使用
  • 通過(guò)查找子節(jié)點(diǎn)、父節(jié)點(diǎn)等信息,可以獲取到想要的標(biāo)簽信息
  • 通過(guò)獲取標(biāo)簽信息的.name、.attrs等,可以獲取精確的信息
  • 后續(xù)繼續(xù)學(xué)習(xí)搜索文檔樹(shù)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容