XPath語法和lxml模塊
什么是XPath?
xpath(XML Path Language)是一門在XML和HTML文檔中查找信息的語言滑负,可用來在XML和HTML文檔中對元素和屬性進行遍歷。
XPath開發(fā)工具
- Chrome插件XPath Helper娃善。
- Firefox插件Try XPath憔涉。
XPath語法
選取節(jié)點:
XPath 使用路徑表達式來選取 XML 文檔中的節(jié)點或者節(jié)點集。這些路徑表達式和我們在常規(guī)的電腦文件系統(tǒng)中看到的表達式非常相似链嘀。
表達式 | 描述 | 示例 | 結(jié)果 |
---|---|---|---|
nodename | 選取此節(jié)點的所有子節(jié)點 | bookstore | 選取bookstore下所有的子節(jié)點 |
/ | 如果是在最前面,代表從根節(jié)點選取档玻。否則選擇某節(jié)點下的某個節(jié)點 | /bookstore | 選取根元素下所有的bookstore節(jié)點 |
// | 從全局節(jié)點中選擇節(jié)點怀泊,隨便在哪個位置 | //book | 從全局節(jié)點中找到所有的book節(jié)點 |
@ | 選取某個節(jié)點的屬性 | //book[@price] | 選擇所有擁有price屬性的book節(jié)點 |
. | 當前節(jié)點 | ./a | 選取當前節(jié)點下的a標簽 |
謂語:
謂語用來查找某個特定的節(jié)點或者包含某個指定的值的節(jié)點,被嵌在方括號中误趴。
在下面的表格中霹琼,我們列出了帶有謂語的一些路徑表達式,以及表達式的結(jié)果:
路徑表達式 | 描述 |
---|---|
/bookstore/book[1] | 選取bookstore下的第一個子元素 |
/bookstore/book[last()] | 選取bookstore下的倒數(shù)第二個book元素凉当。 |
bookstore/book[position()<3] | 選取bookstore下前面兩個子元素枣申。 |
//book[@price] | 選取擁有price屬性的book元素 |
//book[@price=10] | 選取所有屬性price等于10的book元素 |
通配符
*表示通配符。
通配符 | 描述 | 示例 | 結(jié)果 |
---|---|---|---|
* | 匹配任意節(jié)點 | /bookstore/* | 選取bookstore下的所有子元素看杭。 |
@* | 匹配節(jié)點中的任何屬性 | //book[@*] | 選取所有帶有屬性的book元素忠藤。 |
選取多個路徑:
通過在路徑表達式中使用“|”運算符流码,可以選取若干個路徑类溢。
示例如下:
//bookstore/book | //book/title
# 選取所有book元素以及book元素下所有的title元素
lxml庫
lxml 是 一個HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 數(shù)據(jù)简肴。
lxml和正則一樣贮缅,也是用 C 實現(xiàn)的榨咐,是一款高性能的 Python HTML/XML 解析器,我們可以利用之前學習的XPath語法谴供,來快速的定位特定元素以及節(jié)點信息祭芦。
lxml python 官方文檔:http://lxml.de/index.html
需要安裝C語言庫,可使用 pip 安裝:pip install lxml
基本使用:
我們可以利用他來解析HTML代碼憔鬼,并且在解析HTML代碼的時候,如果HTML代碼不規(guī)范胃夏,他會自動的進行補全轴或。示例代碼如下:
# 使用 lxml 的 etree 庫
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a> # 注意,此處缺少一個 </li> 閉合標簽
</ul>
</div>
'''
#利用etree.HTML仰禀,將字符串解析為HTML文檔
html = etree.HTML(text)
# 按字符串序列化HTML文檔
result = etree.tostring(html)
print(result)
輸入結(jié)果如下:
<html><body>
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
</body></html>
可以看到照雁。lxml會自動修改HTML代碼。例子中不僅補全了li標簽,還添加了body饺蚊,html標簽萍诱。
從文件中讀取html代碼:
除了直接使用字符串進行解析,lxml還支持從文件中讀取內(nèi)容污呼。我們新建一個hello.html文件:
<!-- hello.html -->
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
然后利用etree.parse()
方法來讀取文件裕坊。示例代碼如下:
from lxml import etree
# 讀取外部文件 hello.html
html = etree.parse('hello.html')
result = etree.tostring(html, pretty_print=True)
print(result)
輸入結(jié)果和之前是相同的。
在lxml中使用XPath語法:
-
獲取所有l(wèi)i標簽:
from lxml import etree html = etree.parse('hello.html') print type(html) # 顯示etree.parse() 返回類型 result = html.xpath('//li') print(result) # 打印<li>標簽的元素集合
-
獲取所有l(wèi)i元素下的所有class屬性的值:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li/@class') print(result)
-
獲取li標簽下href為
www.baidu.com
的a標簽:from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li/a[@href="www.baidu.com"]') print(result)
-
獲取li標簽下所有span標簽:
from lxml import etree html = etree.parse('hello.html') #result = html.xpath('//li/span') #注意這么寫是不對的: #因為 / 是用來獲取子元素的燕酷,而 <span> 并不是 <li> 的子元素籍凝,所以,要用雙斜杠 result = html.xpath('//li//span') print(result)
-
獲取li標簽下的a標簽里的所有class:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li/a//@class') print(result)
-
獲取最后一個li的a的href屬性對應的值:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li[last()]/a/@href') # 謂語 [last()] 可以找到最后一個元素 print(result)
-
獲取倒數(shù)第二個li元素的內(nèi)容:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li[last()-1]/a') # text 方法可以獲取元素內(nèi)容 print(result[0].text)
-
獲取倒數(shù)第二個li元素的內(nèi)容的第二種方式:
from lxml import etree html = etree.parse('hello.html') result = html.xpath('//li[last()-1]/a/text()') print(result)
使用requests和xpath爬取電影天堂
示例代碼如下:
import requests
from lxml import etree
BASE_DOMAIN = 'http://www.dytt8.net'
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Referer': 'http://www.dytt8.net/html/gndy/dyzz/list_23_2.html'
}
def spider():
url = 'http://www.dytt8.net/html/gndy/dyzz/list_23_1.html'
resp = requests.get(url,headers=HEADERS)
# resp.content:經(jīng)過編碼后的字符串
# resp.text:沒有經(jīng)過編碼苗缩,也就是unicode字符串
# text:相當于是網(wǎng)頁中的源代碼了
text = resp.content.decode('gbk')
# tree:經(jīng)過lxml解析后的一個對象饵蒂,以后使用這個對象的xpath方法,就可以
# 提取一些想要的數(shù)據(jù)了
tree = etree.HTML(text)
# xpath/beautifulsou4
all_a = tree.xpath("http://div[@class='co_content8']//a")
for a in all_a:
title = a.xpath("text()")[0]
href = a.xpath("@href")[0]
if href.startswith('/'):
detail_url = BASE_DOMAIN + href
crawl_detail(detail_url)
break
def crawl_detail(url):
resp = requests.get(url,headers=HEADERS)
text = resp.content.decode('gbk')
tree = etree.HTML(text)
create_time = tree.xpath("http://div[@class='co_content8']/ul/text()")[0].strip()
imgs = tree.xpath("http://div[@id='Zoom']//img/@src")
# 電影海報
cover = imgs[0]
# 電影截圖
screenshoot = imgs[1]
# 獲取span標簽下所有的文本
infos = tree.xpath("http://div[@id='Zoom']//text()")
for index,info in enumerate(infos):
if info.startswith("◎年 代"):
year = info.replace("◎年 代","").strip()
if info.startswith("◎豆瓣評分"):
douban_rating = info.replace("◎豆瓣評分",'').strip()
print(douban_rating)
if info.startswith("◎主 演"):
# 從當前位置酱讶,一直往下面遍歷
actors = [info]
for x in range(index+1,len(infos)):
actor = infos[x]
if actor.startswith("◎"):
break
actors.append(actor.strip())
print(",".join(actors))
if __name__ == '__main__':
spider()