第四章 解析库基本使用
4.1 xpath
xpath(XML Path)即xml路径语言,在xml文档中查找信息,也可以用于HTML文档的搜索。
使用之前确保安装lxml库。
4.1.1 xpath使用方法
from lxml import etree
tree = etree.parse('name.html')
tree = etree.HTML('name.html')
r = tree.xpath('xpath路径')
print(r)
xpath的几个常用规则
表达式 | 描述 |
---|
nodename | 选取此节点的所有子节点 | / | 从当前节点选取直接子节点 | // | 从当前节点选取子孙节点 | . | 选取当前节点 | … | 选取当前节点的父节点 | @ | 选取属性 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<div>
<ul>
<li id="l1" class="n">No_Matter</li>
<li id="l2">小猫咪</li>
<li>蓬佩奥</li>
<a href="1026" id="123" class="a1">饭醉团伙</a>
<span>666</span>
</ul>
</div>
<a href="1025" title="a2">啊啊啊</a>
</body>
</html>
/找儿子,//找孙子
一般用//开头的xpath规则来选取所有符合要求的节点,*号代表匹配所有的节点。返回的结果是列表类型,每个元素都是一个element对象
通过/或//查找元素的子节点或子孙节点
用xpath()中的text()方法获取节点的文本内容
from lxml import etree
html = etree.parse('pr.html')
r = html.xpath('//li[@id="l2"]/text()')
print(r)
-> ['小猫咪']
r = html.xpath('//li[@id="l1"]/@class')
-> ['n']
r = html.xpath('//li[contains(@id,"l")]/text()')
-> ['No_Matter', '小猫咪']
运算符|计算两个节点集,它不支持属性
这里的排序与代码中不一样,序号是以1开始,不是以0开始。
r = html.xpath('//li[1]/text()')
-> ['No_Matter']
r = html.xpath('//li[last()]/text()')
-> ['蓬佩奥']
r = html.xpath('//li[position()<3]/text()')
-> ['No_Matter', '小猫咪']
r = html.xpath('//li[last()-1]/text()')
-> ['小猫咪']
除了以上的xpath还提供了很多节点轴选择方法,比如子元素,兄弟元素,父元素,祖先元素等。功能非常强大,内置函数也多。详细见xpath教程XPath 教程 (w3school.com.cn)
栗子01-获取’百度一下’这个按钮
import urllib.request
from lxml import etree
url = 'https://www.baidu.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'
}
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
tree = etree.HTML(content)
result = tree.xpath('//input[@id="su"]/@value')
print(result)
['百度一下']
栗子02-婚纱图片下载
import urllib.request
from lxml import etree
def create_request(pages):
"""
请求对象定制
:param:page
:return:
"""
if page == 1:
url = 'https://sc.chinaz.com/tupian/hunsha.html'
else:
url = 'https://sc.chinaz.com/tupian/hunsha_'+str(pages)+'.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1'
}
request = urllib.request.Request(url=url, headers=headers)
return request
def get_response(request):
"""
获取响应
:param:request
:return:
"""
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
return content
def my_download(content, pages):
"""
下载图片,
:param:content
:return:
"""
tree = etree.HTML(content)
name_lst = tree.xpath("//div[@id='container']//a/img/@alt")
src_lst = tree.xpath("//div[@id='container']//a/img/@src2")
for i in range(5):
name = name_lst[i]
src = src_lst[i]
src_url = 'https:'+src
urllib.request.urlretrieve(url=url, filename=name+'jpg')
if __name__ == '__main__':
start_page = int(input('请输入起始页码:'))
end_page = int(input('请输入结束页码:'))
for page in range(start_page, end_page+1):
req = create_request(page)
result = get_response(req)
my_download(result, page)
4.3 Beautiful Soup
简称bs4,和lxml一样,是一个html的解析器,主要功能也是解析和提取数据。借助网页的结构和属性等特性来解析网页,效率没有lxml高,但是接口设计更加人性化。
安装:
pip install bs4
导入:
from bs4 import BeatifulSoup
创建对象:
- 服务器响应的文件生成对象:
soup = BeatifulSoup(response.read().decode(), 'lxml')
- 本地文件生成对象:
soup = BeatifulSoup(open('name.html'), 'lxml')
默认打开的编码格式是gbk,需要指定打开编码格式
基本用法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<div>
<ul>
<li id="l1">No_Matter</li>
<li id="l2">小猫咪</li>
<li>蓬佩奥</li>
<a href="1026" id="123" class="a1">饭醉团伙</a>
<span>666</span>
</ul>
</div>
<a href="1025" title="a2">啊啊啊</a>
</body>
</html>
from bs4 import BeautifulSoup
soup = BeautifulSoup(open('pr.html', encoding='utf-8'), 'lxml')
(1)根据标签名查找节点,查找的结果是第一个a节点的内容,后面的a节点没有选到
print(soup.a )
-> <a class="a1" href="1026" id="123">饭醉团伙</a>
(2)输出html的title节点的文本内容
print(soup.title.name )
-> Title
(3)标签的属性和属性值
print(soup.a.attrs)
-> {'href': '1026', 'id': '123', 'class': ['01']}
或者在节点后面加入['属性名']
print(soup.a['href'])
-> 1026
(4)bs4的一些常用函数
1)find() 返回的是第一个符合条件的数据
print(soup.find('a'))
-> <a class="a1" href="1026" id="123">饭醉团伙</a> 结果与上面一样
2) find_all() 返回的是一个列表,里面包含了所有待找的标签
print(soup.find_all('a'))
-> [<a class="a1" href="1026" id="123">饭醉团伙</a>, <a href="1025" title="a2">啊啊啊</a>]
limit属性作用:查找前几个数据
print(soup.find_all('li', limit=2))
-> [<li id="l1">No_Matter</li>, <li id="l2">小猫咪</li>]
3) select() 根据选择器得到节点对象
返回的是一个列表,并且包含多个数据
print(soup.select('a'))
-> [<a class="a1" href="1026" id="123">饭醉团伙</a>, <a href="1025" title="a2">啊啊啊</a>]
找到a标签和li标签所有的对象
print(soup.select('a, li'))
-> [<li id="l1">No_Matter</li>, <li id="l2">小猫咪</li>, <li>蓬佩奥</li>, <a class="a1" href="1026" id="123">饭醉团伙</a>, <a href="1025" title="a2">啊啊啊</a>]
.class
可以通过.代表class,把这种操作叫做类选择器
print(soup.select('.a1'))
-> [<a class="a1" href="1026" id="123">饭醉团伙</a>]
print(soup.select('#l1'))
-> [<li id="l1">No_Matter</li>]
3.2)属性选择器:通过属性选找对应的标签
1.查找到li标签中有id的标签
print(soup.select('li[id]'))
-> [<li id="l1">No_Matter</li>, <li id="l2">小猫咪</li>]
2.查找li标签中id为l2的标签
print(soup.select('li[id="l2"]'))
-> [<li id="l2">小猫咪</li>]
3.3)层级选择器
1.后代选择器
找到div下面的li,用空格表示
print(soup.select('div li'))
-> [<li id="l1">No_Matter</li>, <li id="l2">小猫咪</li>, <li>蓬佩奥</li>]
2.子代选择器
某标签的第一级子标签
print(soup.select('div > ul > li'))
-> [<li id="l1">No_Matter</li>, <li id="l2">小猫咪</li>, <li>蓬佩奥</li>]
1)获取名称:soup.节点名.name
2)获取属性:soup.节点名.attrs/soup.节点名.attrs[‘name’],或者soup.节点名[‘属性名’]
3)获取内容:soup.节点名.string 多个相同节点时只返回第一个节点的内容
每一个返回结果都是bs4.element.Tag类型,可以继续调用节点进行下一步操作。
1)子节点与子孙节点
选取节点元素后可以使用contents属性获取它的直接子节点。返回结果是列表形式,列表中每个元素是直接子节点,元素中包含的节点相当于孙子节点了。从结果看出来contents属性得到的结果是直接字节点的列表。
print(soup.ul.contents)
-> ['\n', <li id="l1">No_Matter</li>, '\n', <li id="l2">小猫咪</li>, '\n', <li>蓬佩奥</li>, '\n', <a class="a1" href="1026" id="123">饭醉团伙</a>, '\n', <span>666</span>, '\n']
2)父节点与祖先节点
比如获取上面a节点的父节点,可以调用parent属性,选择的是第一个a节点的父节点元素,返回的结果是它的父节点和父节点内部的内容。
print(soup.a.parent)
<ul>
<li id="l1">No_Matter</li>
<li id="l2">小猫咪</li>
<li>蓬佩奥</li>
<a class="a1" href="1026" id="123">饭醉团伙</a>
<span>666</span>
</ul>
获取祖先节点可以调用parents属性,返回的结果是生成器类型
3)兄弟节点
也就是同一级别的节点
上面的三个常用的find()、find_all()、select()
上面的select()使用
select()方法同样扛得住,所以综上来看,相比于其他方法,select()方法更值得被推荐。
|