本文重点
- xpath helpler
- xpath:语言,查取HTML/XML文档中元素。
- lxml:解析器。
- csv文件的读写
参考文档
辅助工具
XPath Helper
此篇文档讲解了如何安装和使用。XPath Helper
在浏览器中的使用流程,右键->检查->点击一个
标签,右键copy->copy xpath,将复制的xpath复制到XPath Helper命令行【快捷键CTRL+SHIFT+X】内。
xpath
此处仅明确几个概念,简单会用即可,详细的参考文档可以看看XPath 教程
概念
节点
每个xml的标签都称为节点
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
<bookstore> (文档节点)
<author>J K. Rowling</author> (元素节点)
lang="en" (属性节点)
选取节点
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。
路径表达式
常用路径表达式
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
带谓语的路径表达式
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()❤️] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=‘eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
lxml
简介
lxml是一个HTML/XML的解析器,主要功能用于解析HTML/XML数据。
lxml中的etree将html字符串转化为element对象,再通过xpath()方法来选取节点。
安装
pip install lxml
官方文档
https://lxml.de/index.html
lxml的简单应用
from lxml import etree
"""
etree的作用
第⼀个是将html字符串转化为element对象
第⼆个是element对象可以转换为字符串或者⼆进制类型
"""
wb_data = """
<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>
</ul>
</div>
"""
#html字符串转化为element对象
html_element = etree.HTML(wb_data)
#<Element html at 0x20ea63304c0>
# print(html_element)
#<class 'lxml.etree._Element'>
# print(type(html_element))
# result = etree.tostring(html_element)
# r = result.decode()
# print(type(r),r)
# 获取所有<li>标签下面<a>标签的href属性
links=html_element.xpath('//li/a/@href')
# print(links)
# 获取<a>标签下的文本数据
results=html_element.xpath('//li/a/text()')
# print(results)
# {'href': 'link1.html', 'title': 'first item'}
for link in links:
d={}
d['href']=link
# print(links.index(link))
d['title']=results[links.index(link)]
print(d)
csv的读写
csv(字符分隔值,常用的是逗号分隔值),常用的文本格式,用以存储表格数据,包括数字或者字符。python自带csv模块,专门用于处理csv文件的读取。下面代码是csv的简单应用。
"""
@FileName:csv的读写.py
@Description:
@Author:lmz
@Time:2020/12/14 14:57
"""
"""
需求:读取persons.csv的内容
步骤:
1.导入csv库
# 读取csv文件
2.打开persons.csv
with open('filename.csv','r|w',encoding='utf-8',newline='') as f:
3.读取文件内容方法
- csv.DictReader
- csv.reader
# csv写入文件
- csv.DictWriter(f,titles)
- writer.writeheader(titles)# 写入标题
- writer.writerows(rows) # 写入所有行
- csv.writer()
- writer.writerow(titles)
- writer.writerows(rows)
"""
# csv读取文件
import csv
# 第一种
with open('persons.csv','r',encoding='utf-8') as f:
reader=csv.reader(f)
print(type(reader),reader)
for i in reader: # 按行遍历
print(i[2])# [开始下标是0,第2列]
# 第二种
# 打开文件作为f,解码格式是utf-8
# with open('persons.csv','r',encoding='utf-8') as f:
# reader=csv.DictReader(f)
# # print(type(reader),reader)
# for i in reader:
# print(i['name'])
# csv写入文件
titles = ('name','age','height')
persons = [('张三',20,175),('李四',22,178),('王五',30,180)]
# 第一种
# 问题:每2行中间会有一个空行
# with open('person1.csv','w',encoding='utf-8') as f:
# writer = csv.writer(f) # 写入到文件f
# writer.writerow(titles)# 写入行
# for data in persons:
# writer.writerow(data)
# 换行为'',解决了问题:每2行中间会有一个空行
# with open('person2.csv','w',encoding='utf-8',newline='') as f:
# writer = csv.writer(f)
# writer.writerow(titles)
# writer.writerows(persons)# 写入所有行
# 第二种
person = [
{'name':'张三','age':20,'height':175},
{'name':'李四','age':22,'height':178},
{'name':'王五','age':30,'height':180}
]
with open('person3.csv','w',encoding='utf-8',newline='') as f:
writer = csv.DictWriter(f,titles) # 传入待写入文件和标题
writer.writeheader()# 写入标题
writer.writerows(person) # 写入所有行
豆瓣
此案例是对requests、lxml、csv的一个结合使用。
requests:获取网页源码
lxml:解析数据
csv:保存数据
"""
@FileName:豆瓣.py
@Description:
@Author:lmz
@Time:2020/12/14 17:05
"""
import requests
from lxml import etree
import csv
"""
需求:
爬取每部电影的名字 评分 引言 详情页的url 10页 把这些数据保存到csv文件当中
走一遍流程,查看url进行总结
# https://movie.douban.com/top250?start=0&filter= 第一页
# https://movie.douban.com/top250?start=25&filter= 第二页
# https://movie.douban.com/top250?start=50&filter= 第三页
# https://movie.douban.com/top250?start=75&filter= 第四页
# 规律 (page-1)*25
使用xpath对下面节点进行解析
![image-20201214183309894](https://yanyunpo-oss.oss-cn-shanghai.aliyuncs.com/img/image-20201214183309894.png)
步骤:
1. 获取网页源码
2. 解析数据 电影的名字 评分 引言 详情页的url
3. 保存数据
"""
doubanurl='https://movie.douban.com/top250?start={}&filter='
# 获取网页源码
def getSource(url):
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'
}
response = requests.get(url,headers=headers)
response.encoding='utf-8'
return response.text
# 解析数据 电影的名字 评分 引言 详情页的url
def getEveryItem(source):
html_element = etree.HTML(source)
movieItemlist = html_element.xpath('//div[@class="info"]')
# 保存字典数据
movieList = []
for eachMoive in movieItemlist:
# 保存电影详情数据
movieDict = {}
title = eachMoive.xpath('div[@class="hd"]/a/span[@class="title"]/text()') # 标题
otherTitle = eachMoive.xpath('div[@class="hd"]/a/span[@class="other"]/text()') # 副标题
link = eachMoive.xpath('div[@class="hd"]/a/@href')[0] # 详情页的url
star = eachMoive.xpath('div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()')[0] # 评分
quote = eachMoive.xpath('div[@class="bd"]/p[@class="quote"]/span/text()') # 引言(名句)
# 处理格式问题
if quote:
quote = quote[0]
else:
quote = ''
movieDict['title'] = ''.join(title + otherTitle)
movieDict['url'] = link
movieDict['star'] = star
movieDict['quote'] = quote
movieList.append(movieDict)
# print(movieList)
return movieList
# 保存数据
def writeData(movieList):
with open('douban.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f,fieldnames=['title','star','quote','url'])
writer.writeheader()
for each in movieList:
writer.writerow(each)
# 执行程序
if __name__ == '__main__':
movielist=[]
for i in range(10):
# 获取每一页的url
pageLink=doubanurl.format(i*25)
# print(pageLink)
# 获取每一页的源码
source = getSource(pageLink)
# print(source)
# 解析数据 movieList = movieList + getEveryItem(source)
movielist += getEveryItem(source)
# print(movielist)
# 写入数据 csv
writeData(movielist)