1驱负、xpath學習筆記
1)xpath描述
??xpath(XML Path Language)是一門在XML和HTML文檔中查找信息的語言,可用來在XML和HTML文檔中對元素和屬性進行遍歷寝贡。
2)xpaht開發(fā)工具
??A)Chrome插件XPath Helper
??B)Firefox插件Try XPath
3)xpath語法
選取節(jié)點:xpath使用路徑表達式來選取XML文檔中的節(jié)點或節(jié)點集悼吱。
??A)當/
在最前面時沸停,選取根節(jié)點下面的元素堰酿,否則選取某個節(jié)點下面的元素疾宏,然后寫標簽名,再寫謂詞進行選取触创,示例代碼如下:
??/div[@class = 'abc']
??/div/tr[@class = 'xyz']
??B)使用//
選取整個頁面當中的元素坎藐,不管在任何位置都是,然后寫標簽名哼绑,再寫謂詞進行選取岩馍,示例代碼如下:
??//div[@class = 'abc']
??//div//tr[@class = 'xyz']
??C)使用@
選取某個節(jié)點的屬性,使用中括號[]
括起來抖韩,示例代碼如下:
??//div[@class = 'abc']
??D)使用.
指定當前節(jié)點蛀恩,示例代碼如下:
??./div[@class = 'abc']
謂詞用法:謂詞用來查找某個特定的節(jié)點或包含某個指定的值的節(jié)點,被嵌在中括號
[]
中帽蝶。
??A)選取某個節(jié)點下的第一個節(jié)點元素赦肋,示例代碼如下:
??//div//tr[1]
??B)選取某個節(jié)點下的倒數第二個節(jié)點元素块攒,示例代碼如下:
??//div//tr[last()]
??C)選取某個節(jié)點下的前面兩個節(jié)點元素励稳,示例代碼如下:
??//div//tr[position() < 3]
??D)選取某個節(jié)點下?lián)碛心硞€屬性的節(jié)點元素,示例代碼如下:
??//div//tr[@class]
??E)選取某個節(jié)點下等于某個值的所有屬性的節(jié)點元素囱井,示例代碼如下:
??//div//tr[@class = 'abc]
通配符:使用
*
代表通配符驹尼。
??A)使用*
匹配任意節(jié)點,示例代碼如下:
??//div/*
??B)使用@*
匹配節(jié)點中的任意屬性庞呕,示例代碼如下:
??//div//tr[@*]
選取多個路徑:通過在路徑表達式中使用
|
運算符新翎,可以選取若干個路徑程帕,示例代碼如下:
??//div//tr[last()] | //div//td[@class = 'abc] | //div//p
需要注意的知識點:
??1、/
和//
的區(qū)別:/
代表只獲取直接子節(jié)點地啰,//
代表獲取子孫節(jié)點愁拭。一般//
用的比較多,當然也要視情況而定亏吝。
??2岭埠、contains:有時候某個屬性中包含了多個值,那么可以使用contains()
函數蔚鸥,示例代碼如下:
??//div[contains(@class, 'job_detail')]
??3惜论、謂詞中的下標是從1開始的,不是從0開始的止喷。
2馆类、lxml學習筆記
1)lxml描述
??lxml 是 一個HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 數據弹谁。
??lxml和正則一樣乾巧,也是用 C 實現(xiàn)的,是一款高性能的 Python HTML/XML 解析器僵闯,可以使用之前學習的XPath語法卧抗,來快速的定位特定元素以及節(jié)點信息。
??lxml Python 官方文檔:http://lxml.de/index.html鳖粟。
2)解析HTML字符串
??使用
lxml.etree.HTML
進行解析社裆,示例代碼如下:
htmlElement = etree.HTML(text)
print(etree.tostring(htmlElement, encoding = 'utf-8').decode('utf-8'))
3)解析HTML文件
??使用
lxml.etree.parse
進行解析,示例代碼如下:
htmlElement = etree.HTML('tencent.html')
print(etree.tostring(htmlElement, encoding = 'utf-8').decode('utf-8'))
??這個函數默認使用的是
XML
解析器向图,所以如果碰到一些不規(guī)范的HTML
代碼的時候就會解析錯誤泳秀,這時候就要自己創(chuàng)建HTML
解析器,示例代碼如下:
parser = etree.HTMLParser(encoding = 'utf-8')
htmlElement = etree.parse('lagou.html', parser = parser)
print(etree.tostring(htmlElement, encoding = 'utf-8').decode('utf-8'))
3榄攀、lxml與xpath結合
1)獲取所有li
標簽
from lxml import etree
html = etree.parse('hello.html')
print type(html) # 顯示etree.parse() 返回類型
result = html.xpath('//li')
print(result) # 打印<li>標簽的元素集合
2)獲取所有l(wèi)i元素下的所有class屬性的值
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/@class')
print(result)
3)獲取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)
4)獲取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)
5)獲取li標簽下的a標簽里的所有class
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/a//@class')
print(result)
6)獲取最后一個li的a的href屬性對應的值
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()]/a/@href')
# 謂語 [last()] 可以找到最后一個元素
print(result)
7)獲取倒數第二個li元素的內容
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a')
# text 方法可以獲取元素內容
print(result[0].text)
8)獲取倒數第二個li元素的內容的第二種方式
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a/text()')
print(result)
9)注意事項
??A)使用
xpath
語法吕嘀,應該使用Element.xpath
方法來執(zhí)行xpath
的選擇,xpath
函數返回的永遠都是一個列表贞瞒;
??B)獲取文本偶房,是通過xpath
中的text()
函數;
??C)在某個標簽下军浆,再執(zhí)行xpath
函數棕洋,獲取這個標簽下的子孫元素時,應該在//
之前加一個點.
乒融,代表是在在當前元素下獲取掰盘。
4摄悯、實戰(zhàn)示例
??使用
requests
和xpath
爬取“電影天堂”中“最新電影”的所有數據,示例代碼如下:
import requests
from lxml import etree
def url_status_code(target_url, headers):
'''
獲取target_url的status_code(即url訪問狀態(tài)碼)愧捕,常用狀態(tài)碼如下:
200:返回消息為“OK”奢驯,描述-請求被確認
201:返回消息為“Created”,描述-請求是完整的次绘,新的資源被創(chuàng)建
202:返回消息為“Accepted”叨橱,描述-請求被接受,但未處理完
301:返回消息為“Moved Permanently”断盛,描述-被請求的頁面已經移動到了新的URL下
302:返回消息為“Found”罗洗,描述-被請求的頁面暫時性地移動到了新的URL下
303:返回消息為“See Other”,描述-被請求的頁面可以在一個不同的URL下找到
400:返回消息為“Bad Request”钢猛,描述-服務器無法識別請求
403:返回消息為“Forbidden”伙菜,描述-禁止訪問所請求的頁面
500:返回消息為“Internal Server Error”,描述-請求不完整命迈,服務器遇見了出乎意料的狀況
502:返回消息為“Bad Gateway”贩绕,描述-請求不完整,服務器從上游服務器接受了一個無效的響應
503:返回消息為“Service Unavailable”壶愤,描述-請求不完整淑倾,服務器暫時重啟或關閉
504:返回消息為“Gateway Timeout”,描述-網關超時
:param target_url: (類型:字符串)指定的url地址
:param headers: (類型:字典)設置的請求頭信息
:return: (類型:整型數值)返回指定url的請求狀態(tài)碼
'''
response = requests.get(target_url, headers=headers) # 使用requests的get()方法請求target_url
status_code = response.status_code # 獲取url訪問的請求狀態(tài)碼征椒,并賦值給status_code
return status_code # 返回指定url的請求狀態(tài)碼
def get_page_url(base_domain, page_num_max, headers):
'''
獲取指定頁碼數中每頁的url地址娇哆,該函數為生成器。
:param base_domain: (類型:字符串)訪問站點的域名
:param page_num_max: (類型:整型數值)最大獲取頁碼數
:param headers: (類型:字典)設置的請求頭信息
:return: (類型:字符串)返回每頁的url地址
'''
page_num = 1
while page_num <= page_num_max:
page_url = base_domain + 'html/gndy/dyzz/list_23_{}.html'.format(page_num) # 拼接每頁的url地址
status_code = url_status_code(page_url, headers) # 獲取指定url頁面的請求狀態(tài)碼
'''
判斷每個url頁面的請求狀態(tài)碼是否為200勃救,如果是碍讨,則返回,否則結束蒙秒。
'''
if status_code == 200:
page_num += 1
yield page_url
else:
break
def crawl_html(target_url, headers):
'''
獲取target_url的內容勃黍,并返回
:param target_url: (類型:字符串)指定的url地址
:param headers: (類型:字典)設置的請求頭信息
:return: 返回target_url頁面內容
'''
response = requests.get(target_url, headers = headers)
text = response.content.decode('gbk', errors = 'ignore')
return text
def get_detail_url(text, base_domain):
'''
獲取指定text中每個對象的詳情頁url,該函數為生成器晕讲。
:param text: 指定的text內容
:param base_domain: (類型:字符串)訪問站點的域名
:return: (類型:字符串)返回詳情頁url
'''
html = etree.HTML(text)
tables = html.xpath("http://table[@class='tbspan']")
for table in tables:
detail_url = base_domain + table.xpath(".//a/@href")[0]
yield detail_url
def parse_detail_url(detail_url, headers):
'''
解析詳情頁中的內容
:param detail_url: (類型:字符串)指定的詳情頁url
:param headers: (類型:字典)設置的請求頭信息
:return: (類型:字典)返回詳情頁中解析出來的內容
'''
movie = {}
text = crawl_html(detail_url, headers)
html = etree.HTML(text)
title = html.xpath("http://div[@class='title_all']//font[@color='#07519a']//text()")[0]
movie['title'] = title
zoom = html.xpath("http://div[@id='Zoom']")[0]
cover = zoom.xpath(".//img/@src")
if cover != []:
cover = cover[0]
movie['cover'] = cover
infos = zoom.xpath(".//text()")
def parse_info(info, rule):
return info.replace(rule, "").strip()
for index, info in enumerate(infos):
if info.startswith("◎譯 名"):
info = parse_info(info, "◎譯 名")
movie['translation'] = info
elif info.startswith("◎片 名"):
info = parse_info(info, "◎片 名")
movie['name'] = info
elif info.startswith("◎年 代"):
info = parse_info(info, "◎年 代")
movie['year'] = info
elif info.startswith("◎產 地"):
info = parse_info(info, "◎產 地")
movie['country'] = info
elif info.startswith("類 別"):
info = parse_info(info, "類 別")
movie['category'] = info
elif info.startswith("語 言"):
info = parse_info(info, "語 言")
movie['language'] = info
elif info.startswith("字 幕"):
info = parse_info(info, "字 幕")
movie['subtitles'] = info
elif info.startswith("◎上映日期"):
info = parse_info(info, "◎上映日期")
movie['release_date'] = info
elif info.startswith("◎IMDb評分"):
info = parse_info(info, "◎IMDb評分")
movie['IMDb'] = info
elif info.startswith("◎豆瓣評分"):
info = parse_info(info, "◎豆瓣評分")
movie['douban_rating'] = info
elif info.startswith("◎片 長"):
info = parse_info(info, "◎片 長")
movie['duration'] = info
elif info.startswith("◎導 演"):
info = parse_info(info, "◎導 演")
movie['director'] = info
elif info.startswith("◎編 劇"):
info = parse_info(info, "◎編 劇")
writers = [info]
for x in range(index+1, len(infos)):
writer = infos[x].strip()
if writer.startswith("◎"):
break
writers.append(writer)
movie['writers'] = writers
elif info.startswith("◎主 演"):
info = parse_info(info, "◎主 演")
actors = [info]
for x in range(index + 1, len(infos)):
actor = infos[x].strip()
if actor.startswith("◎"):
break
actors.append(actor)
movie['actors'] = actors
elif info.startswith("◎標 簽"):
info = parse_info(info, "◎標 簽")
movie['label'] = info
elif info.startswith("◎簡 介"):
info = parse_info(info, "◎簡 介")
for x in range(index + 1, len(infos)):
profile = infos[x].strip()
if profile.startswith("【下載地址】"):
break
movie['profile'] = profile
download_url = html.xpath("http://td[@bgcolor='#fdfddf']/a/text()")
movie['download_url'] = download_url
return movie
if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.9 Safari/537.36',
'Cookie': '37cs_pidx=1; 37cs_user=37cs20498016905; 37cs_show=253; UM_distinctid=171521ba5e1a5-0d67214e920904-721f3a40-13c680-171521ba5e27a; bz_finger=6133b30a7ebc8a1a448492bb4dbea710; CNZZDATA1260535040=1919690611-1586220307-https%253A%252F%252Fwww.dytt8.net%252F%7C1586225707; cscpvrich5041_fidx=4',
}
base_domain = 'https://www.dytt8.net/'
page_num_max = 1 # 可以通過修改page_num_max的值來確定需要爬取總頁數
movies = []
page_url_generator = get_page_url(base_domain, page_num_max, headers)
for page_url in page_url_generator:
text = crawl_html(page_url, headers)
detail_url_generator = get_detail_url(text, base_domain)
for detail_url in detail_url_generator:
movie = parse_detail_url(detail_url, headers)
movies.append(movies)
print(movie)
print(movies)