日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

正則表達(dá)式學(xué)廢了?xpath來(lái)救!

 昵稱73595512 2021-02-18

使用XPath

XPath,全稱XML Path Language,即XML路徑語(yǔ)言,它是在XML語(yǔ)言中查找信息的語(yǔ)言。它最初是用來(lái)搜尋XML文檔的,但是它同樣適用于HTML文檔的搜索。

在上一篇文章中講述了正則表達(dá)式的使用方法,正則表達(dá)式的難度還是比較大的,如果不花足夠多的時(shí)間去做的話還是比較難的,所以今天就來(lái)分享比正則簡(jiǎn)單的內(nèi)容,方便大家接下來(lái)的學(xué)習(xí)。

XPath常用規(guī)則

XPath的規(guī)則是非常豐富的,本篇文章無(wú)法一次性全部概括,只能為大家介紹幾個(gè)常用的規(guī)則。

表達(dá)式描述
nodename選取此節(jié)點(diǎn)的所有子節(jié)點(diǎn)
/從當(dāng)前節(jié)點(diǎn)選取直接子節(jié)點(diǎn)
//從當(dāng)前節(jié)點(diǎn)選取子孫節(jié)點(diǎn)
.選取當(dāng)前子節(jié)點(diǎn)
..選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)
@選取屬性

準(zhǔn)備工作

在使用之前得先安裝好lxml這個(gè)庫(kù),如果沒有安裝請(qǐng)參考下面的安裝方式。

pip install lxml

案例導(dǎo)入

現(xiàn)在通過(guò)實(shí)例來(lái)xpath對(duì)網(wǎng)頁(yè)解析的過(guò)程

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"></a>second-item</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">fifith-item</a></li>


</div>
'''

html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))

這里首先通過(guò)lxml這個(gè)庫(kù)導(dǎo)入etree這個(gè)模塊,然后聲明一段HTML文本,調(diào)用HTML類進(jìn)行初始化,這就成功構(gòu)造了xpath對(duì)象。

細(xì)心的讀者朋友應(yīng)該會(huì)發(fā)現(xiàn)我上面的代碼片段中標(biāo)簽ul是沒有閉合的,但是運(yùn)行之后你會(huì)發(fā)現(xiàn)運(yùn)行結(jié)果是閉合的,并且還自動(dòng)添加了htmlbody標(biāo)簽。

這是因?yàn)槲覀冋{(diào)用了tostring( )方法幫助我們將HTML文本進(jìn)行修正,但是要注意的是tostring( )方法返回的結(jié)果是byte類型,因此這里調(diào)用了tostring( )方法即可輸出修正后的HTML代碼。使用decode( )方法可以將byte類型的數(shù)據(jù)轉(zhuǎn)成str類型的數(shù)據(jù)。

當(dāng)然,etree這個(gè)模塊也可以直接讀取文本文件進(jìn)行解析,具體代碼如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))

其中文件test.html的內(nèi)容就是上面示例的HTML代碼。

獲取所有的節(jié)點(diǎn)

我們一般會(huì)使用 // 開頭的Xpath規(guī)則來(lái)選取所有符合要求的節(jié)點(diǎn),假如我需要獲取所有的節(jié)點(diǎn),示例代碼如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)

首先對(duì)上面的代碼做簡(jiǎn)單的說(shuō)明,這里的 * 代表匹配全部,所以所有的節(jié)點(diǎn)都會(huì)獲取到,返回值是一個(gè)列表。

每個(gè)元素是Element類型,其中后面跟的就是節(jié)點(diǎn)的名稱。

運(yùn)行結(jié)果如下所示:

[<Element html at 0x1a0334c39c0>, <Element body at 0x1a0334c3a80>, <Element div at 0x1a0334c3ac0>, <Element ul at 0x1a0334c3b00>, <Element li at 0x1a0334c3b40>, <Element a at 0x1a0334c3bc0>, <Element li at 0x1a0334c3c00>, <Element a at 0x1a0334c3c40>, <Element li at 0x1a0334c3c80>, <Element a at 0x1a0334c3b80>, <Element li at 0x1a0334c3cc0>, <Element a at 0x1a0334c3d00>, <Element li at 0x1a0334c3d40>, <Element a at 0x1a0334c3d80>]

從上面的運(yùn)行結(jié)果你會(huì)發(fā)現(xiàn),html、body、div、ul、li等等節(jié)點(diǎn)。

獲取指定節(jié)點(diǎn)

例如,在這里我要獲取到所有的li節(jié)點(diǎn),那該怎么辦呢?其實(shí)很簡(jiǎn)單,具體代碼示例如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li')
print(result)

通過(guò)上面的幾個(gè)例子,不知道大家有沒有明白節(jié)點(diǎn)的含義。

其實(shí)節(jié)點(diǎn)的含義你可以理解為當(dāng)前的html文檔開始的地方。

如果上面的代碼你修改一段,變成這樣:

result = html.xpath('/li')

運(yùn)行之后你會(huì)發(fā)現(xiàn)列表是空的,因?yàn)樵撐臋n的的子節(jié)點(diǎn)中沒有 li 這個(gè)節(jié)點(diǎn),li 是該文檔的子孫節(jié)點(diǎn),而該文檔的子節(jié)點(diǎn)是html。

所以,你將代碼這樣修改:

result = html.xpath('/html')
# 另一種寫法
result = html.xpath('.')

運(yùn)行之后你會(huì)驚喜的發(fā)現(xiàn),成功獲取到了html節(jié)點(diǎn)。

子節(jié)點(diǎn)與子孫節(jié)點(diǎn)

通過(guò)/或//即可查好元素的子節(jié)點(diǎn)或者是子孫節(jié)點(diǎn),假如你想要選擇 li 節(jié)點(diǎn)下的所有 a 節(jié)點(diǎn)可以這樣實(shí)現(xiàn),具體代碼如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)

先對(duì)上面的代碼做簡(jiǎn)要的說(shuō)明://li表示獲取所有的li節(jié)點(diǎn),/a表示獲取 li 節(jié)點(diǎn)下的子節(jié)點(diǎn) a 。

或者也可以這樣寫,你可以先獲取到所有的 ul 節(jié)點(diǎn),再獲取 ul 節(jié)點(diǎn)下的所有子孫節(jié)點(diǎn) a 節(jié)點(diǎn)。

具體示例代碼如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//ul//a')    # 注意//a
print(result)

運(yùn)行上面的代碼你會(huì)發(fā)現(xiàn)結(jié)果是相同的。

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

通過(guò)上面的幾個(gè)例子,想必應(yīng)該知道何為子節(jié)點(diǎn)與子孫節(jié)點(diǎn)。那么如何尋找父節(jié)點(diǎn)呢?這里可以通過(guò) .. 來(lái)實(shí)現(xiàn)。

比如,我現(xiàn)在要選中href屬性為link4.html的a節(jié)點(diǎn),然后再獲取其父節(jié)點(diǎn),再獲取其class屬性??粗鴥?nèi)容好多,那就要一個(gè)一個(gè)來(lái),不要著急。

具體代碼示例如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)

運(yùn)行結(jié)果

['item-1']

屬性的匹配

在選取數(shù)據(jù)的時(shí)候,可以使用@符號(hào)進(jìn)行屬性的過(guò)濾,比如:這里通過(guò)選取 li 標(biāo)簽屬性class為item-0的節(jié)點(diǎn),可以這樣實(shí)現(xiàn):

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]')
print(result)

你可以試著運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn)匹配到了兩個(gè)正確的結(jié)果。

文本獲取

在整個(gè)HTML文檔中肯定會(huì)有很多的文本內(nèi)容,有些恰恰是我們需要的,那么應(yīng)該如何獲取這些文本內(nèi)容呢?

接下來(lái)可以嘗試使用text( )方法獲取節(jié)點(diǎn)中的文本。

具體代碼實(shí)例如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)

試著運(yùn)行上面的代碼,你會(huì)發(fā)現(xiàn),已經(jīng)獲取到了所有class屬性為item-0的 li 節(jié)點(diǎn)下的文本。

獲取標(biāo)簽屬性值

在編寫爬蟲的過(guò)程中,很多時(shí)候我們需要的數(shù)據(jù)可能是屬性值,那就要學(xué)會(huì)如何來(lái)獲取我們想要的屬性值了。

例如,我想要獲取 li 節(jié)點(diǎn)下的a節(jié)點(diǎn)的所有href屬性,具體代碼示例如下所示:

from lxml import etree


html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)

通過(guò)@href就獲取到了該節(jié)點(diǎn)的href屬性值,當(dāng)然,它們都是以列表的形式返回。

屬性多值的匹配

在編寫前端代碼的時(shí)候,有些節(jié)點(diǎn)為了方便可能就會(huì)存在多個(gè)值,那么就要使用contains函數(shù)了,例如:

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)

要是你說(shuō)我怎么記得住這些函數(shù),那好,還可以這樣寫。

具體代碼示例如下:

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 li-first"]/a/text()')
print(result)

看出區(qū)別了嗎?

運(yùn)行上面的兩段代碼,你會(huì)發(fā)現(xiàn)結(jié)果是一樣的。

多屬性匹配

另外,我們寫寫爬蟲的時(shí)候會(huì)遇到另一種情況,那就是在一個(gè)標(biāo)簽內(nèi)存在多個(gè)屬性。那此時(shí)可以用and操作符來(lái)連接

具體代碼示例如下所示:

from lxml import etree

text = '''
<li class="li li-first" name="item"><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)

xpath運(yùn)算符的簡(jiǎn)單介紹

從上面的示例你應(yīng)該知道了,and是xpath的運(yùn)算符,xpath的運(yùn)算符也是比較多的,那么接下來(lái)對(duì)xpath運(yùn)算符做簡(jiǎn)單的介紹。

運(yùn)算符描述
or
and
|計(jì)算兩個(gè)節(jié)點(diǎn)集,//li | //a 獲取li和a元素的節(jié)點(diǎn)集
+加法
-減法
*乘法
div除法
=等于
!=不等于
<小于
>大于
>=大于等于
<=小于等于
mod計(jì)算余數(shù)

按序選擇

有時(shí)候,我們編寫爬蟲的時(shí)候可能會(huì)匹配到幾個(gè)相同的 li 節(jié)點(diǎn),但是,我只需要第一個(gè)或者最后一個(gè)就可以了。那這時(shí)該怎么樣處理那?

這時(shí)可以通過(guò)索引的方式,傳入指定的索引,獲取指定節(jié)點(diǎn)。

具體代碼示例如下所示:

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"></a>second-item</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">fifith-item</a></li>
    </ul>

</div>
'''

html = etree.HTML(text)
# 獲取第一個(gè)li節(jié)點(diǎn)
result = html.xpath('//li[1]/a/text()')
print(result)
# 獲取最后一個(gè)li節(jié)點(diǎn)
result = html.xpath('//li[last()]/a/text()')
print(result)
# 獲取位置小于3的li節(jié)點(diǎn)
result = html.xpath('//li[position()<3]/a/text()')
print(result)
# 獲取倒數(shù)第三個(gè)li節(jié)點(diǎn)
result = html.xpath('//li[last()-2]/a/text()')
print(result)

上述內(nèi)容所描述的是xpath的在爬蟲應(yīng)用中常見使用方法,如果各位小伙伴想要繼續(xù)深入學(xué)習(xí)的話,可以參考w3c進(jìn)行學(xué)習(xí),網(wǎng)址如下:

https://www.w3school.com.cn/xpath/xpath_syntax.asp

實(shí)戰(zhàn)

上面的內(nèi)容是描述xpath的使用語(yǔ)法,建議大家要花一個(gè)小時(shí)左右的時(shí)間去練習(xí)。

那么接下來(lái)就帶大家進(jìn)入實(shí)戰(zhàn)演練了,乘熱打鐵是最好的學(xué)習(xí)方式。

今天我?guī)?lái)的內(nèi)容就是爬取必應(yīng)壁紙。

準(zhǔn)備工作

工欲善其事,必利其器。玩爬蟲也是同樣的道理。

首先,安裝好兩個(gè)庫(kù):lxml與requests

pip install lxml
pip install requests

需求分析

爬取的網(wǎng)址:https://bing./

抓包分析

首先打開開發(fā)者工具,隨便點(diǎn)擊一張圖片進(jìn)入它的高清大圖,點(diǎn)擊network進(jìn)行抓包,在點(diǎn)擊圖片的下載按鈕。

點(diǎn)擊下載按鈕之后,你會(huì)發(fā)現(xiàn),瀏覽器向圖中的網(wǎng)址發(fā)起了請(qǐng)求,點(diǎn)擊進(jìn)去之后發(fā)現(xiàn)這個(gè)就是高清圖片的鏈接地址。

從而我們的第一個(gè)需求就是獲取所有圖片的鏈接地址。

獲取圖片鏈接

為什么要獲取圖片鏈接呢?

首先,你思考一下,每一張圖片你都要點(diǎn)擊下載按鈕來(lái)將圖片保存到本地嗎?如果你不懂爬蟲那當(dāng)然沒有辦法了。但是,我們懂爬蟲的人還會(huì)這么干嗎?

既然每一次點(diǎn)擊下載按鈕,瀏覽器都是向?qū)?yīng)的高清大圖發(fā)起請(qǐng)求,那么也就是說(shuō)我們可以獲取到所有的圖片鏈接,然后利用Python模擬瀏覽器向這些鏈接發(fā)起請(qǐng)求,即可下載這些圖片。

鏈接如下:

https://h2./bing/LoonyDook_ZH-CN1879420705_1920x1080.jpg?imageslim

關(guān)于翻頁(yè)

打開網(wǎng)頁(yè)之后,你會(huì)發(fā)現(xiàn)起碼有100頁(yè)的圖片。那這100頁(yè)的圖片怎么樣獲取呢?

很簡(jiǎn)單,依然還是先分析每一頁(yè)的URL地址,看看有沒什么變化規(guī)律。

# 第二頁(yè)
https://bing./?p=2
# 第三頁(yè)
https://bing./?p=3

其實(shí)看到上面的URL變化之后,我想你也應(yīng)該明白了變化的規(guī)律了吧。

功能實(shí)現(xiàn)

構(gòu)造每一頁(yè)的鏈接

其實(shí)就是實(shí)現(xiàn)簡(jiǎn)單的翻頁(yè)功能。

具體代碼示例如下所示:

def get_page_url():
    page_url = []
    for i in range(1,148):
        url = f'https://bing./?p={i}'
        page_url.append(url)
    return page_url

上面代碼的功能是構(gòu)造每一頁(yè)的鏈接。將鏈接保存在page_url中。

獲取每一頁(yè)中的圖片鏈接

在上圖中你會(huì)發(fā)現(xiàn),圖片的鏈接就藏在了data-progressive里面,這不就是img標(biāo)簽的屬性嗎?有何難?

但是細(xì)心的朋友就會(huì)發(fā)現(xiàn),這個(gè)鏈接和我們最開始抓包的鏈接是不一樣的,到底哪里不一樣呢?

我們來(lái)具體看看

https://h2./bing/LoonyDook_ZH-CN1879420705_1920x1080.jpg?imageslim
http://h2./bing/LoonyDook_ZH-CN1879420705_640x480.jpg?imageslim

發(fā)現(xiàn)了嗎?分辨率是不一樣的。其他都相同的,那只要將分辨率替換掉就可以了呀。

具體代碼如下所示:

def get_img_url(urls):
    headers = {
        'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
    }
    img_urls = []
    count = 1
    for url in urls[:3]:
        time.sleep(5)
        text = requests.get(url, headers=headers).content.decode('utf-8')
        html = etree.HTML(text)
        img_url = html.xpath('//div[@class="item"]//img/@data-progressive')
        img_url = [i.replace('640x480''1920x1080'for i in img_url]
        print(f'正在獲取第{count}頁(yè)鏈接')

        img_urls.extend(img_url)
        count += 1
    return img_urls

上面的代碼是獲取每一頁(yè)的圖片鏈接,將鏈接保存在img_urls中。

保存圖片

 def save_img(self, img_urls):

        headers = {
            'User-Agent''Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
        }
        count = 1

        for img_url in img_urls:
            content = requests.get(img_url, headers=headers).content
            print(f'正在下載第{count}張')
            with open(f'../image/{count}.jpg''wb'as f:
                f.write(content)
            count += 1

保存圖片的代碼還是比較簡(jiǎn)單的,可以將獲取到的所有圖片鏈接作為參數(shù)傳進(jìn)來(lái),進(jìn)行逐個(gè)訪問(wèn),即可。

文章來(lái)源:https://www./blog-lyU3E5QA6g.htm

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多