快速入门 Python 爬虫常用解析库(xpathbs4)
Posted Amo Xiang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速入门 Python 爬虫常用解析库(xpathbs4)相关的知识,希望对你有一定的参考价值。
第一章 XPath 解析
在 Python 中可以支持 XPath 提取数据的解析模块有很多,本文主要介绍 lxml 模块,该模块可以解析 html 与 XML,并且支持 XPath 解析方式。由于 lxml 模块 为第三方模块,需要通过 pip install lxml
命令安装该模块。lxml 模块 的底层是通过C语言编写的,所以在解析效率方面是非常优秀的。xpath 语法参考学习网站:https://www.w3school.com.cn/xpath/xpath_nodes.asp。
【示例1】使用 parse() 方法解析本地的 HTML 文件。(一般不怎么常用)
笔者在自己本地新建了一个 demo.html
,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lxml模块读取本地html文件测试</title>
</head>
<body>
hello, lxml!
</body>
</html>
python 代码如下:
from lxml import etree # 导入etree子模块
parser = etree.HTMLParser() # 创建HTMLParser对象
html = etree.parse('demo.html', parser=parser) # 解析demo.html文件
print(type(html)) # <class 'lxml.etree._ElementTree'> 是一个对象
html_txt = etree.tostring(html, encoding="utf-8") # 转换字符串类型,并进行编码
print(html_txt.decode('utf-8')) # 打印解码后的HTML代码
【示例2】解析字符串类型的 HTML 代码。etree 子模块还提供了一个 HTML() 方法,该方法可以实现解析字符串类型的 HTML 代码。示例代码如下:
from lxml import etree # 导入etree子模块
# 定义html字符串
html_str = '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>解析字符串类型的 HTML</title>
</head>
<body>
hello, lxml!
</body>
</html>'''
html = etree.HTML(html_str) # 解析html字符串
html_txt = etree.tostring(html, encoding="utf-8") # 转换字符串类型,并进行编码
print(html_txt.decode('utf-8')) # 打印解码后的HTML代码
【示例3】解析服务器返回的 HTML 代码。在实际开发中,HTML() 方法的使用率是非常高的,因为发送网络请求后,多数情况下都会将返回的响应结果转换为字符串类型,如果返回的结果是 HTML 代码,则需要使用 HTML() 方法来进行解析。示例代码如下:
from lxml import etree # 导入etree子模块
import requests # 导入requests模块
from requests.auth import HTTPBasicAuth # 导入HTTPBasicAuth类
# 定义请求地址
url = 'http://sck.rjkflm.com:666/spider/auth/'
# 实现验证请求功能
ah = HTTPBasicAuth('admin', 'admin') # 创建HTTPBasicAuth对象,参数为用户名与密码
response = requests.get(url=url, auth=ah) # 发送网络请求
if response.status_code == 200: # 如果请求成功
html = etree.HTML(response.text) # 解析html字符串
html_txt = etree.tostring(html, encoding="utf-8") # 转换字符串类型,并进行编码
print(html_txt.decode('utf-8')) # 打印解码后的HTML代码
"""
分析: 运行结果中会出现 表示 Unicode 编码的回车字符。
"""
【示例4】使用 //
获取HTML代码的所有节点 ,示例如下:
html_str = '''
<div class="level_one on">
<ul>
<li> <a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的下载">JDK的下载</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的安装">JDK的安装</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="配置JDK">配置JDK</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
node_all = html.xpath('//*') # 获取所有节点
print('数据类型:', type(node_all)) # 打印数据类型
print('数据长度:', len(node_all)) # 打印数据长度
print('数据内容:', node_all) # 打印数据内容
# 通过推导式打印所有节点名称,通过节点对象.tag获取节点名称
print('节点名称:', [i.tag for i in node_all])
######################################################
html = etree.HTML(html_str) # 解析html字符串
li_all = html.xpath('//li') # 获取所有li节点
print('所有li节点', li_all) # 打印所有li节点
print('获取指定li节点:', li_all[1]) # 打印指定li节点
li_txt = etree.tostring(li_all[1], encoding="utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:', li_txt.decode('utf-8'))
程序运行结果如下:
数据类型: <class 'list'>
数据长度: 16
数据内容: [<Element html at 0x20aec9e8088>, <Element body at 0x20aecabee48>, <Element div at 0x20aecabef08>, <Element ul at 0x20aecabef48>, <Element li at 0x20aecabef88>, <Element a at 0x20aecac5048>, <Element li at 0x20aecac5088>, <Element a at 0x20aecac50c8>, <Element li at 0x20aecac5108>, <Element a at 0x20aecabefc8>, <Element li at 0x20aecac5148>, <Element a at 0x20aecac5188>, <Element li at 0x20aecac51c8>, <Element a at 0x20aecac5208>, <Element li at 0x20aecac5248>, <Element a at 0x20aecac5288>]
节点名称: ['html', 'body', 'div', 'ul', 'li', 'a', 'li', 'a', 'li', 'a', 'li', 'a', 'li', 'a', 'li', 'a']
所有li节点 [<Element li at 0x20aec9fad48>, <Element li at 0x20aecac5c88>, <Element li at 0x20aecac5d08>, <Element li at 0x20aecac5dc8>, <Element li at 0x20aecac5e08>, <Element li at 0x20aecac5e88>]
获取指定li节点: <Element li at 0x20aecac5c88>
获取指定节点HTML代码: <li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
【示例5】使用 / 获取一个节点中的直接子节点,示例如下:
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li>
<a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a>
<a>Java</a>
</li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
a_all = html.xpath('//li/a') # 获取li节点中所有子节点a
print('所有子节点a', a_all) # 打印所有a节点
print('获取指定a节点:', a_all[1]) # 打印指定a节点
a_txt = etree.tostring(a_all[1], encoding="utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:', a_txt.decode('utf-8'))
程序运行结果如下:
所有子节点a [<Element a at 0x20aecac7a48>, <Element a at 0x20aecac7a88>, <Element a at 0x20aecac7ac8>, <Element a at 0x20aecac7b08>]
获取指定a节点: <Element a at 0x20aecac7a88>
获取指定节点HTML代码: <a>Java</a>
【示例6】使用 // 实现获取子孙节点,示例如下:
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li>
<a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a>
<a>Java</a>
</li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
<li>
<a href="javascript:" οnclick="login(0)" title="Java API文档">
<a>a节点中的a节点</a>
</a>
</li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
a_all = html.xpath('//ul//a') # 获取ul节点中所有子孙节点a
print('所有子节点a',a_all) # 打印所有a节点
print('获取指定a节点:',a_all[4]) # 打印指定a节点
a_txt = etree.tostring(a_all[4],encoding = "utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:',a_txt.decode('utf-8'))
程序运行结果如下:
所有子节点a [<Element a at 0x20aec9e8708>, <Element a at 0x20aecac8388>, <Element a at 0x20aecac83c8>, <Element a at 0x20aecac8408>, <Element a at 0x20aecac8448>]
获取指定a节点: <Element a at 0x20aecac8448>
获取指定节点HTML代码: <a>a节点中的a节点</a>
【示例7】使用 … 获取一个节点的父节点,示例如下:
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li><a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a></li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
a_all_parent = html.xpath('//a/..') # 获取所有a节点的父节点
print('所有a的父节点',a_all_parent) # 打印所有a的父节点
print('获取指定a的父节点:',a_all_parent[0]) # 打印指定a的父节点
a_txt = etree.tostring(a_all_parent[0],encoding = "utf-8") # 转换字符串类型,并进行编码
# 打印指定节点的HTML代码
print('获取指定节点HTML代码:\\n',a_txt.decode('utf-8'))
# 除了使用“..”获取一个节点的父节点以外,还可以使用“/parent::*”的方式来获取。
# 例子:获取li的父节点
ul = html.xpath("//li/parent::*")
print(ul)
程序运行结果如下:
所有a的父节点 [<Element li at 0x20aee0a8e48>, <Element li at 0x20aee0a8088>]
获取指定a的父节点: <Element li at 0x20aee0a8e48>
获取指定节点HTML代码:
<li><a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a></li>
[<Element ul at 0x20aee0a8a08>]
【示例8】使用 text() 方法获取HTML代码中的文本,示例如下:
# 定义html字符串
html_str = '''
<div class="level_one on">
<ul>
<li><a href="/index/index/view/id/1.html" title="什么是Java" class="on">什么是Java</a></li>
<li> <a href="javascript:" οnclick="login(0)" title="Java的版本">Java的版本</a> </li>
</ul>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
a_text = html.xpath('//a/text()') # 获取所有a节点中的文本信息
print('所有a节点中文本信息:',a_text)
程序运行结果如下:
所有a节点中文本信息: ['什么是Java', 'Java的版本']
【示例9】使用[@…] 实现节点属性的匹配,示例如下:
# 定义html字符串
html_str = '''
<div class="video_scroll">
<div class="level">什么是Java</div>
<div class="level">Java的版本</div>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
# 获取所有class="level"的div节点中的文本信息 @后面加对应属性名都可
div_one = html.xpath('//div[@class="level"]/text()')
print(div_one) # 打印class="level"的div中文本
程序运行结果如下:
['什么是Java', 'Java的版本']
【示例10】属性多值匹配进行节点内容的筛选,示例如下:
# 定义html字符串
html_str = '''
<div class="video_scroll">
<div class="level one">什么是Java</div>
<div class="level">Java的版本</div>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
# 获取所有class="level one"的div节点中的文本信息
div_one = html.xpath('//div[@class="level one"]/text()')
print(div_one) # 打印class="level one"的div中文本
html = etree.HTML(html_str) # 解析html字符串
# 获取所有class属性值中包含level的div节点中的文本信息
div_all = html.xpath('//div[contains(@class,"level")]/text()')
print(div_all) # 打印所有符合条件的文本信息
程序运行结果如下:
['什么是Java']
['什么是Java', 'Java的版本']
【示例11】一个节点中多个属性的匹配,示例如下:
# 定义html字符串
html_str = '''
<div class="video_scroll">
<div class="level" id="one">什么是Java</div>
<div class="level">Java的版本</div>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
# 获取所有符合class="level与id="one"的div节点中的文本信息
div_all = html.xpath('//div[@class="level" and @id="one"]/text()')
print(div_all) # 打印所有符合条件的文本信息
程序运行结果如下:
['什么是Java']
【示例12】使用 @获取属性所对应的值,示例如下:
# 定义html字符串
html_str = '''
<div class="video_scroll">
<li class="level" id="one">什么是Java</li>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
# 获取li节点中的class属性值
li_class = html.xpath('//div/li/@class')
# 获取li节点中的id属性值
li_id = html.xpath('//div/li/@id')
print('class属性值:',li_class)
print('id属性值:',li_id)
程序运行结果如下:
class属性值: ['level']
id属性值: ['one']
【示例13】使用索引按序获取属性对应的值,示例如下:
# 定义html字符串
html_str = '''
<div class="video_scroll">
<li> <a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的下载">JDK的下载</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的安装">JDK的安装</a> </li>
<li> <a href="javascript:" οnclick="login(0)" title="配置JDK">配置JDK</a> </li>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
# 获取所有li/a节点中title属性值
li_all = html.xpath('//div/li/a/@title')
print('所有属性值:',li_all)
# 获取第1个li/a节点中title属性值
li_first = html.xpath('//div/li[1]/a/@title')
print('第一个属性值:',li_first) # 最后的结果哪怕只有一个,也是以列表的形式进行返回
# 获取第4个li/a节点中title属性值
li_four = html.xpath('//div/li[4]/a/@title')
print('第四个属性值:',li_four)
html = etree.HTML(html_str) # 解析html字符串
# 获取最后一个li/a节点中title属性值
li_last = html.xpath('//div/li[last()]/a/@title')
print('最后一个属性值:',li_last)
# 获取第1个li/a节点中title属性值
li = html.xpath('//div/li[position()=1]/a/@title')
print('第一个位置的属性值:',li)
# 获取倒数第二个li/a节点中title属性值
li = html.xpath('//div/li[last()-1]/a/@title')
print('倒数第二个位置的属性值:',li)
# 获取位置大于1的li/a节点中title属性值
li = html.xpath('//div/li[position()>1]/a/@title')
print('位置大于1的属性值:',li)
程序运行结果如下:
所有属性值: ['Java API文档', 'JDK的下载', 'JDK的安装', '配置JDK']
第一个属性值: ['Java API文档']
第四个属性值: ['配置JDK']
最后一个属性值: ['配置JDK']
第一个位置的属性值: ['Java API文档']
倒数第二个位置的属性值: ['JDK的安装']
位置大于1的属性值: ['JDK的下载', 'JDK的安装', '配置JDK']
【示例14】使用节点轴获取节点内容,示例如下:
# 定义html字符串
html_str = '''
<div class="video_scroll">
<li><a href="javascript:" οnclick="login(0)" title="Java API文档">Java API文档</a></li>
<li><a href="javascript:" οnclick="login(0)" title="JDK的下载">JDK的下载</a></li>
<li> <a href="javascript:" οnclick="login(0)" title="JDK的安装">JDK的安装</a> </li>
</div>
'''
html = etree.HTML(html_str) # 解析html字符串
# 获取li[2]所有祖先节点
ancestors = html.xpath('//li[2]/ancestor::*')
print('li[2]所有祖先节点名称:',[i.tag for i in ancestors])
# 获取li[2]祖先节点位置为body
body = html.xpath('//li[2]/ancestor::body')
print('li[2]指定祖先节点名称:',[i.tag for i in body])
# 获取li[2]属性为class="video_scroll"的祖先节点
class_div = html.xpath('//li[2]/ancestor::*[@class="video_scroll"]')
print('li[2]class="video_scroll"的祖先节点名称:',[i.tag for i in class_div])
# 获取li[2]/a所有属性值
attributes = html.xpath('//li[2]/a/attribute::*')
print('li[2]/a的所有属性值:',attributes)
# 获取div所有子节点
div_child = html.xpath('//div/child::*')
print('div的所有子节点名称:',[i.tag for i in div_child])
# 获取body所有子孙节点
body_descendant = html.xpath('//body/descendant::*')
print('body的所有子孙节点名称:',[i.tag for i in body_descendant])
# 获取li[1]节点后的所有节点
li_following = html.xpath('//li[1]/following::*')
print('li[1]之后的所有节点名称:',[i.tag for i in li_following])
# 获取li[1]节点后的所有同级节点
li_sibling = html.xpath('//li[1]/following-sibling::*')
print('li[1]之后的所有同级节点名称:',[i.tag for i in li_sibling])
# 获取li[3]节点前的所有节点
li_preceding = html.xpath('//li[3]/preceding::*')
print('li[3]之前的所有节点名称:',[i.tag for i in li_preceding])
程序运行结果如下:
li[2]所有祖先节点名称: ['html', 'body', 'div']
li[2]指定祖先节点名称: ['body']
li[2]class="video_scroll"的祖先节点名称: ['div']
li[2]/a的所有属性值: ['javascript:', 'login(0)', 'JDK的下载']
div的所有子节点名称: ['li', 'li', 'li']
body的所有子孙节点名称: ['div', 'li', 'a', 'li', 'a', 'li', 'a']
li[1]之后的所有节点名称: ['li', 'a', 'li', 'a']
li[1]之后的所有同级节点名称: ['li', 'li']
li[3]之前的所有节点名称: ['li', 'a', 'li', 'a']
总结:
第二章 Beautiful Soup 模块
模块介绍请看:https://blog.csdn.net/xw1680/article/details/105845918
【示例1】使用 Beautiful Soup解析HTML代码。示例如下:
from bs4 import BeautifulSoup # 导入BeautifulSoup库
# 创建模拟HTML代码的字符串
html_doc = """
<html>
<head>
<title>第一个 HTML 页面</title>
</head>
<body>
<p>body 元素的内容会显示在浏览器中。</p>
<p>title 元素的内容会显示在浏览器的标题栏中。</p>
</body>
</html>
"""
# 创建一个BeautifulSoup对象,获取页面正文
soup = BeautifulSoup(html_doc, features="lxml")
print(soup) # 打印解析的HTML代码
print(type(soup)) # 打印数据类型
# 如果将html格式字符串的代码保存在 demo.html 文件中,可以通过打开HTML文件的方式进行代码的解析,
# 并且可以通过prettify()方法进行代码的格式化处理,代码如下:
# soup = BeautifulSoup(open("demo.html", encoding="utf8"), features="lxml")
# print(soup.prettify())
程序运行结果如下:
<html>
<head>
<title>第一个 HTML 页面</title>
</head>
<body>
<p>body 元素的内容会显示在浏览器中。</p>
<p>title 元素的内容会显示在浏览器的标题栏中。</p>
</body>
</html>
<class 'bs4.BeautifulSoup'>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>
lxml模块读取本地html文件测试
</title>
</head>
<body>
hello, lxml!
</body>
</html>
【示例2】使用 Beautiful Soup 模块获取节点对应的代码。示例如下:
# 创建模拟HTML代码的字符串
html_doc = """
<html>
<head>
<title>第一个 HTML 页面</title>
</head>
<body>
<p>body 元素的内容会显示在浏览器中。</p>
<p>title 元素的内容会显示在浏览器的标题栏中。</p>
</body>
</html>
"""
# 创建一个BeautifulSoup对象,获取页面正文
soup = BeautifulSoup(html_doc, features="lxml")
print('head节点内容为:\\n',soup.head) # 打印head节点
print('body节点内容为:\\n',soup.body) # 打印body节点
print('title节点内容为:\\n',soup.title) # 打印title节点
print('p节点内容为:\\n',soup.p) # 打印p节点
"""
在打印p节点对应的代码时,可以发现只打印了第一个p节点内容,这说明当有多个节点时,该选择方式只会获取第一个节点中的内容
其他后面的节点将被忽略。
"""
#获取节点名称
print(soup.head. name)
print( soup.body . name)
print(soup.title.name)
print(soup.p.name)
程序运行结果如下:
head节点内容为:
<head>
<title>第一个 HTML 页面</title>
</head>
body节点内容为:
<body>
<p>body 元素的内容会显示在浏览器中。</p>
<p>title 元素的内容会显示在浏览器的标题栏中。</p>
</body>
title节点内容为:
<title>第一个 HTML 页面</title>
p节点内容为:
<p>body 元素的内容会显示在浏览器中。</p>
head
body
title
p
【示例3】获取节点属性。示例如下:
# 创建模拟HTML代码的字符串
html_doc = """
<html>
<head>
<title>横排响应式登录</title>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
<meta name="viewport" content="width=device-width"/>
<link href="font/css/bootstrap.min.css" type="text/css" rel="stylesheet">
<link href="css/style.css" type="text/css" rel="stylesheet">
</head>
<body>
<h3>登录</h3>
<div class="glyphicon glyphicon-envelope"><input type="text" placeholder="请输入邮箱"></div>
<div class="glyphicon glyphicon-lock"><input type="password" placeholder="请输入密码"></div>
</body>
</html>
"""
# 创建一个BeautifulSoup对象,获取页面正文
soup = BeautifulSoup(html_doc, features="lxml")
print('meta节点中属性如下:\\n',soup.meta.attrs)
print('link节点中属性如下:\\n',soup.link.attrs)
print('div节点中属性如下:\\n',soup.div.attrs)
print('-------------------------------分割线-------------------------------')
# 在attrs后面添加括号并在括号内添加属性名称即可获取指定属性对应的值。
print ( 'meta节点中http-equiv属性对应的值为: ',soup.meta.attrs[ 'http-equiv'])
print( 'link节点中href属性对应的值为: ',soup.link.attrs [ 'href']Python3爬虫解析库之XPath详解