日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

关于爬虫中常见的两个网页解析工具的分析 —— lxml / xpath 与 bs4 / BeautifulSoup...

發(fā)布時(shí)間:2023/12/19 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于爬虫中常见的两个网页解析工具的分析 —— lxml / xpath 与 bs4 / BeautifulSoup... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

http://www.cnblogs.com/binye-typing/p/6656595.html

  讀者可能會(huì)奇怪我標(biāo)題怎么理成這個(gè)鬼樣子,主要是單單寫(xiě) lxml 與 bs4 這兩個(gè) py 模塊名可能并不能一下引起大眾的注意,一般講到網(wǎng)頁(yè)解析技術(shù),提到的關(guān)鍵詞更多的是 BeautifulSoup 和 xpath ,而它們各自所在的模塊(python 中是叫做模塊,但其他平臺(tái)下更多地是稱(chēng)作庫(kù)),很少被拿到明面上來(lái)談?wù)摗O旅嫖覍男省?fù)雜度等多個(gè)角度來(lái)對(duì)比 xpath 與 beautifulsoup 的區(qū)別。

效率

從效率上來(lái)講,xpath 確實(shí)比?BeautifulSoup?高效得多,每次分步調(diào)試時(shí),soup?對(duì)象的生成有很明顯的延遲,而?lxml.etree.HTML(html) 方式則在?step?over?的一瞬間便構(gòu)建成功了一個(gè)可執(zhí)行?xpath?操作的對(duì)象,速度驚人。原理上來(lái)講,bs4 是用 python 寫(xiě)的,lxml 是 c 語(yǔ)言實(shí)現(xiàn)的,而且 BeautifulSoup 是基于 DOM 的,會(huì)載入整個(gè)文檔,解析整個(gè)DOM樹(shù),因此時(shí)間和內(nèi)存開(kāi)銷(xiāo)都會(huì)大很多。而lxml只會(huì)進(jìn)行局部遍歷。

使用復(fù)雜度

從使用復(fù)雜度來(lái)講,beautifulsoup?的?find?方法要比?xpath?簡(jiǎn)單,后者不僅要求通曉?xpath?語(yǔ)法,而且?xpath?方法的返回對(duì)象始終是一個(gè)?list,這使得對(duì)于頁(yè)面中一些唯一元素的處理有些尷尬,比如根據(jù)?id?獲取頁(yè)面某一標(biāo)簽,下面我用兩種方式實(shí)現(xiàn)一個(gè)獲取網(wǎng)頁(yè)導(dǎo)航欄的方法 (注釋部分為 bs4 的實(shí)現(xiàn)): 1 def get_nav(self,response): 2 # soup = BeautifulSoup(response.body_as_unicode(), 'lxml') 3 # nav_list = soup.find('ul', id='nav').find_all('li') 4 model = etree.HTML(response.body_as_unicode()) 5 nav_list = model.xpath('//ul[@id="nav"]/li') 6 for nav in nav_list[1:]: 7 # href = nav.find('a').get('href') 8 href = nav.xpath('./a/@href')[0] 9 yield Request(href, callback=self.get_url)

?

  可以看到 xpath 除了其特殊的語(yǔ)法看上去有些別扭(跟正則表達(dá)式似的)以外,它在代碼簡(jiǎn)潔度上還是可觀的,只是所有 xpath 方法的返回結(jié)果都是一個(gè) list ,如果匹配目標(biāo)是單個(gè)元素,對(duì)于無(wú)腦下標(biāo)取0的操作,強(qiáng)迫癥患者可能有些難受。相比之下,BeautifulSoup 這一長(zhǎng)串的 find 與 find_all 方法顯得有些呆板,如果碰到搜索路線比較曲折的,比如:

# href = article.find('div', class_='txt').find('p', class_='tit blue').find('span').find('em').find('a').get('href') href = article.xpath('./div[@class="txt"]//p[@class="tit blue"]/span/em/a/@href')[0]

?

  這種情況下,BeautifulSoup 的寫(xiě)法就顯得有些讓人反胃了,當(dāng)然一般情況下不會(huì)出現(xiàn)這么長(zhǎng)的路徑定位。

?

功能缺陷總結(jié)——BeautifulSoup

   BeautifulSoup 在使用上的一個(gè)短板,就是在嵌套列表中去匹配元素的時(shí)候會(huì)顯得很無(wú)力,下面是一個(gè)例子(具體網(wǎng)頁(yè)結(jié)構(gòu)可根據(jù) index_page 在瀏覽器打開(kāi)進(jìn)行審查):

1 class RankSpider(spider): 2 name = 'PCauto_rank' 3 index_page = 'http://price.pcauto.com.cn/top/hot/s1-t1.html' 4 api_url = 'http://price.pcauto.com.cn%s' 5 6 def start_requests(self): 7 yield Request(self.index_page, callback=self.get_left_nav) 8 9 # 測(cè)試 BeautifulSoup 是否能連續(xù)使用兩個(gè) find_all 方法 10 def get_left_nav(self,response): 11 # model = etree.HTML(response.body_as_unicode()) 12 # nav_list = model.xpath('//div[@id="leftNav"]/ul[@class="pb200"]/li//a[@class="dd "]') 13 soup = BeautifulSoup(response.body_as_unicode(), 'lxml') 14 nav_list = soup.find('div', id='leftNav').find('ul', class_='pb200').find_all('li').find_all('a', class_='dd') 15 for sub_nav in nav_list: 16 href = self.api_url % sub_nav.xpath('./@href')[0] 17 yield Request(href, callback=self.get_url) 18 19 def get_url(self): 20 pass

?

?

   使用注釋部分的 xpath 寫(xiě)法沒(méi)什么問(wèn)題,可實(shí)現(xiàn)準(zhǔn)確定位,但用到 BeautifulSoup 去實(shí)現(xiàn)相應(yīng)邏輯的時(shí)候,就要連續(xù)使用兩個(gè) find_all 方法 ,顯然這種寫(xiě)法不符合規(guī)范,運(yùn)行的時(shí)候會(huì)報(bào)?AttributeError: 'ResultSet' object has no attribute 'find_all' 錯(cuò)誤,這時(shí)候我們要實(shí)現(xiàn)這種匹配,只能先去遍歷各個(gè) li ,然后調(diào) find_all 方法找到 li 下的各個(gè) a 標(biāo)簽,實(shí)在繁瑣,所以這種場(chǎng)景用 xpath 來(lái)解決會(huì)省下不少麻煩。

  當(dāng)然這里我只是單單為了詮釋這么個(gè)問(wèn)題才在故意在拿目標(biāo) url 時(shí)分這么多級(jí)的,實(shí)際開(kāi)發(fā)中我這里用的是:

1 # nav_list = model.xpath('//div[@id="leftNav"]///a[@class="dd "]') 2 nav_list = soup.find('div', id='leftNav').find_all('a', class_='dd')

?

  但如果說(shuō)我們的目標(biāo)不是所有的 li 下面的 a 標(biāo)簽,而是部分 class="*" 的 li 下面的 a 標(biāo)簽,這時(shí)候我們就只能選擇使用 xpath 來(lái)達(dá)到目的,當(dāng)然如果你喜歡寫(xiě)遍歷,覺(jué)得這樣寫(xiě)出來(lái)邏輯展示更清晰,那你可以跳過(guò)這一節(jié)。

?

功能缺陷總結(jié)——xpath

xpath?的類(lèi)選擇器在做公共類(lèi)名選擇時(shí)有短板,也勉強(qiáng)把它算作功能缺陷吧,比如:      1 model = etree.HTML(response.body_as_unicode()) 2 model.xpath('//div[@class="box box-2 box-4"]') 無(wú)法定位?html?中 class?為?box?box-2?box-4?mt25?與?box?box-2?box-4?mt17?的兩個(gè)?div,必須分別以:? model.xpath('//div[@class="box box-2 box-4 mt25"]') model.xpath('//div[@class="box box-2 box-4 mt17"]')

?

  來(lái)匹配目標(biāo),這可能要?dú)w結(jié)于 xpath 在設(shè)計(jì)的時(shí)候本身就是以類(lèi)名的完全匹配來(lái)確定目標(biāo)的,哪怕多一個(gè)空格:

頁(yè)面中一個(gè)?a?標(biāo)簽是這樣寫(xiě)的:??<a?href="/top/hot/s1-t1.html"?class="dd?">5萬(wàn)以下</a>?用?xpath?去選擇,寫(xiě)作:

      model.xpath('//a[@class="dd"]')

死活匹配不到(當(dāng)時(shí)真的是蠻懵逼的),必須要在后面加空格,但在通過(guò)?js?控制臺(tái)?a.dd?這個(gè)類(lèi)選擇器又可以定位到目標(biāo),而且 BeautifulSoup 調(diào) find_all('a', class_='dd') 也是沒(méi)有問(wèn)題的,這種應(yīng)用場(chǎng)景下的 xpath 就略顯死板。

?

文本獲取

  xpath 目標(biāo)結(jié)點(diǎn)的 text 屬性值對(duì)應(yīng)的只是當(dāng)前匹配元素下面的文本信息,如要獲取該結(jié)點(diǎn)下面包括子結(jié)點(diǎn)在內(nèi)的所有文本內(nèi)容要使用 .xpath('string()') 的方式:

1   model = etree.HTML(response.body_as_unicode()) 2 place = model.xpath('//div[@class="guide"]') 3 # nav and aiticle 4 if place: 5 mark = place[0].xpath('./span[@class="mark"]') 6 if mark: 7 # text = mark[0].text.strip().replace('\n','').replace('\r','') # false 8 text = mark[0].xpath('string()') 9 result['address'] = text

?

?

其他方面比較

從參考資料上來(lái)講,bs4?有詳細(xì)中/英文版官方幫助文檔,lxml?好像?document?相對(duì)少。另外?BeautifulSoup 的結(jié)點(diǎn)對(duì)象在?Debugger?下面對(duì)于變量?jī)?nèi)容的監(jiān)視更友好,它直接顯示匹配的?html?字符串,而?lxml?就是一個(gè)類(lèi)似這種表示的對(duì)象:?<Element?html?at?0x####>,不是很友好,但這都不重要,筆者還是更喜歡 xpath 的高效,簡(jiǎn)潔,一步到位。    ?

轉(zhuǎn)載于:https://www.cnblogs.com/mapu/p/8337407.html

總結(jié)

以上是生活随笔為你收集整理的关于爬虫中常见的两个网页解析工具的分析 —— lxml / xpath 与 bs4 / BeautifulSoup...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。