pythonxpathhtml
『壹』 python里的爬蟲如何使用xpath 提取script里的元素
xpath也許只能提取html元素?
建議你先把content保存到本地文件,看看需要的內容有沒有下載下來。
你這個屬於script內容,看看直接正則能獲得嗎?
『貳』 python使用xpath(超詳細)
使用時先安裝 lxml 包
開始使用 #
和beautifulsoup類似,首先我們需要得到一個文檔樹
把文本轉換成一個文檔樹對象
from lxml import etreeif __name__ == '__main__':doc='''
把文件轉換成一個文檔樹對象
fromlxmlimportetree# 讀取外部文件 index.htmlhtml = etree.parse('./index.html')result = etree.tostring(html, pretty_print=True)#pretty_print=True 會格式化輸出print(result)
均會列印出文檔內容
節點、元素、屬性、內容 #
xpath 的思想是通過 路徑表達 去尋找節點。節點包括元素,屬性,和內容
元素舉例
html --->...div --->
這里我們可以看到,這里的元素和html中的標簽一個意思。單獨的元素是無法表達一個路徑的,所以單獨的元素不能獨立使用
路徑表達式 #
/ 根節點,節點分隔符,// 任意位置. 當前節點.. 父級節點@ 屬性
通配符 #
* 任意元素@* 任意屬性node() 任意子節點(元素,屬性,內容)
謂語 #
使用中括弧來限定元素,稱為謂語
//a[n] n為大於零的整數,代表子元素排在第n個位置的 元素//a[last()] last() 代表子元素排在最後個位置的 元素//a[last()-] 和上面同理,代表倒數第二個//a[position()<3] 位置序號小於3,也就是前兩個,這里我們可以看出xpath中的序列是從1開始//a[@href] 擁有href的 元素//a[@href='www..com'] href屬性值為'www..com'的 元素//book[@price>2] price值大於2的元素
多個路徑 #
用| 連接兩個表達式,可以進行 或匹配
//book/title | //book/price
函數 #
xpath內置很多函數。更多函數查看 https://www.w3school.com.cn/xpath/xpath_functions.asp
contains(string1,string2)
starts-with(string1,string2)
ends-with(string1,string2) #不支持
upper-case(string) #不支持
text()
last()
position()
node()
可以看到last()也是個函數,在前面我們在謂語中已經提到過了
案例 #
定位元素 #
匹配多個元素,返回列表
fromlxmlimportetreeif__name__ =='__main__':doc='''
【結果為】
[<Element li at 0x2b41b749848>, <Element li at 0x2b41b749808>, <Element li at 0x2b41b749908>, <Element li at 0x2b41b749948>, <Element li at 0x2b41b749988>][] #沒找到p元素
html = etree.HTML(doc)print(etree.tostring(html.xpath("//li[@class='item-inactive']")[0]))print(html.xpath("//li[@class='item-inactive']")[0].text)print(html.xpath("//li[@class='item-inactive']/a")[0].text)print(html.xpath("//li[@class='item-inactive']/a/text()"))print(html.xpath("//li[@class='item-inactive']/.."))print(html.xpath("//li[@class='item-inactive']/../li[@class='item-0']"))
【結果為】
b' third item \n 'None #因為第三個li下面沒有直接text,Nonethird item #['third item'][<Element ul at 0x19cd8c4c848>][<Element li at 0x15ea3c5b848>, <Element li at 0x15ea3c5b6c8>]
使用函數 #
contains #
有的時候,class作為選擇條件的時候不合適@class='....' 這個是完全匹配,當王爺樣式發生變化時,class或許會增加或減少像active的class。用contains就能很方便
from lxml import etreeif __name__ == '__main__':doc='''
【結果為】
[<Element p at 0x23f4a9d12c8>, <Element li at 0x23f4a9d13c8>, <Element li at 0x23f4a9d1408>, <Element li at 0x23f4a9d1448>, <Element li at 0x23f4a9d1488>]
starts-with #
from lxml import etreeif __name__ == '__main__':doc='''
【結果為】
[<Element ul at 0x23384e51148>, <Element p at 0x23384e51248>, <Element li at 0x23384e51288>, <Element li at 0x23384e512c8>, <Element li at 0x23384e51308>, <Element li at 0x23384e51388>][<Element ul at 0x23384e51148>]
ends-with #
print(html.xpath("//*[ends-with(@class,'ul')]"))
【結果為】
Traceback (most recent call last):File"F:/OneDrive/pprojects/shoes-show-spider/test/xp5_test.py",line18,inprint(html.xpath("//*[ends-with(@class,'ul')]"))File"src\lxml\etree.pyx",line1582,inlxml.etree._Element.xpathFile"src\lxml\xpath.pxi",line305,inlxml.etree.XPathElementEvaluator.__call__File"src\lxml\xpath.pxi",line225,inlxml.etree._XPathEvaluatorBase._handle_resultlxml.etree.XPathEvalError: Unregisteredfunction
看來python的lxml並不支持有的xpath函數列表
upper-case #
和ends-with函數一樣,也不支持。同樣報錯lxml.etree.XPathEvalError: Unregistered function
print(html.xpath("//a[contains(upper-case(@class),'ITEM-INACTIVE')]"))
text、last #
#最後一個li被限定了print(html.xpath("//li[last()]/a/text()"))#會得到所有的`<a>`元素的內容,因為每個<a>標簽都是各自父元素的最後一個元素。#本來每個li就只有一個<a>子元素,所以都是最後一個print(html.xpath("//li/a[last()]/text()"))print(html.xpath("//li/a[contains(text(),'third')]"))
【結果為】
['fifth item']['second item', 'third item', 'fourth item', 'fifth item'][<Element a at 0x26ab7bd1308>]
position #
print(html.xpath("//li[position()=2]/a/text()"))#結果為['third item']
上面這個例子我們之前以及講解過了
* 這里有個疑問,就是position()函數能不能像text()那樣用呢
print(html.xpath("//li[last()]/a/position()"))#結果 lxml.etree.XPathEvalError: Unregisteredfunction
這里我們得到一個結論,函數不是隨意放在哪裡都能得到自己想要的結果
node #
返回所有子節點,不管這個子節點是什麼類型(熟悉,元素,內容)
print(html.xpath("//ul/li[@class='item-inactive']/node()"))print(html.xpath("//ul/node()"))
【結果為】
[]['\n ', , '\n ', , '\n ', , '\n ', , '\n ', , ' 閉合標簽\n ']
獲取內容 #
**剛剛已經提到過,可以使用.text和text()的方式來獲取元素的內容
from lxml import etreeif __name__ == '__main__':doc='''
【結果為】
['first item','second item','third item','fourth item','fifth item']first item18['\n ','\n ','\n ','\n ','\n ',' 閉合標簽\n ']
看到這里,我們觀察到text()和.text的區別。自己總結吧。不太好表達,就不表達了
獲取屬性 #
print(html.xpath("//a/@href"))print(html.xpath("//li/@class"))
【結果為】
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']['item-0active', 'item-1', 'item-inactive', 'item-1', 'item-0']
自定義函數 #
我們從使用函數的過程中得到結論,就是有的函數不支持,有的支持,那問題來了,到底那些方法支持呢。我們在lxml官網找到了答案。 https://lxml.de/xpathxslt.html 。lxml 支持XPath 1.0 ,想使用其他擴展,使用libxml2,和libxslt的標准兼容的方式。 XPath 1.0官方文檔 以及其他版本的XPath文檔 https://www.w3.org/TR/xpath/
lxml supports XPath1.0, XSLT1.0andthe EXSLT extensions through libxml2andlibxsltina standards compliant way.
除此之外,lxml還提供了自定義函數的方式來擴展xpath的支持度 https://lxml.de/extensions.html
from lxml import etree#定義函數def ends_with(context,s1,s2):return s1[0].endswith(s2)if __name__ == '__main__':doc='''
【結果為】
[<Element li at 0x2816ed30548>, <Element li at 0x2816ed30508>]['first item', 'third item']
形參s1會傳入xpath中的第一個參數@class,但這里注意@class是個列表
形參s2會傳入xpath中的第二個參數'active','active'是個字元串
官網例子 https://lxml.de/extensions.html
defhello(context, a):return"Hello %s"% afromlxmlimportetreens = etree.FunctionNamespace(None)ns['hello'] = helloroot = etree.XML('<a><b>Haegar</b></a>')print(root.xpath("hello('Dr. Falken')"))# 結果為 Hello Dr. Falken
『叄』 如何在python中使用xpath
在進行網頁抓取的時候,分析定位html節點是獲取抓取信息的關鍵,目前我用的是lxml模塊(用來分析XML文檔結構的,當然也能分析html結構), 利用其lxml.html的xpath對html進行分析,獲取抓取信息。
首先,我們需要安裝一個支持xpath的python庫。目前在libxml2的網站上被推薦的python binding是lxml,也有beautifulsoup,不嫌麻煩的話還可以自己用正則表達式去構建,本文以lxml為例講解。
假設有如下的HTML文檔:
1 <html>
2 <body>
3 <form>
4 <div id='leftmenu'>
5 <h3>text</h3>
6 <ul id=』china』><!-- first location -->
7 <li>...</li>
8 <li>...</li>
9 ......
10 </ul>
11 <ul id=』england』><!-- second location-->
12 <li>...</li>
13 <li>...</li>
14 ......
15 </ul>
16 </div>
17 </form>
18 </body>
19 </html>
直接使用lxml處理:
1 import codecs
2 from lxml import etree
3 f=codecs.open("ceshi.html","r","utf-8")
4 content=f.read()
5 f.close()
6 tree=etree.HTML(content)
etree提供了HTML這個解析函數,現在我們可以直接對HTML使用xpath了,是不是有點小激動,現在就嘗試下吧。
在使用xpath之前我們先來看看作為對照的jQuery和RE。
在jQuery里要處理這種東西就很簡單,特別是假如那個ul節點有id的話(比如是<ul id=』china』>):
$("#china").each(function(){...});
具體到此處是:
$("#leftmenu").children("h3:contains('text')").next("ul").each(function(){...});
找到id為leftmenu的節點,在其下找到一個內容包含為」text」的h3節點,再取其接下來的一個ul節點。
在python里要是用RE來處理就略麻煩一些:
block_pattern=re.compile(u"<h3>檔案</h3>(.*?)<h3>", re.I | re.S)
m=block_pattern.findall(content)
item_pattern=re.compile(u"<li>(.*?)</li>", re.I | re.S)
items=item_pattern.findall(m[0])
for i in items:
print i
那麼用xpath要怎麼做呢?其實跟jQuery是差不多的:
nodes=tree.xpath("/descendant::ul[@id='china']")
當然,現在沒有id的話也就只能用類似於jQuery的方法了。完整的xpath應該是這樣寫的(注意,原文件中的TAG有大小寫的情況,但是在XPATH里只能用小寫):
nodes=tree.xpath(u"/html/body/form/div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")
更簡單的方法就是像jQuery那樣直接根據id定位:
nodes=tree.xpath(u"//div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")
這兩種方法返回的結果中,nodes[0]就是那個「text」的h3節點後面緊跟的第一個ul節點,這樣就可以列出後面所有的ul節點內容了。
如果ul節點下面還有其他的節點,我們要找到更深節點的內容,如下的循環就是把這些節點的文本內容列出:
nodes=nodes[0].xpath("li/a")
for n in nodes:
print n.text
對比三種方法應該可以看出xpath和jQuery對於頁面的解析都是基於XML的語義進行,而RE則純粹是基於plain text。RE對付簡單的頁面是沒有問題,如果頁面結構復雜度較高的時候(比如一堆的DIV來回嵌套之類),設計一個恰當的RE pattern可能會遠比寫一個xpath要復雜。特別是目前主流的基於CSS的頁面設計方式,其中大部分關鍵節點都會有id――對於使用jQuery的頁面來說則更是如此,這時xpath相比RE就有了決定性的優勢。
附錄:基本XPATH語法介紹,詳細請參考XPath的官方文檔
XPATH基本上是用一種類似目錄樹的方法來描述在XML文檔中的路徑。比如用「/」來作為上下層級間的分隔。第一個「/」表示文檔的根節點(注意,不是指文檔最外層的tag節點,而是指文檔本身)。比如對於一個HTML文件來說,最外層的節點應該是」/html」。
同樣的,「..」和「.」分別被用來表示父節點和本節點。
XPATH返回的不一定就是唯一的節點,而是符合條件的所有節點。比如在HTML文檔里使用「/html/head/scrpt」就會把head里的所有script節點都取出來。
為了縮小定位范圍,往往還需要增加過濾條件。過濾的方法就是用「[」「]」把過濾條件加上。比如在HTML文檔里使用「/html/body/div[@id='main']」,即可取出body里id為main的div節點。
其中@id表示屬性id,類似的還可以使用如@name, @value, @href, @src, @class….
而 函數text()的意思則是取得節點包含的文本。比如:<div>hello<p>world</p>< /div>中,用」div[text()='hello']「即可取得這個div,而world則是p的text()。
函數position()的意思是取得節點的位置。比如「li[position()=2]」表示取得第二個li節點,它也可以被省略為「li[2]」。
不過要注意的是數字定位和過濾 條件的順序。比如「ul/li[5][@name='hello']」表示取ul下第五項li,並且其name必須是hello,否則返回空。而如果用 「ul/li[@name='hello'][5]」的意思就不同,它表示尋找ul下第五個name為」hello「的li節點。
此外,「*」可以代替所有的節點名,比如用」/html/body/*/span」可以取出body下第二級的所有span,而不管它上一級是div還是p或是其它什麼東東。
而 「descendant::」前綴可以指代任意多層的中間節點,它也可以被省略成一個「/」。比如在整個HTML文檔中查找id為「leftmenu」的 div,可以用「/descendant::div[@id='leftmenu']」,也可以簡單地使用「 //div[@id='leftmenu']」。
至於「following-sibling::」前綴就如其名所說,表示同一層的下一個節點。」following-sibling::*」就是任意下一個節點,而「following-sibling::ul」就是下一個ul節點。
『肆』 Python lxml包下面的xpath基本用法
對於網頁數據抓取,有BeautifulSoup、lxml以及正則表達式三種方法,其中正則表達式過於復雜,而beautifulsoup和lxml使用起來較為方便。以前簡單使用過beautifulsoup(美味湯),後面為了擴展一下,熟悉一下lxml進行數據抓取。
先貼一個lxml的簡單框架:
其中,最主要的在於xpath路徑的獲取和解析,而XPath就是地址,具體地,就是需要知道所要尋找的內容處在哪個地址下。一般而言,我們可以根據開發者工具來定位我們需要的元素,然後右擊選擇其所在xpath,選擇初步的路徑,如下圖所示,
這只是一種簡單的方法,更重要的,需要掌握xpath的語法規則,下面分別論述。
使用xpath獲取信息,主要包括獲取本文和獲取屬性,基本用法為
對比可以看出,一個是採用text()獲取文本,一個是採用@屬性獲取屬性值。而前面標簽後面方括弧就是來對標簽進行篩選的。一般而言,通過選擇器可以獲取諸如/html/body/div[@class="useful"]/ul/li/text()的信息,但是開頭的信息沒有標志性,採用//div[@class="useful"]/ul/li/text()即可。
這個地方即涉及到了xpath的語法選擇,主要包括以下幾點:
而在選擇器方面,包括以下幾個
除此之外,在獲取了一個元素之後,我們需要獲取其下面元素的屬性,即要對基於xpath獲取的元素再次採用xpath,此時的獲取方式為:
另外,我們也可以獲取節點下面所有的字元串,方法為string(.),示例為:
懶得打字了,下面的截圖來自W3Cschool, https://www.w3cschool.cn/lxml/_lxml-98h23fk0.html
主要的Xpath運算符包括以下:
按順序選擇等進一步的內容可以移步 https://www.w3cschool.cn/lxml/_lxml-eh1k3fk6.html
具體到不同的網頁上,需要的其他知識就更多了,慢慢補充吧。不過似乎還是beautifulsoup好用一些,哈哈。
參考資料:
https://blog.csdn.net/weixin_39851008/article/details/109960957
https://www.w3cschool.cn/lxml/_lxml-98h23fk0.html
『伍』 python中如何將通過xpath提取部分html代碼或者有什麼其他方法
mport urllib.request
import re
def getHtml(url):
page = urllib.request.urlopen(url)
html = page.read()
html = html.decode('GBK')
return html
def getMeg(html):
reg = re.compile(r'******')
meglist = re.findall(reg,html)
for meg in meglist:
with open('out.txt',mode='a',encoding='utf-8') as file:
file.write('%s\n' % meg)
if __name__ == "__main__":
html = getHtml(url)
getMeg(html)
『陸』 如何用Python爬取出HTML指定標簽內的文本
你好!
可以通過lxml來獲取指定標簽的內容。
#安裝lxml
pipinstalllxml
importrequests
fromlxmlimporthtml
defgetHTMLText(url):
....
etree=html.etree
root=etree.HTML(getHTMLText(url))
#這里得到一個表格內tr的集合
trArr=root.xpath("//div[@class='news-text']/table/tbody/tr");
#循環顯示tr裡面的內容
fortrintrArr:
rank=tr.xpath("./td[1]/text()")[0]
name=tr.xpath("./td[2]/div/text()")[0]
prov=tr.xpath("./td[3]/text()")[0]
strLen=22-len(name.encode('GBK'))+len(name)
print('排名:{:<3},學校名稱:{:<{}} ,省份:{}'.format(rank,name,strLen,prov))
希望對你有幫助!