1. 程式人生 > >簡單比較 BeautifulSoup 和 Xpath 的效能

簡單比較 BeautifulSoup 和 Xpath 的效能

一些說明

  • 我為什麼要寫這篇文章?

      其實這篇文章並不是為了比較出結論,因為結論是顯而易見的,Xpath 必然 是要比 BeautifulSoup 在時間和空間上都要效能更好一些。其中理由有很多,其中一個很明顯的是 BeautifulSoup 在構建一個物件的時候需要傳入一個引數以指定解析器,而在它支援的眾多的解析器中,lxml 是效能最佳的,那麼 BeautifulSoup 物件的各種方法可以理解為是對 lxml 的封裝,換句話說,BeautifulSoup 本質上並沒有創造出自己的解析方式,而是建立在各種解析器的基礎上。考慮到其他一些內部耗時因素,BeautifulSoup 註定會比 lxml 甚至是任何一個構建物件時使用的解析器要慢,要更耗費空間。只有付出這樣子的代價才能夠換來它的簡潔、優美與使用者友好性。
      那麼,本文其實是通過一個爬蟲例子來簡單的驗證一下這個結論,以及對它們之間的差距有一個數量上的認識。

測試例子

# test.py
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup, SoupStrainer
import traceback
import json
from lxml import etree
import re
import time

def getHtmlText(url):
    try:
        r = requests.get(url, headers=headers)
        r.raise_for_status()
        if r.encoding == 'ISO-8859-1'
: r.encoding = r.apparent_encoding return r.text except: traceback.print_exc() def parseWithBeautifulSoup(html_text): soup = BeautifulSoup(html_text, 'html.parser') # 後改為 'lxml' content = [] for mulu in soup.find_all(class_='mulu'): h2 = mulu.find('h2'
) if h2 != None: h2_title = h2.string # 獲取標題 lst = [] for a in mulu.select('div.box a'): href = a.get('href') box_title = a.get('title') pattern = re.compile(r'\s*\[(.*)\]\s+(.*)') # (re) 匹配括號內的表示式,也表示一個組 match = pattern.search(box_title) if match != None: date = match.group(1) real_title = match.group(2) lst.append({'href':href,'title':real_title,'date':date}) content.append({'title':h2_title,'content':lst}) with open('dmbj_bs.json', 'w') as fp: json.dump(content, fp=fp, indent=4) def parseWithXpath(html_text): html = etree.HTML(html_text) div_mulus = html.xpath('.//*[@class="mulu"]') # 先找到所有的 div class=mulu 標記 content = [] for div_mulu in div_mulus: # 找到所有的 div_h2 標記 div_h2 = div_mulu.xpath('./div[@class="mulu-title"]/center/h2/text()') if len(div_h2) > 0: h2_title = div_h2[0] a_s = div_mulu.xpath('./div[@class="box"]/ul/li/a') lst = [] for a in a_s: # 找到 href 屬性 href = a.xpath('./@href')[0] # 找到 title 屬性 box_title = a.xpath('./@title')[0] pattern = re.compile(r'\s*\[(.*)\]\s+(.*)') # (re) 匹配括號內的表示式,也表示一個組 match = pattern.search(box_title) if match != None: date = match.group(1) real_title = match.group(2) lst.append({'href':href,'title':real_title,'date':date}) content.append({'title':h2_title,'content':lst}) with open('dmbj_xp.json', 'w') as fp: json.dump(content, fp=fp, indent=4) def main(): html_text = getHtmlText('http://www.seputu.com') print(len(html_text)) start = time.clock() parseWithBeautifulSoup(html_text) print('BSoup cost:', time.clock()-start) start = time.clock() parseWithXpath(html_text) print('Xpath cost:', time.clock()-start) if __name__ == '__main__': user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36' headers={'User-Agent': user_agent} main()
  • 執行截圖

html.parser
html.parser

lxml
lxml

  • 結果分析

      可以看到,當我們使用 html.parser 作為解析器時,BeautifulSoup 解析的耗時平均是 Xpath 的 1.8 倍+;當我們使用 lxml 作為解析器時,BeautifulSoup 解析的耗時雖有減少,但平均仍是 Xpath 的 1.5 倍+。

最後

  BeautifulSoup 這碗美味湯確實是美味可口,但是一碗好湯煲制時間和用料上面都更加花費,這無可厚非。Xpath 相對來說可能語義性沒有前者強,但總體也是 user-friendly,也很好用,功能十分強大,最重要的是它的爸爸 lxml 使用 C 編寫的,速度自然就不必說了,如果在很追求效率和資源節約的情況下,熟練運用 Xpath 會使你感到無盡的愉悅。