目錄
1 XPath的使用
1.1 XPath概覽
1.2? XPath常用規則
1.3 準備工作
1.4 實例引入
1.5 所有節點
1.6 節點
1.7 父節點
1.8 屬性匹配
1.9 文本獲取
1.10 屬性獲取
1.11 屬性多值匹配
1.12 多屬性匹配
1.13 按序選擇
1.14?節點軸選擇
2 Beautiful Soup
2.1 簡介
2.2 解析器
2.3 準備工作
2.4 基本使用
2.5 節點選擇器
2.6 提取信息
2.6.1 獲取名稱
2.6.2 獲取屬性
2.6.3 獲取內容
2.6.4 嵌套選擇
2.7 關聯選擇
2.7.1 子節點和子孫節點
2.7.2 父節點和祖先節點
2.7.3 兄弟節點
2.7.4 提取信息
2.8 方法選擇器
2.8.1 find_all
2.8.2 name
2.8.3 attrs
2.8.4 text
2.8.5 find
2.8.6?其他查詢方法
2.9 CSS選擇器
2.9.1 嵌套選擇
2.9.2 獲取屬性
2.9.3 獲取文本
2.9.4?總結
3 pyquery
3.1 準備工作
3.2 初始化
3.2.1 字符串初始化
3.2.2 URL初始化
3.2.3 文件初始化
3.3 基本CSS選擇器
3.4 查找節點
3.4.1 子節點
3.4.2 父節點
3.4.3 兄弟節點
3.5 遍歷節點
3.5.1?獲取屬性
3.5.2?獲取文本
3.6 節點操作
3.6.1 addClass和removeClass
3.6.2 attr、text和html
3.6.3 remove
3.7 偽類選擇器
4 parsel 的使用
4.1 提取數據?
4.2 提取文本內容
4.3?提取特定的文本內容
4.4?提取特定的屬性值
4.5 提取屬性
4.6?正則提取
對于網頁的節點來說,可以定義id、class或其他屬性,而且節點之間還有層次關系,在網頁中可以通過XPath或CSS選擇器來定位一個或多個節點。相關的解析庫也比較多,包括lxml、Beautiful Soup、pyquery、parsel等。
1 XPath的使用
XPath的全程是XML Path Language,即XML路徑語言,用來在XML文檔中查找信息。雖然最初是用來搜尋XML文檔的,但同樣適用于HTML文檔的搜索。
1.1 XPath概覽
包含許多定位的節點。
1.2? XPath常用規則
XPath的一個常用匹配規則://title[@lang='eng'],代表選擇所有名稱為title,同時屬性為lang的值為eng的節點。
1.3 準備工作
使用lxml庫,利用XPath對HTML進行解析。
pip install lxml
1.4 實例引入
from lxml import etree
text='''
<div><ul><li class='item-0'><a href='link1.html'>first item</a></li><li class='item-1'><a href='link2.html'>second item</a></li><li class='item-inactive'><a href='link3.html'>third item</a></li><li class='item-1'><a href='link4.html'>fourth item</a></li><li class='item-0'><a href='link5.html'>fifth item</a></ul>
</div>
'''
html=etree.HTML(text)
result=etree.tostring(html)
print(result.decode('utf-8'))
<html><body><div><ul><li class='item-0'><a href='link1.html'>first item</a></li><li class='item-1'><a href='link2.html'>second item</a></li><li class='item-inactive'><a href='link3.html'>third item</a></li><li class='item-1'><a href='link4.html'>fourth item</a></li><li class='item-0'><a href='link5.html'>fifth item</a></li></ul>
</div>
</body></html>
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=etree.tostring(html)
print(result.decode('utf-8'))
1.5 所有節點
一般會用以//開頭的XPath規則,來選取所有符合要求的節點。
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//*')
print(result)
*代表匹配的所有節點,也就是獲取整個HTML文本中的所有節點。從運行結果可以看到,返回形式是一個列表,其中每個元素是element類型,類型后面跟著節點的名稱。
如果想要獲取所有的li節點,實例如下:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li')
print(result)
print(result[0])
從運行結果也可以看出,提取結果是一個列表,其中每個元素都是element類型,如果想要取出其中一個對象,可以直接用中括號加索引獲取,如[0]。
1.6 節點
通過/或//即可查找元素的子節點或子孫節點。如果想選擇li節點的所有直接子節點a,則可以通過以下代碼實現:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li//a')
print(result)
如果要獲取節點的所有子孫節點,可以使用//。
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//ul//a')
print(result)
但是如果這里用//ul/a,就無法獲取任何結果了,因為/用于獲取直接子節點,但ul節點下沒有直接的a子節點,只有li節點。
1.7 父節點
通過連續的/或//可以查找子節點或子孫節點,如果指導子節點,查找父節點,可以通過..實現。
例如,首先選中href屬性為link4.html的a節點,然后獲取其父節點,在獲取父節點的class屬性,代碼如下:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//a[@href='link4.html]'/../@class')
print(result)
運行結果如下:
['item-1']
此外,也可以通過parent::獲取父節點,代碼如下:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//a[@href='link4.html]'/parent::*/@class')
print(result)
1.8 屬性匹配
在選取節點的時候,還可以使用@符號實現屬性過濾。例如,要選取class屬性為item-0的li節點,可以通過以下代碼實現:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class='item-0']')
print(result)
1.9 文本獲取
用XPath中的text方法可以獲取節點中的文本,接下來嘗試獲取前面li節點中的文本,代碼如下:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class='item-0']/text()')
print(result)
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class='item-0']/a/text()')
print(result)
如果使用//,能夠獲取到的結果,代碼:
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class='item-0']//text()')
print(result)
1.10 屬性獲取
利用@符號,可以獲取所有li節點下所有a節點的href屬性:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)
通過@href獲取節點的href屬性。此處與屬性匹配方法不同,屬性匹配是用中括號加屬性名和值來限定某個屬性,如[@href="link1.html"],此處的@href是指獲取節點的某個屬性。
1.11 屬性多值匹配
有時候,某些節點的某個屬性可能有多個值,例如:
from lxml import etree
text='''
<li class='li li-first'><a href='link.html'>first item</a></li>
'''
html=etree.HTML(text)
result=html.xpath('//li[@class='li']/a/text()')
print(result)
這里的HTML文本中li節點的class屬性就有兩個值:li和li-frist,需要用到contains方法,代碼如下:
from lxml import etree
text='''
<li class='li li-first'><a href='link.html'>first item</a></li>
'''
html=etree.HTML(text)
result=html.xpath('//li[contains(@class,'li')]/a/text()')
print(result)
上面的contains,給第一個參數傳入屬性名稱,第二個參數傳入屬性值,只要傳入的屬性包含傳入的屬性值,就可以完成匹配。
1.12 多屬性匹配
根據多個屬性確定一個節點,這時需要同時匹配多個屬性,運算符and用于連接多個屬性,代碼如下:
from lxml import etree
text='''
<li class='li li-first'><a href='link.html'>first item</a></li>
'''
html=etree.HTML(text)
result=html.xpath('//li[contains(@class='li') and @name='item']/a/text()')
print(result)
此外,還有許多運算符:
1.13 按序選擇
在選擇節點時,某些屬性可能同時匹配了多個節點,但我么只想要其中的某一個,可以使用往中括號中傳入索引的方法獲取特定次序的節點,代碼:
from lxml import etree
text='''
<div><ul><li class='item-0'><a href='link1.html'>first item</a></li><li class='item-1'><a href='link2.html'>second item</a></li><li class='item-inactive'><a href='link3.html'>third item</a></li><li class='item-1'><a href='link4.html'>fourth item</a></li><li class='item-0'><a href='link5.html'>fifth item</a></ul>
</div>
'''
html=etree.HTML(text)
result=html.xpath('//li[1]/a/text()')
print(result)
result=html.xpath('//li[last()]/a/text()')
print(result)
result=html.xpath('//li[position()<3]/a/text()')
print(result)
result=html.xpath('//li[last()-2]/a/text()')
print(result)
注:這里的代碼序號以1開頭,而不是0。last()表示最后,position表示位置。
1.14?節點軸選擇
XPath提供了許多節點軸的選擇方法,包括獲取子元素、兄弟元素、父元素、祖先元素等,代碼:
from lxml import etree
text ='''
<div><ul><li class="item-0"><a href="link1.html"><span>first item</span></a></li><li class="item-1"><a href_"link2.html">second item</a></li><li class="item-inactive"><a href="link3.html">third item</a></li><li class="item-1"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></ul>
</div>
'''
html = etree.HTML(text)
result = html.xpath('//li[1]/ancestor::*')
print(result)
result = html.xpath('//li[1]/ancestor::div')
print(result)
result = html.xpath('//li[1]/attribute::*')
print(result)
result = html.xpath('//li[1]/child::a[@href="link1.html"]')
print(result)
result = html.xpath('//li[1]/descendant::span')
print(result)
result = html.xpath('//li[1]/following::*[2]')
print(result)
result = html.xpath('//li[1]/following-sibling::*')
print(result)
第一次選擇,調用ancestor軸,可以獲取所有祖先節點,其后需要跟兩個冒號,然后是節點的選擇器(*表示匹配所有節點)。返回的是第一個li節點的所有祖先節點,包括html、body、div和ul。
第二次選擇,加了限定條件。
第三次選擇,調用了attribute軸,可以獲取所有屬性值,其后跟的選擇器還是*,代表獲取節點的所有屬性,返回值就是li節點的所有屬性值。
第四次選擇,調用了child軸,可以獲取所有直接子節點,這里的限定條件指的事選取href屬性為link.html的a節點。
第五次選擇,調用了descendant軸,可以獲取所有子孫節點,這里的限定條件指獲取span節點。
第六次選擇,調用了following軸,獲取當前節點之后的所有節點,這里的限定條件指獲取了第二個后續節點。
第七次選擇,調用了following-sibling軸,獲取當前節點之后的所有同級節點。
2 Beautiful Soup
2.1 簡介
python的一個html或xml的解析庫,用它可以方便地從網頁中提取數據。
2.2 解析器
通過上表,lxml解析器具有解析html和xml的功能。
使用lxml解析器,只需要在初始化時,把第二個參數修改為lxml即可:
from bs4 import BeautifulSoup
soup=BeautifulSoup('<p>Hello</p>','lxml')
print(soup.p.string)
2.3 準備工作
pip3 install beautifulsoup4
2.4 基本使用
html="""
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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/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
soup=BeautifulSoup(html,'lxml')
print(soup.prettify())
print(soup.title.string)
2.5 節點選擇器
直接調用節點的名稱即可選擇節點,然后調用string屬性就可以得到節點內的文本了。
html="""
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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/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
soup=BeautifulSoup(html,'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)
2.6 提取信息
2.6.1 獲取名稱
利用name屬性可以獲取節點的名稱。
print(soup.title.name)
2.6.2 獲取屬性
一個節點可能有多個屬性,例如id和class,選擇某個節點元素后,可以調用attrs獲取其所有屬性:
print(soup.p.attrs)
print(soup.p.attrs['name'])
print(soup.p['name'])
print(soup.p['Class'])
2.6.3 獲取內容
print(soup.p.string)
2.6.4 嵌套選擇
html ='''
<html><head><title>The Dormouse's story</title></head>
<body>
'''
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.head.title)
print(type(soup.head.title))
print(soup.head.title.string)
2.7 關聯選擇
在做選擇的過程中,需要先選中某一個節點,然后再以這個節點為基準選子節點、父節點、兄弟節點等。
2.7.1 子節點和子孫節點
選取節點之后,如果想要獲取它的直接子節點,可以調用contens屬性,代碼:
<html><head><title>The Dormouse'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" class="sister" id="link1"><span>Elsie</span></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
soup=BeautifulSoup(html,'lxml')
print(soup.p.contents)
可以看到,返回結果是列表形式:p節點里既包括文本,又包括節點。列表中的每個元素都是p節點的直接子節點。像第一個a節點里面包括的span節點,相當于孫子節點,但是返回結果并沒有把span節點單獨選出來。所以說,contens屬性得到的結果是直接子節點組成的列表。
同樣,可以調用children屬性得到相應的結果:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.children)
for i, child in enumerate(soup.p.children):print(i, child)
這里掉用children屬性來選擇,返回結果是生成器類型,利用for循環輸出了相應內容。
如果要得到所有的子孫節點,則可以調用descendants屬性:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.descendants)
for i, child in enumerate(soup.p.descendants):print(i, child)
2.7.2 父節點和祖先節點
如果要獲取某個節點元素的父節點,可以掉用parents屬性:
html = """
<html><head><title>The Dormouse'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" class="sister" id="link1"><span>Elsie</span></a></p><p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.a.parent)
這里選的是第一個a節點的父節點元素,a節點的父節點是p節點,所以輸出結果是p節點及其內部內容。
如果想要獲取所有祖先節點,可以調用parents屬性:
html = """
<html><body><p class="story"><a href="http://example.com/elsie" class="sister" id="link1"><span>Elsie</span></a></p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(type(soup.a.parents))
print(list(enumerate(soup.a.parents)))
2.7.3 兄弟節點
如果想要獲取同級節點,也就是兄弟節點:
html = """
<html><body><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"><span>Elsie</span></a>Hello<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>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print('Next Sibling', soup.a.next_sibling)
print('Prev Sibling', soup.a.previous_sibling)
print('Next Siblings', list(enumerate(soup.a.next_siblings)))
print('Prev Siblings', list(enumerate(soup.a.previous_siblings)))
可以看到,這里調用了4個屬性。next_sibling和previous_sibling分別用于獲取節點的下一個和上一個兄弟節點,next_siblings和previous_siblings則分別返回后面和前面的所有兄弟節點。
2.7.4 提取信息
關聯元素的信息獲取,代碼如下:
html = """
<html><body><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">Bob</a><a href="http://example.com/lacie" class="sister" id="link2">Lacie</a></p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print('Next Sibling:')
print(type(soup.a.next_sibling))
print(soup.a.next_sibling)
print(soup.a.next_sibling.string)
print('Parent:')
print(type(soup.a.parents))
print(list(soup.a.parents)[0])
print(list(soup.a.parents)[0].attrs['class'])
如果返回結果是單個節點,可以直接掉用string、attrs等屬性獲取其文本和屬性;如果返回結果是包含多個節點的生成器,則可以現將結果轉為列表,再從中取出某個元素,之后調用string、attrs等屬性即可獲取對應節點的文本和屬性。
2.8 方法選擇器
利用以下方法可以靈活查詢相應參數:
2.8.1 find_all
查詢所有符合條件的元素,可以給它傳入一些屬性或文本得到符合條件的元素,API如下:
find_all(name,attrs,recursive,text,**kwargs)
2.8.2 name
根據name參數查詢元素,代碼如下:
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>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))
調用find_all方法,向其中傳入name參數,其參數值為ul,意思是查詢所有ul節點。
列表中每個元素都是bs4.element.Tag類型,依然可以進行嵌套查詢。代碼:
for ul in soup.find_all(name='ul'):print(ul.find_all(name='li'))
返回結果還是列表類型,列表中每個元素依然是Tag類型。接下來遍歷每個li節點,獲取文本內容:
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)
2.8.3 attrs
除了根據節點名查詢,也可以傳入一些屬性進行查詢:
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>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={'id':'lisy-1'}))
print(soup.find_all(attrs={'name':'elements'}))
另一種查詢方式:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(id="lisy-1"))
print(soup.find_all(class="elements"))
2.8.4 text
text參數可以用來匹配節點的文本,其傳入形式可以是字符串,也可以是正則表達式形式,代碼:
import re
html='''
<div class="panel"><div class="panel-body"><a>Hello, this is a link</a><a>Hello, this is a link, too</a></div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(text=re.compile('link')))
這里有兩個a節點,其內部包含文本信息,find_all方法中傳入text參數,該參數為正則表達式,返回結果是由所有與正則表達式相匹配的節點文本組成的元素。
2.8.5 find
find方法可以查詢符合條件的元素,但是find方法返回的是單個元素,也就是第一個匹配的元素,而find_all會返回由所有匹配的元素組成的列表,代碼:
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>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find(name='ul'))
print(type(soup.find(name='ul')))
print(soup.find(class_='list'))
2.8.6?其他查詢方法
2.9 CSS選擇器
使用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"><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>
'''
from bs4 import BeautifulSoupsoup = BeautifulSoup(html, 'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))
返回結果均是符合css選擇器的節點組成的列表,例如select('ul li')表示選擇所有ul節點下面的所有li節點,結果便是所有li節點組成的列表。
2.9.1 嵌套選擇
例如,先選擇所有ul節點,再遍歷每個ul節點,選擇其li節點,代碼:
?
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
for ul in soup.select('ul'):print(ul.select('li'))
2.9.2 獲取屬性
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
for ul in soup.select('ul'):print(ul['id'])print(ul.attrs['id'])
2.9.3 獲取文本
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
for li in soup.select('li'):print('Get Text:', li.get_text())print('String:', li.string)
2.9.4?總結
3 pyquery
功能更強大的庫
3.1 準備工作
pip3 install pyquery
3.2 初始化
在用pyquery庫解析html文本的時候,需要先將其初始化為一個PyQuery對象。
3.2.1 字符串初始化
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('li'))
3.2.2 URL初始化
初始化的參數除了能以字符串形式傳遞,還能是網頁的url,此時只需要指定PyQuery對象的參數url即可:
from pyquery import PyQuery as pq
doc = pq(url='https://cuiqingcai.com')
print(doc('title'))
下面代碼實現的功能是相同的:
from pyquery import PyQuery as pq
import requests
doc = pq(requests.get('https://cuiqingcai.com').text)
print(doc('title'))
3.2.3 文件初始化
還可以傳遞本地的文件名,此時將參數指定為filename即可:
from pyquery import PyQuery as pq
doc = pq(filename = 'demo.html')
print(doc('li'))
3.3 基本CSS選擇器
實例:
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('#container .list li'))
print(type(doc('#container .list li')))
運行結果如下:
采用直接遍歷獲取的節點,調用text方法,可以直接獲取節點的文本內容,代碼:
for item in doc('#container .list li').items():print(item.text())
3.4 查找節點
3.4.1 子節點
查找子節點時,需要用到find方法,其參數時css選擇器,代碼:
from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.list')
print(type(items))
lis = items.find('li')
print(type(lis))
print(lis)
這里我們選擇class為list的節點,然后調用find方法,并將其傳入css選擇器,選取其內部的li節點,最后打印輸出。
此外,如果想只查找子節點,可以用children方法:
lis = items.children()
print(type(lis))
print(lis)
如果要篩選所有子節點中符合條件的節點,例如想篩選出子節點中class為active的節點,則可以向children方法傳入css選擇器.active,代碼:
lis = items.children('.active')
print(lis)
3.4.2 父節點
用parent方法獲取某個節點的父節點,代碼:
html = '''
<div class='wrap'><div id='container'><ul class="list"><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
print(doc('.list'))
container = items.parent()
print(type(container))
print(container)
這里我們首先用.list選取class為list的節點,然后調用parent方法得到其父節點,其類型依然是PyQuery。
如果想獲取某個祖先節點,可以用parents方法:
如果想要篩選某個祖先節點,可以向parents方法傳入css選擇器,就會返回祖先節點中符合css選擇器的節點:
parent = items.parents('.wrap')
print(parent)
3.4.3 兄弟節點
獲取兄弟節點可以使用siblings方法:
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings())
這里首先選擇class為list的節點內部的class為item-0和active的節點,也就是第三個li節點。
如果要篩選某個兄弟節點,依然可以向siblings方法中傳入css選擇器,這樣就能從所有兄弟節點中挑選出符合條件的節點:
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.list .item-0.active')
print(li.siblings('.active'))
運行結果如下:
3.5 遍歷節點
pyquery庫的選擇結果可能是多個節點,也可能是單個節點,類型都是pyquery類型,但不像beautiful soup那樣返回列表。
如果結果是單個節點,既可以直接打印輸出,也可以直接轉成字符串:
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
print(str(li))
如果結果是多個節點,就可以通過items方法遍歷獲取:
from pyquery import PyQuery as pq
doc = pq(html)
lis = doc('li').items()
print(type(lis))
for li in lis:print(li,type(li))
需要獲取的信息一般包括屬性和文本:
3.5.1?獲取屬性
html = '''
<div class='wrap'><div id='container'><ul class="list"><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active')
print(a,type(a))
print(a.attr('href'))
此外,也可以通過調用attr屬性來獲取屬性值:
print(a.attr.href)
如果選中的多個元素,調用attr方法,智慧得到第一個節點的屬性:
a = doc('a')
print(a,type(a))
print(a.attr('href'))
print(a.attr.href)
如果想要獲取a節點的所有屬性,需要遍歷:
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('a')
for item in a.items():print(item.attr('href'))
3.5.2?獲取文本
獲取節點之后的另一個主要操作就是獲取其內部的文本,調用text方法實現:
html = '''
<div class='wrap'><div id='container'><ul class="list"><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active')
print(a)
print(a.text)
如果想要獲取節點內部的html文本,需要用到html方法:
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
print(li.html())
如果我們選中的是多個節點,html方法返回的是第一個li節點內部的html文本,而text返回了所有li節點內部的純文本,各節點內部中間用一個空格分隔開,返回結果是一個字符串。
html = '''
<div class='wrap'><div id='container'><ul class="list"><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('li')
print(li.html())
print(li.text())
print(type(li,text())
3.6 節點操作
pyquery庫提供了一系列方法對節點進行動態修改,例如為某個節點添加一個class,移除某個節點等。
3.6.1 addClass和removeClass
html = '''
<div class='wrap'><div id='container'><ul class="list"><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.removeClass('active')
print(li)
li.addClass('active')
print(li)
3.6.2 attr、text和html
html = '''
<ul class="list"><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li></ul>
'''
from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.attr('name','link')
print(li)
li.text('changed item')
print(li)
li.html('<span>changed item</span>')
print(li)
3.6.3 remove
html = '''
<div class='wrap'>Hello,world<p>This is a paragraph.</p>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)
wrap = doc('.wrap')
print(wrap.text())
3.7 偽類選擇器
html = '''
<div class='wrap'><div id='container'><ul class="list"><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
</div>
'''
from pyquery import PyQuery as pq
doc = pq(html)# 選擇第一個 li 節點
li = doc('li:first-child')
print("第一個 li 節點:")
print(li)
print()# 選擇最后一個 li 節點
li = doc('li:last-child')
print("最后一個 li 節點:")
print(li)
print()# 選擇第奇數個 li 節點
li = doc('li:nth-child(odd)')
print("第奇數個 li 節點:")
print(li)
print()# 選擇第偶數個 li 節點
li = doc('li:nth-child(even)')
print("第偶數個 li 節點:")
print(li)
print()# 選擇包含文本 "second" 的 li 節點
li = doc('li:contains("second")')
print('包含文本 "second" 的 li 節點:')
print(li)
print()# 選擇類名為 "active" 的 li 節點
li = doc('li.active')
print('類名為 "active" 的 li 節點:')
print(li)
print()# 選擇有子元素 a 的 li 節點
li = doc('li:has(a)')
print('有子元素 a 的 li 節點:')
print(li)
print()
4 parsel 的使用
4.1 提取數據?
使用 selector.css('.item-0')
提取所有類名為 item-0
的 <li>
元素。使用 selector.xpath('//li[contains(@class, "item-0")]')
提取所有包含 item-0
類的 <li>
元素。打印提取到的元素數量、類型和內容。
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from parsel import Selectorselector = Selector(text=html)
items = selector.css('.item-0')
print(len(items), type(items), items)
items2 = selector.xpath('//li[contains(@class, "item-0")]')
print(len(items2), type(items), items2)
4.2 提取文本內容
使用 CSS 選擇器 .item-0
提取所有類名為 item-0
的 <li>
元素。遍歷提取到的元素,使用 XPath 表達式 .//text()
提取每個元素及其子元素中的所有文本內容。使用 CSS 選擇器 .item-0 *::text
提取所有 .item-0
元素的子元素中的文本節點,并獲取所有結果。
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from parsel import Selector
selector = Selector(text=html)
items = selector.css('.item-0')
for item in items:text = item.xpath('.//text()').get()print(text)# result = selector.xpath('//li[contains(@class, "item-0")]//text()').get()
# print(result)result = selector.css('.item-0 *::text').getall()
print(result)# result = selector.css('.item-0::text').get()
# print(result)
4.3?提取特定的文本內容
selector.xpath('//li[contains(@class, "item-0")]//text()')
使用 XPath 表達式提取所有 <li>
元素中包含 item-0
類的文本內容。//li[contains(@class, "item-0")]
:匹配所有 <li>
元素,其 class
屬性包含 item-0
。//text()
:提取匹配到的 <li>
元素及其子元素中的所有文本節點。.getall()
獲取所有提取到的文本節點,返回一個列表。
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from parsel import Selector
selector = Selector(text=html)
result = selector.xpath('//li[contains(@class, "item-0")]//text()').getall()
print(result)# result = selector.css('.item-0::text').get()
# print(result)
4.4?提取特定的屬性值
selector.xpath('//li[contains(@class, "item-0") and contains(@class, "active")]/a/@href')
使用 XPath 表達式提取同時具有 item-0
和 active
類的 <li>
元素中的 <a>
標簽的 href
屬性值。//li[contains(@class, "item-0") and contains(@class, "active")]
:匹配同時具有 item-0
和 active
類的 <li>
元素。/a/@href
:提取 <a>
標簽的 href
屬性值。.get()
獲取提取到的第一個屬性值。
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from parsel import Selector
selector = Selector(text=html)
result = selector.css('.item-0.active a::attr(href)').get()
print(result)
result = selector.xpath('//li[contains(@class, "item-0") and contains(@class, "active")]/a/@href').get()
print(result)
4.5 提取屬性
對于每個匹配到的 .item-0
元素,正則表達式 link.*
會匹配其文本內容中以 "link" 開頭的部分。具體來說:
-
第一個
.item-0
元素的文本是 "first item",不匹配正則表達式。 -
第二個
.item-0
元素的文本是 "third item",不匹配正則表達式。 -
第三個
.item-0
元素的文本是 "fifth item",不匹配正則表達式。
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from parsel import Selector
selector = Selector(text=html)
result = selector.css('.item-0').re('link.*')
print(result)
4.6?正則提取
selector.css('.item-0')
提取所有類名為 item-0
的 <li>
元素。.re_first('<span class="bold">(.*?)</span>')
使用正則表達式 <span class="bold">(.*?)</span>
提取匹配的內容,并返回第一個匹配結果。<span class="bold">(.*?)</span>
:匹配 <span class="bold">
開頭,</span>
結尾的內容,并捕獲中間的任意字符(非貪婪模式)。
html = '''
<div><ul><li class="item-0">first item</li><li class="item-1"><a href="link2.html">second item</a></li><li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li><li class="item-1 active"><a href="link4.html">fourth item</a></li><li class="item-0"><a href="link5.html">fifth item</a></li></ul></div>
'''
from parsel import Selector
selector = Selector(text=html)
result = selector.css('.item-0').re_first('<span class="bold">(.*?)</span>')
print(result)
?
?來源:
?