學習爬蟲之網頁解析_beautifulsoup和xpath文檔學習(five day)

1.BeautifulSoup

**BeautifulSoup 是一個可以從HTML或XML文件中提取數據的Python庫,**它的使用方式相對於正則來說更加的簡單方便,常常能夠節省我們大量的時間。(cmd命令pipinstall beautifulsoup4即可)

官方中文文檔的:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

注意:使用BeautifulSoup需要指定解析器

BeautifulSoup解析網頁需要指定一個可用的解析器,以下是主要幾種解析器:
在這裏插入圖片描述由於這個解析的過程在大規模的爬取中是會影響到整個爬蟲系統的速度的,所以推薦使用的是lxml,速度會快很多,而lxml需要單獨安裝:

	pip install lxml
	soup = BeautifulSoup(html_doc, 'lxml')   # 指定

提示:如果一段HTML或XML文檔格式不正確的話,那麼在不同的解析器中返回的結果可能是不一樣的,所以要指定某一個解析器。

(1)代碼演示使用:

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's 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" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

from bs4 import BeautifulSoup
# 使用BeautifulSoup解析上述代碼,能夠得到一個 BeautifulSoup 的對象
soup = BeautifulSoup(html_doc,"lxml")   #將html轉化爲可操作的對象
# print(type(soup))                       #輸出:<class 'bs4.BeautifulSoup'>
# print(soup.prettify())                  #.prettify()按照標準的縮進格式的結構輸出

########################第零類:入門
print(soup.title)           #獲取title標籤
print(soup.title.name)      #獲取title標籤的標籤名
print(soup.title.string)    #獲取title標籤的文本內容
print(soup.title.parent)    #獲取title標籤的父標籤
print(soup.find(id="link2"))    #找到id=link2的標籤

print("第一類:Tag對象","*"*50)
########################第一類:Tag對象
'''
    tag對象可以說是BeautifulSoup中最爲重要的對象,通過BeautifulSoup來提取數據基本都圍繞着這個對象來進行操作。
'''
print("我是Tag對象:",type(soup.a))          #輸出爲:<class 'bs4.element.Tag'>

#1.獲取標籤             僅獲取第一個符合條件的標籤
print(soup.a)           #輸出爲:<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

#2.獲取屬性             僅獲取第一個符合條件的標籤的屬性值
print(soup.a["href"])   #輸出爲:http://example.com/elsie

#3.獲取文本內容         有兩種方法:
# 第一個例子:
print(soup.a.text)       #輸出都爲:Elsie
print(soup.a.get_text())
#第二個例子:
print(soup.body.get_text())     #發現:這個方法會獲取選中標籤下的所有文本內容

# 簡要講一下NavigableString對象:
'''
    NavigableString的意思是可以遍歷的字符串,一般被標籤包裹在其中的的文本就是NavigableString格式。
'''
print(type(soup.body.get_text()))       #輸出爲:<class 'str'>
print(type(soup.p.string))              #輸出爲:<class 'bs4.element.NavigableString'>

print("第二類:","*"*50)
##########################第二類
body=soup.body           #獲取整個body標籤

#1.子代標籤的選擇
'''
    contents和children:
    通過contents可以獲取某個節點所有的子節點,包括裏面的NavigableString對象。獲取的子節點是列表格式。
    而通過children同樣的是獲取某個節點的所有子節點,但是返回的是一個迭代器,這種方式會比列表格式更加的節省內存。

'''
print(body.contents)

tags = body.children             #打印body.children可知這是個迭代器。獲取的是子代標籤。
# print(list(tags))
# for tag in tags:
#     print(tag)

#2.後代標籤的選擇
'''
    descendants
    contents和children獲取的是某個節點的直接子節點,而無法獲得子孫節點。
    通過descendants可以獲得所有子孫節點,返回的結果跟children一樣,需要迭代或者轉類型使用。
'''
tags_des=body.descendants        #打印body.descendants可知這是個生成器。獲取的是後代標籤
# print(list(tags_des))

#3.兄弟標籤的選擇
'''
    兄弟節點:
    兄弟節點指的就是父節點相同的節點。            如果選中標籤在body子節點的第一個位置,那麼它沒有上一級標籤;
    next_sibling 和 previous_sibling         同理,如果選中標籤在body子節點的最後一個位置,那麼它沒有下一級標籤。
    next_siblings 和 previous_siblings
    
    加s是獲取上級或者下級所有的兄弟節點
'''
p = body.p                                           #可以一層層獲取指定標籤,但是也只可以獲取第一個符合條件的標籤
# print(p)
print(p.next_sibling.next_sibling)                  #如果想獲取符合條件的第二個標籤,使用.next_sibling方法
print(body.previous_sibling.previous_sibling)       #如果想獲取符合條件的同級標籤的上一個標籤,使用.previous_sibling方法

#4.父級標籤的選擇
'''
    父節點parent和parents
    有時我們也需要去獲取某個節點的父節點,也就是包裹着當前節點的節點
    而使用parents則可以獲得當前節點遞歸到頂層的所有父輩元素。
'''
print(p.parent)                 #.parent只能獲取選中標籤的父親

p_parents = p.parents           #.parents可以獲取選中標籤的所有的父輩元素
# print(p_parents)               #打印可知這是個生成器
print(list(p_parents))

#4.string和strings
'''
    string和strings:
    我們常常會遇到需要獲取某個節點中的文本值的情況,如果這個節點中只有一個字符串,那麼使用string可以正常將其取出。
    而如果這個節點中有多個字符串的時候,BeautifulSoup就無法確定要取出哪個字符串了,這時候需要使用strings。
    使用stripped_strings可以將全是空白的行去掉。
'''
print(p.string)     #輸出爲:The Dormouse's story

gg=body.strings     #返回的是一個generator(生成器)
print(list(gg))

print(list(body.stripped_strings))      #會發現這個返回的相比上面直接用strings的區別:沒有了空白行

print("第三類:find_all","*"*50)
########################第三類:find_all()
# 上方這些直接通過屬性來進行訪問屬性的方法,很多時候只能適用於比較簡單的一些場景,
# 所以BeautifulSoup還提供了搜索整個文檔樹的方法find_all()。
'''
    源碼:
        def find_all(self, name=None, attrs={}, recursive=True, text=None)
    功能:獲取所有元素
    返回值:一個可迭代對象(列表)
'''
#注意:find_all獲取到的內容在列表裏哦!!!

# 1.通過name搜索,find_all('p')可以直接查找出整個文檔樹中所有的p標籤,並返回列表
print(soup.find_all("p"))
# 拓展:
print(soup.find_all(["p","a"]))                             #獲取所有的p標籤和a標籤

#2.通過屬性搜索,這時候我們可以通過傳遞給attrs一個字典參數來搜索屬性。soup.find_all(attrs={'class': 'sister'})
print(soup.find_all(name="p",attrs={"class":"story"}))      #獲取p標籤裏class爲story的p標籤

#3.通過文本內容搜索,soup.find_all(text="Elsie")
print(soup.find_all(text="Elsie"))          #輸出爲:['Elsie']
print(soup.find_all("a",text="Elsie"))     #獲取a標籤裏文本爲Elsie的a標籤/soup.find_all(text="Elsie")[0].parent也可以
# 小拓展:獲取屬性值
print(soup.find_all("a",text="Elsie")[0]["href"])           #獲取滿足條件的a標籤的href

# 4.限制查找範圍爲子節點
# find_all()方法會默認的去所有的子孫節點中搜索,
# 而如果將recursive參數設置爲False,則可以將搜索範圍限制在直接子節點中。
print(soup.html.find_all("body",recursive=False))           #recursive爲True時可以遞歸拿到後代元素;
                                                            # 爲False時不可以遞歸只拿到子代元素。
                                                            #例如:此處:html標籤下的body不管爲True還是False都可以拿到,因爲body是html的子代元素也是後代元素;
                                                            #但是如果此處是a標籤,爲False就拿不到東西,因爲a標籤是html標籤的後代元素,而非子元素。

#5.結合使用正則表達式進行查找
'''
    通過正則表達式來篩選查找結果在BeautifulSoup中,也是可以與re模塊進行相互配合的,
    將re.compile編譯的對象傳入find_all()方法,即可通過正則來進行搜索。
    tags = soup.find_all(re.compile("^b"))
'''
import re
tags = soup.find_all(re.compile("^b"))              #查找以b開頭的標籤(b標籤和body標籤)

print("第四類:css選擇器","*"*50)
#######################第四類:css選擇器
#1.css選擇器
print(soup.select("p"))                     #獲取所有p標籤
print(soup.select("p>a"))                   #獲取p標籤下面的a標籤  在列表裏

(2)代碼輸出:

在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述

2.XPath(路徑表達式)

簡介:

XPath 是一門在 XML 文檔中查找信息的語言。XPath 可用來在 XML 文檔中對元素和屬性進行遍歷。

相比於BeautifulSoup,Xpath在提取數據時會更有效率。

安裝:

在python中很多庫都提供XPath的功能,但是最流行的還是lxml這個庫,效率最高。(直接pip install lxml 即可)

(1)代碼演示簡單使用:

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>

<p class="story" id="66">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">999</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

from lxml import etree          #原理和beautiful一樣,都是將html字符串轉換爲我們易於處理的標籤對象
page = etree.HTML(html_doc)     #返回了html節點
print(type(page))               #輸出爲:<class 'lxml.etree._Element'>

######################第一部分:常用!
'''
    XPath 使用路徑表達式在 XML/HTML 文檔中選取節點。節點是通過沿着路徑或者 step 來選取的。
    下面列出了最有用的路徑表達式:
'''
# 1.     根據nodename(標籤名字)選取標籤的時候,只會選擇子標籤;比如:如果是兒子的兒子則選取不到。
print(page.xpath("body"))

#2.     /從根節點選取  一級一級篩選(不能跳)
gen = page.xpath("/html")
print(gen)

#3.     從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。     注意:是所有符合條件的
a = page.xpath("//a")
print(a)

#4.    .選取當前標籤
p = page.xpath("//p")[0]            #先選擇p標籤的第一個
print(p.xpath("."))
print(p.xpath("./b"))               #選取當前標籤下的b標籤

#5.   ..選取當前標籤的父節點
a = page.xpath("//a")[0]
print(a.xpath(".."))

# 6.獲取標籤的屬性值
bb = page.xpath('//p[@class="story"]/@id')     #獲取標籤的id屬性值
print(bb)

print("第二部分:謂語","*"*50)
#########################第二部分:謂語
'''
    謂語:
    謂語用來查找某個或某些特定的節點或者包含某個指定值的節點
    謂語被嵌在方括號中。實例:
    下面列出了帶有謂語的一些路徑表達式,以及表達式的結果。
'''
#1.選取所有擁有屬性class的p標籤
j = page.xpath('//p[@class]')
print("j:",j)

#選取所有p標籤,且擁有屬性class同時值爲story的標籤
b = page.xpath('//p[@class="story"]')
print("b",b)

#2.選取所有的p標籤,且其中a標籤的文本值大於889。
print(page.xpath('//p[a>889]'))

#3.選取屬於class爲story的p標籤 子元素的第一個a元素。
dd = page.xpath('//p[@class="story"]/a[1]')     #如果是在xpath裏進行索引選擇,是從1開始
ee = page.xpath('//p[@class="story"]/a')[0]     #如果是從列表裏進行索引選擇,是從0開始
print("dd:",dd)
print("ee",ee)

# 選取屬於class爲story的p標籤 子元素的最後一個a元素。
ss = page.xpath('//p[@class="story"]/a[last()]')
print("ss:",ss)

# 選取屬於class爲story的p標籤 子元素的倒數第二個a元素。
rr = page.xpath('//p[@class="story"]/a[last()-1]')
print("rr",rr)

# 選取最前面的兩個屬於class爲story的p標籤的子元素的a元素。
gg = page.xpath('//p[@class="story"]/a[position()<3]')
print("gg:",gg)

print("第三部分:獲取文本","*"*50)
########################第三部分:獲取文本
#1.用text()獲取某個節點下的文本
contents=page.xpath("//p/a/text()")     #獲取文本數據  放在列表裏
print(contents)

#2.用string()獲取某個節點下所有的文本
con = page.xpath("string(//p)")         #只拿到第一個標籤下的所有文本
print(con)

print("第四部分:XPath通配符","*"*50)
########################第四部分:XPath通配符
'''
    選取未知節點
    XPath通配符可用來選取未知節點
'''
# 1.* 匹配任何元素節點
s = page.xpath("//p/*")             #選擇p標籤的所有子元素
print(s)

#2.@* 匹配任何屬性節點
ss = page.xpath("//p/@*")           #選取選中標籤(所有p標籤)的所有的屬性值
print(ss)

print("第五部分;使用|運算符","*"*50)
########################第五部分:使用|運算符
'''
    選取多個路徑
    通過在路徑表達式中使用"|"運算符,您可以選取若干個路徑。
    在下面,我們列出了一些路徑表達式,以及這些表達式的結果:
'''
# 選取p元素的所有a和b元素
print(page.xpath('//p/a|//p/b'))

#選取文檔中的所有a和b元素
print(page.xpath('//a|//b'))


(2)代碼輸出:

在這裏插入圖片描述在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章