您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“l(fā)xml與pyquery解析html的方法”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“l(fā)xml與pyquery解析html的方法”吧!
首先來(lái)了解一下lxml,很多常用的解析html的庫(kù)都用到了lxml這個(gè)庫(kù),例如BeautifulSoup、pyquery。
下面我們介紹一下lxml關(guān)于html解析的3個(gè)Element。
from lxml import etree text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first</a></li> <li class="item-1"><a href="link2.html">second</a></li> <li class="item-0"><a href="link3.html">third</a></li> </ul> </div> ''' # lxml.etree._Element element = etree.HTML(text)
# 通過(guò)css選擇器獲取節(jié)點(diǎn) cssselect(expr) # 通過(guò)標(biāo)簽或者xpath語(yǔ)法獲取第一個(gè)匹配 find(path) # 通過(guò)標(biāo)簽或者xpath語(yǔ)法獲取所有匹配 findall(path) # 獲取屬性值 get(key) # 獲取所有屬性 items() # 獲取所有屬性名稱 keys() # 獲取所有屬性值 values() # 獲取子節(jié)點(diǎn) getchildren() # 獲取父節(jié)點(diǎn) getparent() # 獲取相鄰的下一個(gè)節(jié)點(diǎn) getnext() # 獲取相鄰的上一個(gè)節(jié)點(diǎn) getprevious() # 迭代節(jié)點(diǎn) iter(tag) # 通過(guò)xpath表達(dá)式獲取節(jié)點(diǎn) xpath(path)
from lxml import etree text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first</a></li> <li class="item-1"><a href="link2.html">second</a></li> <li class="item-0" rel="third"><a href="link3.html">third</a></li> </ul> </div> ''' element = etree.HTML(text) # css選擇器,獲取class為item-0的li節(jié)點(diǎn) lis = element.cssselect("li.item-0") for li in lis: # 獲取class屬性 print(li.get("class")) # 獲取屬性名稱和值,元組列表 print(li.items()) # 獲取節(jié)點(diǎn)所有的屬性名稱 print(li.keys()) # 獲取所有屬性值 print(li.values()) print("--------------") ass = element.cssselect("li a") for a in ass: # 獲取文本節(jié)點(diǎn) print(a.text) print("--------------") # 獲取第一個(gè)li節(jié)點(diǎn) li = element.find("li") # 獲取所有l(wèi)i節(jié)點(diǎn) lis = element.find("li") # 獲取所有的a節(jié)點(diǎn) lias = element.iter("a") for lia in lias: print(lia.get("href")) textStr = element.itertext("a") for ts in textStr: print(ts)
xpath我們后面單獨(dú)介紹。
from io import StringIO from lxml import etree text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first</a></li> <li class="item-1"><a href="link2.html">second</a></li> <li class="item-0"><a href="link3.html">third</a></li> </ul> </div> ''' parser = etree.HTMLParser() # lxml.etree._ElementTree elementTree = etree.parse(StringIO(text), parser) # 可以直接從文件讀取 # elementTree = etree.parse(r'F:\tmp\etree.html',parser)
find(path) findall(path) iter(tag) xpath(path)
_ElementTree方法和 _Element的同名方法使用基本一樣。
有很多不同的是_ElementTree的find和findall方法只接受xpath表達(dá)式。
from io import StringIO from lxml import etree text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first</a></li> <li class="item-1"><a href="link2.html">second</a></li> <li class="item-0"><a href="link3.html">third</a></li> </ul> </div> ''' parser = etree.HTMLParser() elementTree = etree.parse(StringIO(text), parser) lis = elementTree.iter("li") for li in lis: print(type(li)) print("---------") firstLi = elementTree.find("//li") print(type(firstLi)) print(firstLi.get("class")) print("---------") ass = elementTree.findall("//li/a") for a in ass: print(a.text)
import lxml.html text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first</a></li> <li class="item-1"><a href="link2.html">second</a></li> <li class="item-0"><a href="link3.html">third</a> </ul> </div> ''' # lxml.html.HtmlElement htmlElement = lxml.html.fromstring(text)
HtmlElement繼承了etree.ElementBase和HtmlMixin,etree.ElementBase繼承了_Element。
因?yàn)镠tmlElement繼承了_Element,所以_Element中介紹的方法,HtmlElement都可以使用。 HtmlElement還可以使用HtmlMixin中的方法。
# 通過(guò)類名獲取節(jié)點(diǎn) find_class(class_name) # 通過(guò)id獲取節(jié)點(diǎn) get_element_by_id(id) # 獲取文本節(jié)點(diǎn) text_content() # 通過(guò)css選擇器獲取節(jié)點(diǎn) cssselect(expr)
xpath功能非常強(qiáng)大,并且_Element、_ElementTree、HtmlElement都可以使用xpath表達(dá)式,所以最后介紹一下xpath。
表達(dá)式 | 描述 |
---|---|
/ | 從根節(jié)點(diǎn)開始,絕對(duì)路徑 |
// | 從當(dāng)前節(jié)點(diǎn)選取子孫節(jié)點(diǎn),相對(duì)路徑,不關(guān)心位置 |
. | 選取當(dāng)前節(jié)點(diǎn) |
.. | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
@ | 選取屬性 |
* | 通配符,選擇所有元素節(jié)點(diǎn)與元素名 |
@* | 選取所有屬性 |
[@attrib] | 選取具有給定屬性的所有元素 |
[@attrib='value'] | 選取給定屬性具有給定值的所有元素 |
[tag] | 選取所有具有指定元素的直接子節(jié)點(diǎn) |
[tag='text'] | 選取所有具有指定元素并且文本內(nèi)容是text節(jié)點(diǎn) |
expression | 表達(dá)式 | 描述 |
---|---|---|
ancestor | xpath('./ancestor:: *') | 選取當(dāng)前節(jié)點(diǎn)的所有先輩節(jié)點(diǎn) |
ancestor-or-self | ('./ancestor-or-self:: *') | 選取當(dāng)前節(jié)點(diǎn)的所有先輩以及節(jié)點(diǎn)本身 |
attribute | xpath('./attribute:: *') | 選取當(dāng)前節(jié)點(diǎn)的所有屬性 |
child | xpath('./child:: *') | 返回當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn) |
descendant | xpath('./descendant:: *') | 返回當(dāng)前節(jié)點(diǎn)的所有后代節(jié)點(diǎn)(子節(jié)點(diǎn)、孫節(jié)點(diǎn)) |
following | xpath('./following:: *') | 選取文檔中當(dāng)前節(jié)點(diǎn)結(jié)束標(biāo)簽后的所有節(jié)點(diǎn) |
following-sibing | xpath('./following-sibing:: *') | 選取當(dāng)前節(jié)點(diǎn)之后的兄弟節(jié)點(diǎn) |
parent | xpath('./parent:: *') | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
preceding | xpath('./preceding:: *') | 選取文檔中當(dāng)前節(jié)點(diǎn)開始標(biāo)簽前的所有節(jié)點(diǎn) |
preceding-sibling | xpath('./preceding-sibling:: *') | 選取當(dāng)前節(jié)點(diǎn)之前的兄弟節(jié)點(diǎn) |
self | xpath('./self:: *') | 選取當(dāng)前節(jié)點(diǎn) |
很多時(shí)候我們可以通過(guò)瀏覽器獲取xpath表達(dá)式:
from lxml.html.clean import Cleaner from lxml import etree text = ''' <div> <ul> <li class="item-0" rel="li1"><a href="link1.html">first</a></li> <li class="item-1" rel="li2"><a href="link2.html" rel="attrRel">second</a></li> <li class="item-0" rel="li3"><a href="link3.html" id="thirda">third</a> </ul> </div> ''' # 去除css、script cleaner = Cleaner(style=True, scripts=True, page_structure=False, safe_attrs_only=False) print(cleaner.clean_html(text)) # _Element element = etree.HTML(text) # 文本節(jié)點(diǎn),特殊字符轉(zhuǎn)義 print(element.xpath('//text()')) # 文本節(jié)點(diǎn),不轉(zhuǎn)義 print(element.xpath('string()')) # find、findall只能使用相對(duì)路徑,以.//開頭 print(element.findall('.//a[@rel]')) print(element.find('.//a[@rel]')) # 獲取包含rel屬性的a節(jié)點(diǎn) print(element.xpath('//a[@rel]')) # 獲取ul元素下的第一個(gè)li節(jié)點(diǎn),注意是列表,因?yàn)閡l可能有多個(gè) print(element.xpath("//ul/li[1]")) # 獲取ul元素下rel屬性為li2的li節(jié)點(diǎn) print(element.xpath("//ul/li[@rel='li2']")) # 獲取ul元素下的倒數(shù)第2個(gè)節(jié)點(diǎn) print(element.xpath("//ul/li[last()-1]")) # 獲取ul元素下的前2個(gè)li節(jié)點(diǎn) print(element.xpath("//ul/li[position()<3]")) # 獲取li元素下的所有a節(jié)點(diǎn) for a in element.xpath("//li/a"): print(a.text) print(a.get("href")) # 獲取父節(jié)點(diǎn),列表,因?yàn)榭赡芷ヅ涠鄠€(gè)a print(element.xpath('//a[@href="link2.html"]/parent::*')) # 獲取的是文本節(jié)點(diǎn)對(duì)象列表 print(element.xpath('//li[@class="item-1"]/a/text()')) print("---------------") # 獲取a的href屬性 print(element.xpath('//li/a/@href')) # 獲取所有l(wèi)i子孫節(jié)點(diǎn)的href屬性 print(element.xpath('//li//@href'))
from lxml import etree text = ''' <li class="subject-item"> <div class="pic"> <a class="nbg" href="https://book.douban.com/subject/25862578/"> <img class="" src="https://img3.doubanio.com/view/subject/m/public/s27264181.jpg" width="90"> </a> </div> <div class="info"> <h3 class=""><a href="https://book.douban.com/subject/25862578/" title="解憂雜貨店">解憂雜貨店</a></h3> <div class="pub">[日] 東野圭吾 / 李盈春 / 南海出版公司 / 2014-5 / 39.50元</div> <div class="star clearfix"> <span class="allstar45"></span> <span class="rating_nums">8.5</span> <span class="pl"> (537322人評(píng)價(jià)) </span> </div> <p>現(xiàn)代人內(nèi)心流失的東西,這家雜貨店能幫你找回——僻靜的街道旁有一家雜貨店,只要寫下煩惱投進(jìn)卷簾門的投信口,第二天就會(huì)在店后的牛奶箱里得到回答。因男友身患絕... </p> </div> </li> ''' element = etree.HTML(text) # 查找符合xpath(//li/div/a)的節(jié)點(diǎn) aeles = element.xpath("//li/div/a") for aele in aeles: # 獲取href屬性 print(aele.get("href")) # 查找img標(biāo)簽,并且獲取src屬性 print(aele.find("img").get("src")) # 返回列表的原因是:雖然我們只取了第一個(gè)a節(jié)點(diǎn),但是上級(jí)xpath(//li/div[@class='info']/h3)可能匹配多個(gè) for a in element.xpath("//li/div[@class='info']/h3/a[1]"): print(a.get("href")) print(a.text) # print(a.get("title")) # 指定div的class屬性 for pu in element.xpath("//li/div[@class='info']/div[@class='pub']"): print(pu.text) # 使用到contains函數(shù)和or運(yùn)算符 spans = element.xpath("//li/div[@class='info']/div[@class='star clearfix']/span[contains(@class,'rating_nums') or " "contains(@class,'pl')]") for span in spans: print(span.text.strip()) for content in element.xpath("//li/div[@class='info']/p"): print(content.text) # 如果確定只有一個(gè)或者只需要第一個(gè)可以使用find,注意find使用xpath為參數(shù)的時(shí)候使用相對(duì)路徑(.//開頭) print(element.find(".//li/div[@class='info']/p").text)
從字符串:
from pyquery import PyQuery as pq html = '' with open(r"F:\tmp\db.html", "r", encoding='utf-8') as f: html = f.read() doc = pq(html)
從URL:
from pyquery import PyQuery as pq headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0' } doc = pq('https://book.douban.com/tag/小說(shuō)', headers=headers)
從文件:
from pyquery import PyQuery as pq doc = pq(filename=r"F:\tmp\db.html")
從文件有一個(gè)問(wèn)題就是不能指定文件編碼,所以一般都是自己讀取文件,然后從字符串構(gòu)造。
pyquery最強(qiáng)大的地方就在于,它可以像jQuery使用css選擇器一樣獲取節(jié)點(diǎn)。
常用的一些選擇器:
id選擇器(#id) 類選擇器(.class) 屬性選擇器(a[href="xxx"]) 偽類選擇器(:first :last :even :odd :eq :lt :gt :checked :selected)
前面我們已經(jīng)知道怎樣構(gòu)造一個(gè)PyQuery,上面我們有知道了怎么通過(guò)選擇器獲取節(jié)點(diǎn),下面我們通過(guò)一個(gè)小示例來(lái)具體了解一下。
from pyquery import PyQuery as pq html = ''' <!DOCTYPE html> <html> <head> <title>pyquery</title> </head> <body> <ul id="container"> <li class="li1">li1</li> <li class="li2">li2</li> <li class="li3" data-type='3'>li3</li> </ul> </body> </html> ''' doc = pq(html) # id選擇器,outerHtml輸出整體的html print(doc("#container").outerHtml()) print("----------") # 類選擇器 print(doc(".li1").outerHtml()) print("----------") # 偽類選擇器 # 選擇第2個(gè)li節(jié)點(diǎn),并通過(guò)text獲取該li節(jié)點(diǎn)的值 print(doc('li:nth-child(2)').text()) # 獲取第1個(gè)li節(jié)點(diǎn) print(doc('li:first-child').text()) # 獲取最后一個(gè)li節(jié)點(diǎn),并通過(guò)attr獲取該節(jié)點(diǎn)的data-type屬性值 print(doc('li:last-child').attr("data-type")) print(doc("li:contains('li3')").attr("data-type")) print("----------") # 屬性選擇器 # 選擇li的data-type的屬性值為3的節(jié)點(diǎn) print(doc("li[data-type='3']").outerHtml())
首先我們通過(guò)html字符串構(gòu)造了一個(gè)PyQuery對(duì)象,然后就可以通過(guò)選擇器愉快的獲取我們想要的節(jié)點(diǎn)了。
注意:上面的text方法是獲取節(jié)點(diǎn)的文本,attr是獲取節(jié)點(diǎn)的屬性,非常常用
這里就補(bǔ)貼輸出代碼了,如果感興趣可以自己動(dòng)手嘗試一下,看一下輸出。
很多時(shí)候,我們并不能直接通過(guò)選擇器一步到位的獲取到我們需要的節(jié)點(diǎn),所以我們需要另外一些查找、過(guò)濾、遍歷節(jié)點(diǎn)的方法,例如:find、filter、eq、not_、items、each等,下面我們還是通過(guò)一個(gè)小例子來(lái)介紹一下這些方法。
from pyquery import PyQuery as pq html = ''' <!DOCTYPE html> <html> <head> <title>pyquery</title> </head> <body> <ul id="container"> <li class="li1">li1</li> <li class="li2">li2</li> <li class="li3" data-type='3'>li3</li> </ul> </body> </html> ''' doc = pq(html) # find的語(yǔ)法和直接使用選擇器一樣 print("---find:") print(doc.find("li").show()) # 輸出li的個(gè)數(shù) print(doc.find("li").size()) # filter過(guò)濾得到滿足條件的 print("---filter:") print(doc.find("li").filter(".li3").show()) # eq選擇第n個(gè),下標(biāo)從0開始 print("---eq:") print(doc.find("li").eq(1).show()) # not_排除滿足條件的節(jié)點(diǎn) print("---not_:") print(doc.find("li").not_("li[data-type='3']").show()) lis = doc.find("li") # 輸出PyQuery print(type(lis)) # each輸出的類型是lxml.etree._Element print("---each:") lis.each(lambda i, e: print(type(e))) print("---for:") for li in lis: print(type(li)) # items獲取到的類型才是PyQuery print("---items:") for li in lis.items(): print(type(li))
這些方法還是比較基礎(chǔ)的,看代碼中的注釋就能知道是什么意思了,如果有疑問(wèn),可以自己動(dòng)手調(diào)試一下。
注意lis是PyQuery類型,PyQuery的each是lxml.etree._Element類型,items才是PyQuery
這意味著使用for\each循環(huán)不能使用PyQuery的find、filter、text、attr這些方法。
需要使用lxml.etree._Element的方法。
到此,相信大家對(duì)“l(fā)xml與pyquery解析html的方法”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。