lxml:底層C語言實現、高效地處理html

介紹

lxml也是一個用於篩選指定html內容的模塊,pyquery就是基於lxml。
使用lxml主要須要瞭解xpath

xpath語法

  • /:在子節點裏面找
  • //:在子子孫孫節點裏面找
  • //div:查找當前網頁的全部div標籤
  • //div/p:先找到全部的div標籤,再從div的字標籤中找p標籤
  • //div//p:先找到全部的div標籤,再從div的子孫標籤中找p標籤
  • //div/a:先查找全部div標籤,再從div的子標籤中找a標籤
  • //div/a[@id]:先查找全部div標籤,再從div的子標籤中找有id屬性的a標籤
  • //div//a[@id='fuck']:先查找全部div標籤,再從div的子孫標籤中找有id='fuck'的a標籤
  • //div/a[1]:先查找全部的div標籤,再找div的子標籤中的第一個a標籤,這裏的索引是從1開始的,不是0
  • //div/a[last()]:和上面同樣,不過這裏是最後一個a標籤
  • //div/a[position()<3]:前兩個a標籤
  • //div/a[@price]:擁有price屬性的a標籤
  • //div/a[@price=10]:擁有price屬性,而且值等於10的a標籤。固然裏面還支持>,<,>=,<=等等
  • **//div/*:星號表示通配符,選取全部div的子標籤**
  • //div/a[@*]:選取全部div的子標籤中帶有屬性的a標籤,什麼屬性均可以,id、class、href等等均可以
  • //div/a | //div/p:選取全部的div的子標籤中的全部a元素和p元素
  • //div/a[contains(@class, "BDE")]:找出全部div的子標籤中的class屬性包含"BDE"的a標籤
  • //div/a[starts-with(@class, "BDE")]:找出全部div的子標籤中的class屬性以"BDE"開頭的a標籤
  • //div/a[contains(@href, "mashiro")]:找出全部div的子標籤中的class屬性包含"mashiro"的a標籤
  • //a/@href:獲取href屬性
  • //a/@class:獲取class屬性
  • //a/text():獲取文本
  • //a[contains(text(), "清純可愛")]:找出文本包含"清純可愛"的a標籤
咱們可能注意到:@href、@class、text()前面只有一個/,若是是兩個/的話,好比div標籤,它裏面是沒有href屬性的。可是div裏面有a標籤,a標籤裏面有href屬性,因此咱們仍然能夠經過//div//@href去獲取,此時獲取的是裏面的a標籤裏面的href,可是//div/@href是獲取不到的,由於//div/@href表示的是獲取div標籤裏面的href,而div沒有href屬性。

所以若是是/@href,那麼前面必須是有href屬性的標籤,不然獲取不到。但若是是//@href的話,前面的標籤就沒有太多要求了,能夠是p標籤,也能夠是div標籤,只要裏面有具備href屬性的標籤便可

使用lxml

from lxml import etree
import requests

res = requests.get("http://www.baidu.com",
                   headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"})
res.encoding = res.apparent_encoding

# 調用etree內部的HTML方法,將html文本傳進去,便獲得一個能夠進行xpath的對象
# 咱們能夠調用etree.tostring(html),會獲得一個字節對象,再解碼會獲得字符串,這裏就不演示了。
html = etree.HTML(res.text)

# 找出class屬性等於"toindex"的a標籤
result = html.xpath("//a[@class='toindex']")
for res in result:
    # 打印的結果是一個標籤
    print(res)  # <Element a at 0x1b2cb5cecc8>
    # 能夠調用tostring轉成字節
    print(etree.tostring(res))  # b'<a class="toindex" href="/">&#30334;&#24230;&#39318;&#39029;</a>'
    print(str(etree.tostring(res), encoding="utf-8"))  # <a class="toindex" href="/">&#30334;&#24230;&#39318;&#39029;</a>

    # 內部還有一個etree.parse()方法,能夠直接傳入html文件或者xml文件的路徑,進行解析


# 獲取一下內部屬性
result = html.xpath("//a[@class='toindex']/@href")
# 因爲標籤只有一個,因此列表裏面只有一個元素
print(result)  # ['/']
result = html.xpath("//a[@class='toindex']/@class")
print(result)  # ['toindex']
result = html.xpath("//a[@class='toindex']/text()")
print(result)  # ['百度首頁']
# 能夠看到此時的result又都不是標籤了,這是爲何?
# 若是咱們不選擇href、class等具體屬性的話,那麼獲得的是一個標籤,若是選擇屬性那麼獲得是字符串
# 由於不止一個標籤,因此會將全部的字符串組合成一個列表
# 即使只有一個元素,獲得依舊是一個列表

result = html.xpath("//div[contains(@class, 'tab_inner')]")
# 如今獲取的result裏面只有一個元素
for res in result:
    print(res)  # <Element div at 0x2644f88>
    print(etree.tostring(res))  # b'<div class="s_tab_inner">\n    <b>網頁</b>\n    <a href=。。。。。。
    # 咱們看到了,若是不是獲取href、class、text等屬性的時候,獲得的依舊是一個Element對象,這就意味着咱們能夠繼續使用xpath
    titles = res.xpath(".//a/text()")  # 注意這裏是.//不是//,由於咱們要在當前元素的子孫中去查找
    print(titles)  # ['資訊', '貼吧', '知道', '音樂', '圖片', '視頻', '地圖', '文庫', '更多»']
    # 咱們試試不加.
    titles = res.xpath("//a/text()")
    # 能夠看到內容就多了,由於即使是res.xpath,但指定//的話依舊會在全局html頁面中查找
    print(titles)
    """
    # ['手寫', '拼音', '關閉', '百度首頁', '設置', '登陸', '新聞', 'hao123', 
    '地圖', '視頻', '貼吧', '學術', '登陸', '設置', '更多產品', '資訊', '貼吧', 
    '知道', '音樂', '圖片', '視頻', '地圖', '文庫', '更多»', '把百度設爲主頁', 
    '關於百度', 'About\xa0\xa0Baidu', '百度推廣', '使用百度前必讀', '意見反饋', 
    '京公網安備11000002000001號']
    """
    # 進一步證明了二者結果是同樣的
    print(res.xpath("//a/text()") == html.xpath("//a/text()"))  # True
相關文章
相關標籤/搜索