溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

lxml與pyquery解析html的方法

發(fā)布時(shí)間:2021-06-24 11:44:46 來(lái)源:億速云 閱讀:152 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“l(fā)xml與pyquery解析html的方法”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“l(fā)xml與pyquery解析html的方法”吧!

lxml

首先來(lái)了解一下lxml,很多常用的解析html的庫(kù)都用到了lxml這個(gè)庫(kù),例如BeautifulSoup、pyquery。

下面我們介紹一下lxml關(guān)于html解析的3個(gè)Element。

_Element

_Element獲取
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first</a></li>
         <li class="item-1"><a href="link2.html">second</a></li>
         <li class="item-0"><a href="link3.html">third</a></li>
     </ul>
 </div>
'''

# lxml.etree._Element
element = etree.HTML(text)
_Element常用方法
# 通過(guò)css選擇器獲取節(jié)點(diǎn)
cssselect(expr)

# 通過(guò)標(biāo)簽或者xpath語(yǔ)法獲取第一個(gè)匹配
find(path)

# 通過(guò)標(biāo)簽或者xpath語(yǔ)法獲取所有匹配
findall(path)

# 獲取屬性值
get(key)

# 獲取所有屬性
items()

# 獲取所有屬性名稱
keys()

# 獲取所有屬性值
values()

# 獲取子節(jié)點(diǎn)
getchildren()

# 獲取父節(jié)點(diǎn)
getparent()

# 獲取相鄰的下一個(gè)節(jié)點(diǎn)
getnext()

# 獲取相鄰的上一個(gè)節(jié)點(diǎn)
getprevious()

# 迭代節(jié)點(diǎn)
iter(tag)

# 通過(guò)xpath表達(dá)式獲取節(jié)點(diǎn)
xpath(path)
_Element示例
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first</a></li>
         <li class="item-1"><a href="link2.html">second</a></li>
         <li class="item-0" rel="third"><a href="link3.html">third</a></li>
     </ul>
 </div>
'''

element = etree.HTML(text)

# css選擇器,獲取class為item-0的li節(jié)點(diǎn)
lis = element.cssselect("li.item-0")

for li in lis:
    # 獲取class屬性
    print(li.get("class"))
    # 獲取屬性名稱和值,元組列表
    print(li.items())
    # 獲取節(jié)點(diǎn)所有的屬性名稱
    print(li.keys())
    # 獲取所有屬性值
    print(li.values())

print("--------------")

ass = element.cssselect("li a")
for a in ass:
    # 獲取文本節(jié)點(diǎn)
    print(a.text)

print("--------------")

# 獲取第一個(gè)li節(jié)點(diǎn)
li = element.find("li")

# 獲取所有l(wèi)i節(jié)點(diǎn)
lis = element.find("li")

# 獲取所有的a節(jié)點(diǎn)
lias = element.iter("a")
for lia in lias:
    print(lia.get("href"))

textStr = element.itertext("a")
for ts in textStr:
    print(ts)

xpath我們后面單獨(dú)介紹。

_ElementTree

_ElementTree獲取
from io import StringIO
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first</a></li>
         <li class="item-1"><a href="link2.html">second</a></li>
         <li class="item-0"><a href="link3.html">third</a></li>
     </ul>
 </div>
'''

parser = etree.HTMLParser()
# lxml.etree._ElementTree
elementTree = etree.parse(StringIO(text), parser)
# 可以直接從文件讀取
# elementTree = etree.parse(r'F:\tmp\etree.html',parser)
_ElementTree常用方法
find(path)
findall(path)
iter(tag)
xpath(path)

_ElementTree方法和 _Element的同名方法使用基本一樣。

有很多不同的是_ElementTree的find和findall方法只接受xpath表達(dá)式。

_ElementTree示例
from io import StringIO
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first</a></li>
         <li class="item-1"><a href="link2.html">second</a></li>
         <li class="item-0"><a href="link3.html">third</a></li>
     </ul>
 </div>
'''

parser = etree.HTMLParser()
elementTree = etree.parse(StringIO(text), parser)

lis = elementTree.iter("li")
for li in lis:
    print(type(li))

print("---------")

firstLi = elementTree.find("//li")
print(type(firstLi))
print(firstLi.get("class"))

print("---------")

ass = elementTree.findall("//li/a")
for a in ass:
    print(a.text)

HtmlElement

HtmlElement獲取
import lxml.html

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first</a></li>
         <li class="item-1"><a href="link2.html">second</a></li>
         <li class="item-0"><a href="link3.html">third</a>
     </ul>
 </div>
'''

# lxml.html.HtmlElement
htmlElement = lxml.html.fromstring(text)

HtmlElement繼承了etree.ElementBase和HtmlMixin,etree.ElementBase繼承了_Element。

因?yàn)镠tmlElement繼承了_Element,所以_Element中介紹的方法,HtmlElement都可以使用。 HtmlElement還可以使用HtmlMixin中的方法。

HtmlMixin常用方法
# 通過(guò)類名獲取節(jié)點(diǎn)
find_class(class_name)
# 通過(guò)id獲取節(jié)點(diǎn)
get_element_by_id(id)
# 獲取文本節(jié)點(diǎn)
text_content()
# 通過(guò)css選擇器獲取節(jié)點(diǎn)
cssselect(expr)

xpath

xpath功能非常強(qiáng)大,并且_Element、_ElementTree、HtmlElement都可以使用xpath表達(dá)式,所以最后介紹一下xpath。

表達(dá)式描述
/從根節(jié)點(diǎn)開始,絕對(duì)路徑
//從當(dāng)前節(jié)點(diǎn)選取子孫節(jié)點(diǎn),相對(duì)路徑,不關(guān)心位置
.選取當(dāng)前節(jié)點(diǎn)
..選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
@選取屬性
*通配符,選擇所有元素節(jié)點(diǎn)與元素名
@*選取所有屬性
[@attrib]選取具有給定屬性的所有元素
[@attrib='value']選取給定屬性具有給定值的所有元素
[tag]選取所有具有指定元素的直接子節(jié)點(diǎn)
[tag='text']選取所有具有指定元素并且文本內(nèi)容是text節(jié)點(diǎn)
expression表達(dá)式描述
ancestorxpath('./ancestor:: *')選取當(dāng)前節(jié)點(diǎn)的所有先輩節(jié)點(diǎn)
ancestor-or-self('./ancestor-or-self:: *')選取當(dāng)前節(jié)點(diǎn)的所有先輩以及節(jié)點(diǎn)本身
attributexpath('./attribute:: *')選取當(dāng)前節(jié)點(diǎn)的所有屬性
childxpath('./child:: *')返回當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)
descendantxpath('./descendant:: *')返回當(dāng)前節(jié)點(diǎn)的所有后代節(jié)點(diǎn)(子節(jié)點(diǎn)、孫節(jié)點(diǎn))
followingxpath('./following:: *')選取文檔中當(dāng)前節(jié)點(diǎn)結(jié)束標(biāo)簽后的所有節(jié)點(diǎn)
following-sibingxpath('./following-sibing:: *')選取當(dāng)前節(jié)點(diǎn)之后的兄弟節(jié)點(diǎn)
parentxpath('./parent:: *')選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
precedingxpath('./preceding:: *')選取文檔中當(dāng)前節(jié)點(diǎn)開始標(biāo)簽前的所有節(jié)點(diǎn)
preceding-siblingxpath('./preceding-sibling:: *')選取當(dāng)前節(jié)點(diǎn)之前的兄弟節(jié)點(diǎn)
selfxpath('./self:: *')選取當(dāng)前節(jié)點(diǎn)

lxml與pyquery解析html的方法

很多時(shí)候我們可以通過(guò)瀏覽器獲取xpath表達(dá)式:

lxml與pyquery解析html的方法

示例
from lxml.html.clean import Cleaner
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0" rel="li1"><a href="link1.html">first</a></li>
         <li class="item-1" rel="li2"><a href="link2.html" rel="attrRel">second</a></li>
         <li class="item-0" rel="li3"><a href="link3.html" id="thirda">third</a>
     </ul>
 </div>
'''

# 去除css、script
cleaner = Cleaner(style=True, scripts=True, page_structure=False, safe_attrs_only=False)
print(cleaner.clean_html(text))

# _Element
element = etree.HTML(text)

# 文本節(jié)點(diǎn),特殊字符轉(zhuǎn)義
print(element.xpath('//text()'))

# 文本節(jié)點(diǎn),不轉(zhuǎn)義
print(element.xpath('string()'))

# find、findall只能使用相對(duì)路徑,以.//開頭
print(element.findall('.//a[@rel]'))
print(element.find('.//a[@rel]'))

# 獲取包含rel屬性的a節(jié)點(diǎn)
print(element.xpath('//a[@rel]'))

# 獲取ul元素下的第一個(gè)li節(jié)點(diǎn),注意是列表,因?yàn)閡l可能有多個(gè)
print(element.xpath("//ul/li[1]"))

# 獲取ul元素下rel屬性為li2的li節(jié)點(diǎn)
print(element.xpath("//ul/li[@rel='li2']"))

# 獲取ul元素下的倒數(shù)第2個(gè)節(jié)點(diǎn)
print(element.xpath("//ul/li[last()-1]"))

# 獲取ul元素下的前2個(gè)li節(jié)點(diǎn)
print(element.xpath("//ul/li[position()<3]"))

# 獲取li元素下的所有a節(jié)點(diǎn)
for a in element.xpath("//li/a"):
    print(a.text)
    print(a.get("href"))

# 獲取父節(jié)點(diǎn),列表,因?yàn)榭赡芷ヅ涠鄠€(gè)a
print(element.xpath('//a[@href="link2.html"]/parent::*'))

# 獲取的是文本節(jié)點(diǎn)對(duì)象列表
print(element.xpath('//li[@class="item-1"]/a/text()'))

print("---------------")
# 獲取a的href屬性
print(element.xpath('//li/a/@href'))
# 獲取所有l(wèi)i子孫節(jié)點(diǎn)的href屬性
print(element.xpath('//li//@href'))

xpath示例

from lxml import etree

text = '''
<li class="subject-item">
    <div class="pic">
      <a class="nbg" href="https://book.douban.com/subject/25862578/">
        <img class="" src="https://img3.doubanio.com/view/subject/m/public/s27264181.jpg" width="90">
      </a>
    </div>
    <div class="info">
      <h3 class=""><a href="https://book.douban.com/subject/25862578/" title="解憂雜貨店">解憂雜貨店</a></h3>
      <div class="pub">[日] 東野圭吾 / 李盈春 / 南海出版公司 / 2014-5 / 39.50元</div>
      <div class="star clearfix">
        <span class="allstar45"></span>
        <span class="rating_nums">8.5</span>
        <span class="pl">
            (537322人評(píng)價(jià))
        </span>
      </div>
      <p>現(xiàn)代人內(nèi)心流失的東西,這家雜貨店能幫你找回——僻靜的街道旁有一家雜貨店,只要寫下煩惱投進(jìn)卷簾門的投信口,第二天就會(huì)在店后的牛奶箱里得到回答。因男友身患絕... </p>
    </div>
</li>
'''

element = etree.HTML(text)
# 查找符合xpath(//li/div/a)的節(jié)點(diǎn)
aeles = element.xpath("//li/div/a")
for aele in aeles:
    # 獲取href屬性
    print(aele.get("href"))
    # 查找img標(biāo)簽,并且獲取src屬性
    print(aele.find("img").get("src"))

# 返回列表的原因是:雖然我們只取了第一個(gè)a節(jié)點(diǎn),但是上級(jí)xpath(//li/div[@class='info']/h3)可能匹配多個(gè)
for a in element.xpath("//li/div[@class='info']/h3/a[1]"):
    print(a.get("href"))
    print(a.text)
    # print(a.get("title"))

# 指定div的class屬性
for pu in element.xpath("//li/div[@class='info']/div[@class='pub']"):
    print(pu.text)

# 使用到contains函數(shù)和or運(yùn)算符
spans = element.xpath("//li/div[@class='info']/div[@class='star clearfix']/span[contains(@class,'rating_nums') or "
                      "contains(@class,'pl')]")

for span in spans:
    print(span.text.strip())

for content in element.xpath("//li/div[@class='info']/p"):
    print(content.text)

# 如果確定只有一個(gè)或者只需要第一個(gè)可以使用find,注意find使用xpath為參數(shù)的時(shí)候使用相對(duì)路徑(.//開頭)
print(element.find(".//li/div[@class='info']/p").text)

pyquery

構(gòu)造PyQuery

從字符串:

from pyquery import PyQuery as pq

html = ''
with open(r"F:\tmp\db.html", "r", encoding='utf-8') as f:
    html = f.read()
doc = pq(html)

從URL:

from pyquery import PyQuery as pq

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0'
}
doc = pq('https://book.douban.com/tag/小說(shuō)', headers=headers)

從文件:

from pyquery import PyQuery as pq
doc = pq(filename=r"F:\tmp\db.html")

從文件有一個(gè)問(wèn)題就是不能指定文件編碼,所以一般都是自己讀取文件,然后從字符串構(gòu)造。

選擇器

pyquery最強(qiáng)大的地方就在于,它可以像jQuery使用css選擇器一樣獲取節(jié)點(diǎn)。

常用的一些選擇器:

id選擇器(#id) 類選擇器(.class) 屬性選擇器(a[href="xxx"]) 偽類選擇器(:first :last :even :odd :eq :lt :gt :checked :selected)

前面我們已經(jīng)知道怎樣構(gòu)造一個(gè)PyQuery,上面我們有知道了怎么通過(guò)選擇器獲取節(jié)點(diǎn),下面我們通過(guò)一個(gè)小示例來(lái)具體了解一下。

from pyquery import PyQuery as pq

html = '''
    <!DOCTYPE html>
    <html>
    <head>
        <title>pyquery</title>
    </head>
    <body>
         <ul id="container">
            <li class="li1">li1</li>
            <li class="li2">li2</li>
            <li class="li3" data-type='3'>li3</li>
        </ul>
    </body>
    </html>
'''

doc = pq(html)

# id選擇器,outerHtml輸出整體的html
print(doc("#container").outerHtml())

print("----------")

# 類選擇器
print(doc(".li1").outerHtml())

print("----------")

# 偽類選擇器
# 選擇第2個(gè)li節(jié)點(diǎn),并通過(guò)text獲取該li節(jié)點(diǎn)的值
print(doc('li:nth-child(2)').text())

# 獲取第1個(gè)li節(jié)點(diǎn)
print(doc('li:first-child').text())

# 獲取最后一個(gè)li節(jié)點(diǎn),并通過(guò)attr獲取該節(jié)點(diǎn)的data-type屬性值
print(doc('li:last-child').attr("data-type"))
print(doc("li:contains('li3')").attr("data-type"))

print("----------")

# 屬性選擇器
# 選擇li的data-type的屬性值為3的節(jié)點(diǎn)
print(doc("li[data-type='3']").outerHtml())

首先我們通過(guò)html字符串構(gòu)造了一個(gè)PyQuery對(duì)象,然后就可以通過(guò)選擇器愉快的獲取我們想要的節(jié)點(diǎn)了。

注意:上面的text方法是獲取節(jié)點(diǎn)的文本,attr是獲取節(jié)點(diǎn)的屬性,非常常用

這里就補(bǔ)貼輸出代碼了,如果感興趣可以自己動(dòng)手嘗試一下,看一下輸出。

查找與過(guò)濾節(jié)點(diǎn)

很多時(shí)候,我們并不能直接通過(guò)選擇器一步到位的獲取到我們需要的節(jié)點(diǎn),所以我們需要另外一些查找、過(guò)濾、遍歷節(jié)點(diǎn)的方法,例如:find、filter、eq、not_、items、each等,下面我們還是通過(guò)一個(gè)小例子來(lái)介紹一下這些方法。

from pyquery import PyQuery as pq

html = '''
    <!DOCTYPE html>
    <html>
    <head>
        <title>pyquery</title>
    </head>
    <body>
         <ul id="container">
            <li class="li1">li1</li>
            <li class="li2">li2</li>
            <li class="li3" data-type='3'>li3</li>
        </ul>
    </body>
    </html>
'''

doc = pq(html)

# find的語(yǔ)法和直接使用選擇器一樣
print("---find:")
print(doc.find("li").show())
# 輸出li的個(gè)數(shù)
print(doc.find("li").size())


# filter過(guò)濾得到滿足條件的
print("---filter:")
print(doc.find("li").filter(".li3").show())

# eq選擇第n個(gè),下標(biāo)從0開始
print("---eq:")
print(doc.find("li").eq(1).show())

# not_排除滿足條件的節(jié)點(diǎn)
print("---not_:")
print(doc.find("li").not_("li[data-type='3']").show())


lis = doc.find("li")
# 輸出PyQuery
print(type(lis))

# each輸出的類型是lxml.etree._Element
print("---each:")
lis.each(lambda i, e: print(type(e)))

print("---for:")
for li in lis:
    print(type(li))

# items獲取到的類型才是PyQuery
print("---items:")
for li in lis.items():
    print(type(li))

這些方法還是比較基礎(chǔ)的,看代碼中的注釋就能知道是什么意思了,如果有疑問(wèn),可以自己動(dòng)手調(diào)試一下。

注意lis是PyQuery類型,PyQuery的each是lxml.etree._Element類型,items才是PyQuery

這意味著使用for\each循環(huán)不能使用PyQuery的find、filter、text、attr這些方法。

需要使用lxml.etree._Element的方法。

到此,相信大家對(duì)“l(fā)xml與pyquery解析html的方法”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI