python 爬虫(七)lxml模块 + lxml数据提取(字符串的xmlhtml文件--转换--element对象--转换--字符串)调用XPath方法筛选数据 + 案例(扇贝 酷狗 网易云音乐)

mac2024-01-29  29

爬虫小知识:爬取网站流程

确定网站哪个url是数据的来源。简要分析一下网站结构,查看数据一般放在哪里。查看是否有分页,解决分页的问题。发送请求,查看response.text里面是否有我们想要的数据内容。如果有数据,就用相应的提取数据的方法提取数据保存。

注意:1.刚开做爬虫项目,先不要用类去做,只需要关注数据的来源等问题的解决,不要关注类结构的设计。

文章目录

一、lxml模块1. lxml简介与安装2. lxml初步使用1. 导包2.xml转成element对象的方法(1)将字符串形式的xml内容解析成可以调用xpath语法的element对象的方法 3. 将一个html文件转化成element对象的方法4. 如何将element对象转化成字符串的方法 二、在python中如何使用XPATH1. 使用xpath语法筛选元素2. 筛选元素各种方法实例 三、element对象调用xpath方法筛选数据案例1. 案例:获取扇贝单词中的python必背词汇2. 案例:获取网易云音乐歌手信息3. 案例:获取酷狗音乐的歌手信息

一、lxml模块


1. lxml简介与安装


简介: lxml 是一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。

lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。

lxml python 官方文档: http://lxml.de/index.html

需要安装C语言库,可使用 pip 安装:pip install lxml (或通过wheel方式安装)

在这里我们用的Anaconda编译器,不需再安装


2. lxml初步使用


我们利用它来解析 HTML 代码


1. 导包


from lxml import etree

2.xml转成element对象的方法


(1)将字符串形式的xml内容解析成可以调用xpath语法的element对象的方法


格式: from lxml import etree html_element = etree.HTML(html_str) 方法: etree.HTML(xml字符串) 解析的element对象是html格式 etree.HTML(xml字符串)

案例: 返回值-------------->element对象

etree.HTML()可以帮我们补齐标签

小结:lxml可以自动修正html代码


3. 将一个html文件转化成element对象的方法


格式: from lxml import etree html = etree.parse('demo.html') # print(html) 方法: etree.parse(‘html文件’) 解析的element是xml格式,要注意标签头尾对应 etree.parse('html文件') 案例: 注意:从文件中读取数据,要求文件内容符合xml格式,如果标签缺失,则不能正常读取。

4. 如何将element对象转化成字符串的方法


格式:

#初始化一个xpath解析对象 html = etree.HTML(text) result = etree.tostring(html,encoding='utf-8') ##解析对象输出代码 是一个bytes类型 print(type(html)) #<class 'lxml.etree._Element'> print(type(result)) #<class 'bytes'> print(result.decode('utf-8')) # 输出字符串 ## pretty_print=True 格式化输出,美观数据 print(etree.tostring(element对象,pretty_print=True,encoding='utf-8').decode('utf-8'))

不解码是bytes类型:

案例:.decode(‘utf-8’)写在括号里面(不会加上html标签)

案例:.decode(‘utf-8’)写在括号外面(加html标签)


二、在python中如何使用XPATH


通过lxml模块,可以使用xpath语法来筛选元素(借用lxml模块返回的element对象)


1. 使用xpath语法筛选元素


返回值element还可以继续调用xpath方法来继续筛选元素;

element对象.xpath() 方法返回的是一个列表;

格式:

html_element.xpath() 案例:加xpath来筛选元素

xpath获取标签返回的是列表,列表里面是element对象

2. 筛选元素各种方法实例


要特别注意的是这一串代码!!!!!!!!!

result = point.xpath('//bookstore/book[position()<3]/title/text()') ## 必须指定哪个节点,用/或者不写都取不出数据,因为他不能定位到bookstore,XML并不是我们看到的这样的结构前面还有标签 代码 from lxml import etree html = ''' <bookstore> <title>新华书店</title> <book href="http://www.langlang2017.com/"> <title lang="eng">Harry Potter</title> <price>29.99</price> </book> <book> <title lang="zh">Learning XML</title> <price>39.95</price> </book> <book href="www.baidu.com"> <title>python 大全</title> <price>99.95</price> </book> </bookstore> ''' point = etree.HTML(html) # (1)获取文档中的所有book节点的第一个book节点 result = point.xpath('//book[1]') # (2)获取第一个book节点 first_book = result[0] ## 上题获取的是列表,这题获取的是element对象 print(first_book) # (3)获取first_book当前节点下的href result = first_book.xpath('./@href') ## 当前节点或者父节点下的标签用/和//效果一样,定位到bookstore下了,取得都是book,除非下面还有后代节点才有区别 # (4)获取当前节点的父节点下的book节点的href属性 result = first_book.xpath('../book/@href') # (5)获取价格小于40的书的书名 result = point.xpath('//book[price<40]/title/text()') # (6)获取价格等于99.95的书的书名 result = point.xpath('//book[price=99.95]/title/text()') # (7)获取book标签下面的title和price标签 result = point.xpath('//book/title|//book/price') # (8)获取属性href含有baidu字符串的book标签,获取此标签的书名 result = point.xpath('//book[contains(@href,"baidu")]/title/text()') # (9)获取去前面两个属于bookstore标签的字标签的book标签,获取此标签的书名 result = point.xpath('//bookstore/book[position()<3]/title/text()') ## 必须指定哪个节点,用/或者不写都取不出数据,因为他不能定位到bookstore,XML并不是我们看到的这样的结构前面还有标签 # (10)匹配任何属性节点 # result = point.xpath('//*[@*]') # (11)获取所有带有属性title标签,获取此标签的内容 # result = point.xpath('//title[@*]/text()') print(result)

三、element对象调用xpath方法筛选数据案例


1. 案例:获取扇贝单词中的python必背词汇


爬取内容

注意:分页的实现

代码

普通函数写法

import json import requests from lxml import etree ## 确定url base_url = 'https://www.shanbay.com/wordlist/110521/232414/?page=%s' ## 封装headers headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } def get_text(value): if value: return value[0] else: return '' words = [] ## 分页 for i in range(1,4): ## 发送请求 response = requests.get(base_url %i,headers=headers) # print(response.text) ## 如果有数据,就用相应的方法提取数据 ## 获取element对象来使用xpath方法提取数据 html = etree.HTML(response.text) tr_list = html.xpath('//tbody/tr') ## 从获取的列表中的tr获取数据 for tr in tr_list: en = get_text(tr.xpath('.//td[class="span2"]/strong/text()')) # en = tr.xpath('.//td[contains(@class,"span2")]/strong/text()') zh = get_text(tr.xpath('.//td[@class="span10"]/text()')) ### {'recipe':n. 挂起,暂停} # items() item = {} if en and zh: item[en] = zh words.append(item) with open('word.json','w',encoding='utf-8') as fp: json.dump(words,fp) 封装成面向对象 import json import requests from lxml import etree class Shanbei(object): def __init__(self,url,headers): self.words = [] self.headers = headers self.url = url self.parse() def get_text(self,value): if value: return value[0] else: return '' def parse(self): ### 分页 for i in range(1, 4): ## 发送请求 response = requests.get(self.url % i, self.headers) # print(response.text) ## 如果有数据,就用相应的方法提取数据 ## 获取element对象来使用xpath方法提取数据 html = etree.HTML(response.text) tr_list = html.xpath('//tbody/tr') ## 从获取的列表中的tr获取数据 for tr in tr_list: en = self.get_text(tr.xpath('.//td[class="span2"]/strong/text()')) # en = self.get_text(tr.xpath('.//td[contains(@class,"span2")]/strong/text()')) print(en) zh = self.get_text(tr.xpath('.//td[@class="span10"]/text()')) ### {'recipe':n. 挂起,暂停} # items() item = {} if en and zh: item[en] = zh self.words.append(item) if __name__ == '__main__': base_url = 'https://www.shanbay.com/wordlist/110521/232414/?page=%s' headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } sb = Shanbei(base_url,headers) with open('word.json','w',encoding='utf-8') as fp: json.dump(sb.words,fp) with open('word.json','r') as fp: list = json.load(fp) for one in list: print(one) 运行结果

2. 案例:获取网易云音乐歌手信息


页面内容

注意:推荐页上面没有A,B,C等而且数据重复,热门也不符合我们对于大部分数据的处理方法,我们可以去掉

代码

普通函数写法

import json import requests from lxml import etree base_url = 'https://music.163.com/discover/artist' ### 通过url获取页面的xpath对象以便提取数据 def get_xpath(url): headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } response = requests.get(url,headers=headers) return etree.HTML(response.text) singer_infos = [] ## 获取简介 def parse_detail(url,item): html = get_xpath(url) desc_list = html.xpath('//div[@class="n-artdesc"]/p/text()') desc = ''.join(desc_list) item['desc'] = desc singer_infos.append(item) ### 第三步:通过传过来的A,B,Curl来提取对应页面的数据 def parse_type(url): ## 获取该页面的xpath html = get_xpath(url) singer_names = html.xpath('//ul[@id="m-artist-box"]/li/p/a/text()') singer_urls = html.xpath('//ul[@id="m-artist-box"]/li/p/a[1]/@href|//ul[@id="m-artist-box"]/li/a/@href') for i,name in enumerate(singer_names): item = {} item["歌手"] = name item['歌手连接'] = 'https://music.163.com'+singer_urls[i].strip() item['歌手详情页'] = 'https://music.163.com'+singer_urls[i].replace(r'?id','/desc?id').strip() # print(item) url = 'https://music.163.com'+singer_urls[i].replace(r'?id','/desc?id').strip() parse_detail(url,item) ### 第二步:解析每一个地区歌手页面 def parse_area(url): html = get_xpath(url) ## 获取A,B,C分类的url # type_name_url = html.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href') type_name_url = html.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href') # print(type_name_url) ## 找到A,B,Curl for type in type_name_url: ## 拼接url url = 'https://music.163.com'+type parse_type(url) ### 第一步:首页的解析,获取地区列表的url def parse(): html = get_xpath(base_url) # print(html) area_singer_urls = html.xpath('//ul[@class="nav f-cb"]/li/a/@href') #################### 去掉热门和推荐url############### area_singer_urls = [x for x in area_singer_urls if 'id' in x] ################ 拿到剩下的url ################# for url in area_singer_urls: new_url = 'https://music.163.com'+url parse_area(new_url) if __name__ == '__main__': parse() with open('singer.json','w',encoding='utf-8') as fp: json.dump(singer_infos,fp) with open('singer.json','r') as fp: print(json.load(fp)) 封装成面向对象 import json import requests from lxml import etree class Music_163: def __init__(self,url): self.url = url self.singer_infos = [] ### 通过url获取页面的xpath对象以便提取数据 def get_xpath(self,url): headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36' } response = requests.get(url, headers=headers) return etree.HTML(response.text) ## 获取简介 def parse_detail(self,url, item): html = self.get_xpath(url) desc_list = html.xpath('//div[@class="n-artdesc"]/p/text()') desc = ''.join(desc_list) print(desc) item['desc'] = desc self.singer_infos.append(item) ### 第三步:通过传过来的A,B,Curl来提取对应页面的数据 def parse_type(self,url): ## 获取该页面的xpath html = self.get_xpath(url) singer_names = html.xpath('//ul[@id="m-artist-box"]/li/p/a/text()') singer_urls = html.xpath('//ul[@id="m-artist-box"]/li/p/a[1]/@href|//ul[@id="m-artist-box"]/li/a/@href') for i, name in enumerate(singer_names): item = {} item["歌手"] = name item['歌手连接'] = 'https://music.163.com' + singer_urls[i].strip() item['歌手详情页'] = 'https://music.163.com' + singer_urls[i].replace(r'?id', '/desc?id').strip() # print(item) url = 'https://music.163.com' + singer_urls[i].replace(r'?id', '/desc?id').strip() self.parse_detail(url, item) ### 第二步:解析每一个地区歌手页面 def parse_area(self,url): html = self.get_xpath(url) ## 获取A,B,C分类的url type_name_url = html.xpath('//ul[@id="initial-selector"]/li[position()>1]/a/@href') # print(type_name_url) ## 找到A,B,Curl for type in type_name_url: ## 拼接url url = 'https://music.163.com' + type self.parse_type(url) ### 第一步:首页的解析,获取地区列表的url def parse(self): html = self.get_xpath(self.url) # print(html) area_singer_urls = html.xpath('//ul[@class="nav f-cb"]/li/a/@href') #################### 去掉热门和推荐url############### area_singer_urls = [x for x in area_singer_urls if 'id' in x] ################ 拿到剩下的url ################# for url in area_singer_urls: new_url = 'https://music.163.com' + url self.parse_area(new_url) if __name__ == '__main__': base_url = 'https://music.163.com/discover/artist' m = Music_163(base_url) m.parse() with open('singer.json', 'w', encoding='utf-8') as fp: json.dump(m.singer_infos, fp) with open('singer.json','r') as fp: print(json.load(fp)) 运行结果

3. 案例:获取酷狗音乐的歌手信息


页面内容 注意:分页的实现;热门和全部歌手重复,可以去掉代码普通函数方法 # 获取酷狗音乐的歌手信息 import json import requests from lxml import etree base_url = 'https://www.kugou.com/yy/html/singer.html' def get_html(url): headers = {'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'} response = requests.get(url,headers = headers) return response.text info_list = [] def detail_parse(url,item): html = etree.HTML(get_html(url)) ## 获取歌手信息 info = html.xpath('.//div[@class="intro"]/p/text()') # print(info) item["歌手信息"] = info info_list.append(item) ## 第三步:通过A的url找到下面对应的歌手信息 def singer_parse(url): html = etree.HTML(get_html(url)) ## 上面一部分有图的歌手 head_li = html.xpath('.//ul[@id="list_head"]/li') ## 爬取没有图像的歌手 list1_li = html.xpath('.//ul[@class="list1"]/li') list = head_li + list1_li # print(head_li) for li in list: item = {} if li.xpath('..//strong'): singer_name = li.xpath('.//strong/a/text()')[0] singer_url = li.xpath('.//strong/a/@href')[0] item["歌手"] = singer_name item["歌手链接"] = singer_url else: singer_name = li.xpath('.//a/text()')[0] singer_url = li.xpath('.//a/@href')[0] item["歌手"] = singer_name item["歌手链接"] = singer_url print(item) detail_parse(singer_url, item) # 第二步:通过url来找到页面中每个地区A,B,C的url def area_url(url): url = url.replace("/index/1",'/index/%s') print(url) for i in range(1,7): html = etree.HTML(get_html(url %i)) a_list = html.xpath('.//div[@class="num"]/a[position()>1]/@href') print(a_list) for url in a_list: singer_parse(url) ## 第一步刷选去除全部歌手,选取 地区歌手url def parse(): html = etree.HTML(get_html(base_url)) li_list = html.xpath('.//ul[contains(@class,"sng")]/li[position()>1]') # print(len(li_list)) for li in li_list: url = li.xpath('./a/@href')[0] # print(url) area_url(url) if __name__ == '__main__': parse() with open("kg.json",'w',encoding='utf-8') as fp: json.dump(info_list,fp) 面向对象封装 # 获取酷狗音乐的歌手信息 import json import requests from lxml import etree class Kg: def __init__(self,url): self.url = url self.info_list = [] def get_html(self,url): headers = {'user-agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'} response = requests.get(url,headers = headers) return response.text def detail_parse(self,url,item): html = etree.HTML(self.get_html(url)) ## 获取歌手信息 info = html.xpath('.//div[@class="intro"]/p/text()') print(info) item["歌手信息"] = info self.info_list.append(item) ## 第三步:通过A的url找到下面对应的歌手信息 def singer_parse(self,url): html = etree.HTML(self.get_html(url)) ## 上面一部分有图的歌手 head_li = html.xpath('.//ul[@id="list_head"]/li') ## 爬取没有图像的歌手 list1_li = html.xpath('.//ul[@class="list1"]/li') list = head_li + list1_li # print(head_li) for li in list: item = {} if li.xpath('..//strong'): singer_name = li.xpath('.//strong/a/text()')[0] singer_url = li.xpath('.//strong/a/@href')[0] item["歌手"] = singer_name item["歌手链接"] = singer_url else: singer_name = li.xpath('.//a/text()')[0] singer_url = li.xpath('.//a/@href')[0] item["歌手"] = singer_name item["歌手链接"] = singer_url # print(item) self.detail_parse(singer_url, item) # 第二步:通过url来找到页面中每个地区A,B,C的url def area_url(self,url): url = url.replace("/index/1",'/index/%s') print(url) for i in range(1,7): html = etree.HTML(self.get_html(url %i)) a_list = html.xpath('.//div[@class="num"]/a[position()>1]/@href') print(a_list) for url in a_list: self.singer_parse(url) ## 第一步刷选去除全部歌手,选取 地区歌手url def parse(self): html = etree.HTML(self.get_html(self.url)) li_list = html.xpath('.//ul[contains(@class,"sng")]/li[position()>1]') # print(len(li_list)) for li in li_list: url = li.xpath('./a/@href')[0] # print(url) self.area_url(url) if __name__ == '__main__': base_url = 'https://www.kugou.com/yy/html/singer.html' k = Kg(base_url) k.parse() # with open("kg.json",'w',encoding='utf-8') as fp: # json.dump(k.info_list,fp) 运行结果
最新回复(0)