python爬蟲系列之 html頁面解析:如何寫 xpath路徑

一、前言

上一節(jié)我們講了怎么批量下載壁紙,雖然爬蟲的代碼很簡(jiǎn)單,但是卻有一個(gè)很重要的問題,那就是 xpath路徑應(yīng)該怎么寫。

這個(gè)問題往往會(huì)被我們忽略,但 xpath路徑的寫法是很重要的。不同的 xpath路徑寫法會(huì)后續(xù)爬取代碼會(huì)產(chǎn)生很大影響,而且不同的 xpath寫法的穩(wěn)定性也不同,能不能寫出優(yōu)雅穩(wěn)定的代碼就要看 xpath寫得好不好了。

下面我們來講講為什么 xpath的寫法這么重要

二、為什么 xpath寫法很重要

我們拿幾個(gè)例子來講講不同 xpath寫法對(duì)代碼的影響,以我的個(gè)人主頁作為解析對(duì)象:

python爬蟲貓的個(gè)人主頁

現(xiàn)在的需求是要爬取我個(gè)人主頁里的文章列表,包括文章的鏈接、標(biāo)題、訪問量、評(píng)論數(shù)和點(diǎn)贊數(shù)量

個(gè)人主頁

爬之前我們先分析一下

1、爬什么:文章鏈接文章的鏈接、標(biāo)題、評(píng)論數(shù)和點(diǎn)贊數(shù)量

2、怎么爬:requests請(qǐng)求網(wǎng)頁、xpath解析網(wǎng)頁

接下來正式開始爬?。?/p>

第一步:分析網(wǎng)頁,寫出圖片的 xpath路徑

第二步:用 requests庫獲取網(wǎng)頁

第三步:使用 lxml庫解析網(wǎng)頁

第四步:把爬取到的信息保存下來

我們一步一步來,首先分析網(wǎng)頁,寫出 xpath

按 F12進(jìn)入開發(fā)者模式,找到文章列表所在的標(biāo)簽

example-2.png

可以看到,文章列表是一個(gè) ul標(biāo)簽,ul標(biāo)簽下的每一個(gè) li標(biāo)簽分別代表一篇文章。

我們要爬的信息都在 class="content"的 div標(biāo)簽下:

  • 文章鏈接是第一個(gè) a標(biāo)簽的 herf屬性值
  • 文章標(biāo)題是第一個(gè) a標(biāo)簽的文本屬性的值
  • 文章的評(píng)論數(shù)是 class="meta"的 div標(biāo)簽下的第二個(gè) a標(biāo)簽下的文本值
  • 文章點(diǎn)贊數(shù)量是 class="meta"的 div標(biāo)簽下的 span標(biāo)簽下的文本值

這時(shí)候 xpath有很多種寫法,我寫出其中的兩種,一好一壞,大家可以試著判斷一下哪個(gè)好哪個(gè)壞

第一種寫法:

xpath_link = '//ul[@class="note-list"]/li/div/a/@href'
xpath_title = '//ul[@class="note-list"]/li/div/a/text()'
xpath_comment_num = '//ul[@class="note-list"]/li/div/div[@class="meta"]/a[2]/text()'
xpath_heart_num = '//ul[@class="note-list"]/li/div/div[@class="meta"]/span/text()'

第二種寫法:

#獲取所有 li標(biāo)簽
xpath_items = '//ul[@class="note-list"]/li'
#對(duì)每個(gè) li標(biāo)簽再提取
xpath_link = './div/a/@href'
xpath_title = './div/a/text()'
xpath_comment_num = './/div[@class="meta"]/a[2]/text()'
xpath_heart_num = './/div[@class="meta"]/span/text()'

寫好 xpath之后,我們開始第二步,獲取網(wǎng)頁

獲取簡(jiǎn)書的網(wǎng)頁如果我們還像之前那樣直接請(qǐng)求的話,就會(huì)得到一個(gè) 403錯(cuò)誤,這是因?yàn)闆]有設(shè)置請(qǐng)求頭。

加上請(qǐng)求頭就能解決了[]( ̄▽ ̄)*

第一種 xpath寫法對(duì)應(yīng)的代碼:

#-*- coding: utf-8 -*
import requests
from lxml import etree


#請(qǐng)求頭和目標(biāo)網(wǎng)址
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
}
url = 'http://www.itdecent.cn/u/472a595d244c'

#第一種寫法的 xpath
xpath_link = '//ul[@class="note-list"]/li/div/a/@href'
xpath_title = '//ul[@class="note-list"]/li/div/a/text()'
xpath_comment_num = '//ul[@class="note-list"]/li/div/div[@class="meta"]/a[2]/text()'
xpath_heart_num = '//ul[@class="note-list"]/li/div/div[@class="meta"]/span/text()'

#獲取和解析網(wǎng)頁
r = requests.get(url, headers=headers)
r.encoding = r.apparent_encoding
dom = etree.HTML(r.text)

#所有的 鏈接 標(biāo)題 評(píng)論數(shù) 點(diǎn)贊數(shù)
links = dom.xpath(xpath_link)
titles = dom.xpath(xpath_title)
comment_nums = dom.xpath(xpath_comment_num)
heart_nums = dom.xpath(xpath_heart_num)

#將每篇文章的鏈接 標(biāo)題 評(píng)論數(shù) 點(diǎn)贊數(shù)放到一個(gè)字典里
data = []
for i in range(len(links)):
    t = {}
    t['link'] = links[i]
    t['title'] = titles[i]
    t['comment_num'] = comment_nums[i].strip()
    t['heart_num'] = heart_nums[i].strip()
    data.append(t)

#打印結(jié)果
for t in data:
    print(t)



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

example-3

可以看到,第一篇和第三篇的 comment_num 沒有獲取到

第二種 xpath寫法對(duì)應(yīng)的代碼:

#-*- coding: utf-8 -*
import requests
from lxml import etree


#請(qǐng)求頭和目標(biāo)網(wǎng)址
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36'
}
url = 'http://www.itdecent.cn/u/472a595d244c'

#第二種寫法的 xpath
#獲取所有 li標(biāo)簽
xpath_items = '//ul[@class="note-list"]/li'
#對(duì)每個(gè) li標(biāo)簽再提取
xpath_link = './div/a/@href'
xpath_title = './div/a/text()'
xpath_comment_num = './/div[@class="meta"]/a[2]/text()'
xpath_heart_num = './/div[@class="meta"]/span/text()'


#獲取和解析網(wǎng)頁
r = requests.get(url, headers=headers)
r.encoding = r.apparent_encoding
dom = etree.HTML(r.text)

#獲取所有的文章標(biāo)簽
items = dom.xpath(xpath_items)

#分別對(duì)每一個(gè)文章標(biāo)簽進(jìn)行操作 將每篇文章的鏈接 標(biāo)題 評(píng)論數(shù) 點(diǎn)贊數(shù)放到一個(gè)字典里
data = []
for article in items:
    t = {}
    t['link'] = article.xpath(xpath_link)[0]
    t['title'] = article.xpath(xpath_title)[0]
    #comment_num對(duì)應(yīng)的標(biāo)簽里有兩個(gè)文本標(biāo)簽 用 join方法將兩個(gè)文本拼接起來
    #strip()方法去除換行和空格
    t['comment_num'] = ''.join(article.xpath(xpath_comment_num)).strip()
    t['heart_num'] = article.xpath(xpath_heart_num)[0].strip()
    data.append(t)

#打印結(jié)果
for t in data:
    print(t)



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

example-4

這里 comment_num成功獲得了

僅僅從獲取的結(jié)果來看,我們就可以判斷第二種 xpath寫法更好。

為什么第二種寫法更好呢?

因?yàn)榈诙N方法把每一篇文章看作一個(gè)對(duì)象,這樣后續(xù)的處理都是以對(duì)象為基本單位,對(duì)數(shù)據(jù)進(jìn)行處理的時(shí)候過程更加清晰。

而第一種寫法把鏈接、標(biāo)題、評(píng)論數(shù)和點(diǎn)贊數(shù)量這四個(gè)分別用列表存儲(chǔ),這樣雖然同樣可以獲得結(jié)果,但是再進(jìn)行數(shù)據(jù)處理的時(shí)候就需要考慮怎么才能不破壞四個(gè)變量之間的一一對(duì)應(yīng)關(guān)系。

用第二種方法就沒有這個(gè)問題,因?yàn)樵谔幚頂?shù)據(jù)的時(shí)候它們都被看作同一個(gè)對(duì)象的組成部分,這本身就蘊(yùn)含著蘊(yùn)含著一種關(guān)系。

現(xiàn)在問題來了,平時(shí)我們?cè)谂廊?shù)據(jù)的時(shí)候,怎么才能判斷哪些數(shù)據(jù)是同一個(gè)對(duì)象呢?

這個(gè)其實(shí)很簡(jiǎn)單,在我們分析需求的時(shí)候就已經(jīng)知道了,我們所需要數(shù)據(jù)的一個(gè)完整組合就是一個(gè)對(duì)象。

比如在本文的例子里,我們要爬取鏈接、標(biāo)題、評(píng)論數(shù)和點(diǎn)贊數(shù)量,那么{鏈接,標(biāo)題,評(píng)論數(shù),點(diǎn)贊數(shù)量}就是一個(gè)對(duì)象。

上一篇:python爬蟲系列之 xpath實(shí)戰(zhàn):批量下載壁紙

下一篇:python爬蟲系列之?dāng)?shù)據(jù)的存儲(chǔ)(一):json庫的使用

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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