1.簡介
XPath是一門在XML和HTML文檔中查找信息的語言,可以用來在XML和HTML文檔中對元素和屬性進行遍歷
XPath的安裝
Chrome插件XPath Helper
點Chrome瀏覽器右上角:更多工具-----擴展程序-----谷歌商店--------勾選XPath Helper(需要翻牆)
2.語法詳解

#1.選取節點 ''' / 如果是在最前面,代表從根節點選取,否則選擇某節點下的某個節點.只查詢子一輩的節點 /html 查詢到一個結果 /div 查詢到0個結果,因為根節點以下只有一個html子節點 /html/body 查詢到1個結果 // 查詢所有子孫節點 //head/script //div . 選取當前節點 .. 選取當前節點的父節點 @ 選取屬性 //div[@id] 選擇所有帶有id屬性的div元素 <div id="sidebar" class="sidebar" data-lg-tj-track-code="index_navigation" data-lg-tj-track-type="1"> ''' #2.謂語 ''' 謂語是用來查找某個特定的節點或者包含某個指定的值的節點,被嵌在方括號中。 //body/div[1] body下的第一個div元素 //body/div[last()] body下的最后一個div元素 //body/div[position()<3] body下的位置小於3的元素 //div[@id] div下帶id屬性的元素 <div id="sidebar" class="sidebar" data-lg-tj-track-code="index_navigation" data-lg-tj-track-type="1"> //input[@id="serverTime"] input下id="serverTime"的元素 模糊匹配 //div[contains(@class,'f1')] div的class屬性帶有f1的 通配符 * //body/* body下面所有的元素 //div[@*] 只要有用屬性的div元素 //div[@id='footer'] //div 帶有id='footer'屬性的div下的所有div元素 //div[@class='job_bt'] //dd[@class='job-advantage'] 運算符 //div[@class='job_detail'] and @id='job_tent' //book/title | //book/price 選取 book 元素的所有 title 和 price 元素。 也可以百度搜索XPath語法 .//a/text() 當前標簽下所有a標簽的文字內容 //tr[position()>1 and position()<11] 位置大於1小於11 ''' #需要注意的知識點 ''' 1./和//的區別:/代表子節點,//代表子孫節點,//用的比較多 2.contains有時候某個屬性中包含了多個值,那么使用contains函數 //div[contains(@class,'lg')] 3.謂語中的下標是從1開始的,不是從0開始的 '''
3.要在python中使用xpath,要導入一個庫 lxml。
這個是C編寫的庫,直接pip3 install lxml可能會有一些顯示問題,但是不影響使用。
然而程序員特有的代碼潔癖讓我看見波浪線也不會爽,所以去https://www.lfd.uci.edu/~gohlke/pythonlibs/下載lxml的whl文件進行pip(根據自己的pycharm版本選擇)
4.lxml和xpath的結合使用

# -*-coding:utf8 -*- from lxml import etree #1.獲取所有tr標簽 #2.獲取第2個tr標簽 #3.獲取所有class等於even的標簽 #4.獲取所有a標簽的href屬性 #5.獲取所有的職位信息(純文本) parser=etree.HTMLParser(encoding='utf-8') html=etree.parse('tencent.html',parser=parser) #1.獲取所有tr標簽 #xpath函數返回的是一個列表 # trs=html.xpath('//tr') # print(trs) # for tr in trs: # print(etree.tostring(tr,encoding='utf-8').decode('utf-8')) #2.獲取第2個tr標簽 # trs=html.xpath('//tr[2]')[0] #這樣直接找第2個tr標簽,實際上會把所有的table下的第二個tr標簽找出來, #為了更精准,可以先把table標簽找到,再找這個table下的第二個tr標簽 # trs=html.xpath('//table[@class="tablelist"]//tr[1]')[0] # print(etree.tostring(trs,encoding='utf-8').decode('utf-8')) #3.獲取所有class等於even的標簽 # trs=html.xpath("//tr[@class='even']") # for tr in trs: # print(etree.tostring(tr, encoding='utf-8').decode('utf-8')) #4.獲取所有a標簽的href屬性 # a_list=html.xpath('//a/@href') # for a in a_list: # print(a) #5.獲取所有的職位信息(純文本) trs=html.xpath('//tr[position()>1 and position()<11]') positions=[] for tr in trs: #寫了//后,則一定會從整個文檔找a標簽,會無視前面的tr # href=tr.xpath('//a') #寫了.后,則獲取當前標簽下的a標簽 href=tr.xpath('.//a/@href')[0] fullurl='http://hr.tencent.com/'+href #title文本信息不是td[1]的直接子元素標簽,所以要加./td[1]//text() title=tr.xpath('./td[1]//text()')[0] category=tr.xpath('./td[2]/text()')[0] nums=tr.xpath('./td[3]/text()')[0] address=tr.xpath('./td[4]/text()')[0] pubtime=tr.xpath('./td[5]/text()')[0] position={ 'url':fullurl, 'title':title, 'category':category, 'nums':nums, 'pubtime':pubtime } positions.append(position) # print(positions) #6.獲取純文本信息還可以用string # print(html.xpath("string(//tr[1])")) # trs=html.xpath('//tr') # for tr in trs: # print(tr.xpath("string(.)").strip()
5.實戰案例,豆瓣電影爬蟲

# -*-coding:utf8 -*- #1.將目標網站上的頁面抓取下來 #2.將抓取下來的數據根據一定的規則進行提取 import requests from lxml import etree #1.將目標網站上的頁面抓取下來 headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36', 'Referer':'https://www.douban.com/', } url='https://movie.douban.com/' response=requests.get(url,headers=headers) text=response.text html=etree.HTML(text) ul=html.xpath("//ul[@class='ui-slide-content']")[0] # print(etree.tostring(ul,encoding='utf-8').decode('utf-8')) lis=ul.xpath('./li[@data-title]') movies=[] for li in lis: title=li.xpath('@data-title')[0] score=li.xpath('@data-rate')[0] duration=li.xpath('@data-duration')[0] region=li.xpath('@data-region')[0] director=li.xpath('@data-director')[0] actors=li.xpath('@data-actors')[0] thumbnail=li.xpath('.//img/@src')[0] movie={ 'title':title, 'score':score, 'duration':duration, 'region':region, 'director':director, 'actors':actors, 'thumbnail':thumbnail } movies.append(movie) print(movies)
6.實戰案例,電影天堂爬蟲

# -*-coding:utf8 -*- import requests from lxml import etree # url='https://www.dytt8.net/html/gndy/dyzz/list_23_1.html' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' } # response=requests.get(url,headers=headers) # 在電影天堂的網頁中,因為編碼方式,requests庫猜錯了,所以response.text出現亂碼 # print(response.text) # text=response.content.decode('gbk') BaseDomain = 'https://www.dytt8.net' def get_detail_url(url): response = requests.get(url, headers=headers) # print(response.encoding) # 默認解碼方式ISO-8859-1 # text=response.content.decode('gbk') # 在使用gbk解碼時遇到了一些問題,第五頁里有特殊字符,無法解析 # 估計是因為xpath默認解碼方式和gbk不一致導致的,這時可以直接傳requests.text # 因為要獲取的是英文字符,不指定解碼方式也能得到 html = etree.HTML(response.text) detail_urls = html.xpath('//table[@class="tbspan"]//a/@href') detail_urls = list(map(lambda url: BaseDomain + url, detail_urls)) return detail_urls def parse_detail_page(url): response = requests.get(url, headers=headers) text = response.content.decode('gbk') html = etree.HTML(text) title = html.xpath("//font[@color='#07519a' and position()=1]/text()") zoomE = html.xpath("//div[@id='Zoom']")[0] imgs = zoomE.xpath(".//img/@src") cover = imgs[0] screenshot = imgs[1] infos = zoomE.xpath(".//text()") movie = { 'title': title, 'cover': cover, 'screenshot': screenshot } def parse_info(info, rule): return info.replace(rule, '').strip() for index, info in enumerate(infos): if 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['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, '◎主 演') actors = [] actors.append(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='' for x in range(index+1,len(infos)): if infos[x].startswith('【下載地址】'): break info = info + infos[x].strip() movie['profile']=info download_url = html.xpath("//td[@bgcolor='#fdfddf']//a/@href")[0] movie['download_url']=download_url return movie def spider(): # url = ['https://www.dytt8.net/html/gndy/dyzz/list_23_%s.html' % i for i in range(1, 8)] base_url = 'https://www.dytt8.net/html/gndy/dyzz/list_23_{}.html' movies=[] for x in range(1, 8): url = base_url.format(x) detail_urls = get_detail_url(url) for detail_url in detail_urls: movie = parse_detail_page(detail_url) movies.append(movie) print(movies) if __name__ == '__main__': spider()
7.實戰案例,騰訊招聘爬蟲

# -*-coding:utf8 -*- import requests from lxml import etree base_url = 'https://hr.tencent.com/position.php?tid=87&start={}0#a' base_domain = 'https://hr.tencent.com/' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' } positions=[] def parse_url(url): detail_urls=[] response=requests.get(url,headers=headers) html=etree.HTML(response.text) trs=html.xpath("//table[@class='tablelist']//tr[position()>1 and position()<12]") for tr in trs: href=tr.xpath('.//a/@href')[0] url=base_domain+href detail_urls.append(url) return detail_urls def parse_detail_page(url): response=requests.get(url,headers=headers) html=etree.HTML(response.text) zoomE=html.xpath('//table[@class="tablelist textl"]')[0] title=zoomE.xpath('.//tr[1]/td/text()')[0] city=zoomE.xpath('.//tr[2]/td[1]/text()')[0] category=zoomE.xpath('.//tr[2]/td[2]/text()')[0] nums=zoomE.xpath('.//tr[2]/td[3]/text()')[0] duty=zoomE.xpath('.//tr[3]//ul//text()') dutys='' for i in duty: dutys=dutys+i.strip() require=zoomE.xpath('.//tr[4]//ul//text()') requires='' for i in require: requires=requires+i.strip() position={ 'title':title, 'city':city, 'category':category, 'nums':nums, 'dutys':dutys, 'requires':requires } return position if __name__ == '__main__': for i in range(1,10): url=base_url.format(i) detail_urls=parse_url(url) for detail_url in detail_urls: position=parse_detail_page(detail_url) positions.append(position) print(position)