python 爬虫(七)lxml模块 + lxml数据提取(字符串的xml/html文件--转换--element对象--转换--字符串)调用XPath方法筛选数据 + 案例(扇贝 酷狗 网易云音乐)
element
A Vue.js 2.0 UI Toolkit for Web
项目地址:https://gitcode.com/gh_mirrors/eleme/element
免费下载资源
·
爬虫小知识:爬取网站流程
- 确定网站哪个url是数据的来源。
- 简要分析一下网站结构,查看数据一般放在哪里。
- 查看是否有分页,解决分页的问题。
- 发送请求,查看response.text里面是否有我们想要的数据内容。
- 如果有数据,就用相应的提取数据的方法提取数据保存。
注意:1.刚开做爬虫项目,先不要用类去做,只需要关注数据的来源等问题的解决,不要关注类结构的设计。
文章目录
一、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)
- 运行结果
GitHub 加速计划 / eleme / element
54.06 K
14.63 K
下载
A Vue.js 2.0 UI Toolkit for Web
最近提交(Master分支:3 个月前 )
c345bb45
7 个月前
a07f3a59
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update transition.md
* Update table.md
* Update table.md
* Update transition.md
* Update popover.md 7 个月前
更多推荐
已为社区贡献1条内容
所有评论(0)