目录
一、XPath解析数据
1、XPath解析数据
2、XML的树形结构
3、使用XPath选取节点
4、课堂案例 - 爬取起点小说网
二、BeautifulSoup解析数据
1、BeautifulSoup
2、BeautifulSoup的使用
3、使用BeautifulSoup爬取淘宝网首页
三、正则表达式
1、正则表达式
2、正则语法
3、特殊序列
4、正则处理函数
5、课堂案例(下载糗事百科小视频)
6、课外案例
四、pyquery解析数据
1、pyquery
2、pyquery的初始化方式
3、pyquery的使用
4、课堂案例(爬取起点小说网)
一、XPath解析数据
1、XPath解析数据
- XPath
- 全称:XML Path Language是一种小型的查询语言
- 是一门在XML文档中查找信息的语言
- XPath的优点
- 可在XML中查找信息
- 支持HTML的查找
- 可通过元素和属性进行导航
- XPath需要依赖lxml库
- 安装方式: pip install lxml
2、XML的树形结构
3、使用XPath选取节点
序号 | 表达式 | 描述 |
1 | nodename | 选取此节点的所有子节点 |
2 | / | 从根节点选择 |
3 | // | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
4 | . | 选取当前节点 |
5 | .. | 选取当前节点的父节点 |
6 | /text() | 选取当前路径下的文本内容 |
7 | /@xxx | 提取当前路径下标签的属性值 |
8 | |可选符 | 可选择若干个路径//p|//div,在当前路径下选取所有符合条件的p标签和div标签 |
序号 | 表达式 | 描述 |
1 | xpath('./body/div[1]') | 选取body下的第一个div节点 |
2 | xpath('./body/div[ last() ]') | 选取body下最后一个div节点 |
3 | xpath('./body/div[ last()-1 ]') | 选取body下倒数第二个div节点 |
4 | xpath('./body/div[ position()<3 ]') | 选取body下前两个div节点 |
5 | xpath('./body/div[ @class ]') | 选取body下带有class属性的div节点 |
6 | xpath('./body/div[ @class="main" ]') | 选取body下class属性为main的div节点 |
7 | xpath('./body/div[ price>35.00 ]') | 选取body下price元素大于35的div节点 |
4、课堂案例 - 爬取起点小说网
- 下载谷歌浏览器XPath插件
- 安装XPath插件
- 使用XPath
import requests
from lxml import etree
url='https://www.qidian.com/rank/yuepiao'
headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}
# 发送请求
resp=requests.get(url,headers)
e=etree.HTML(resp.text) # 类型转换,把str类型转换成class 'lxml.etree._Element'
# print(type(e)) # <class 'lxml.etree._Element'>
names=e.xpath('//div[@class="book-mid-info"]/h4/a/text()') # 获取小说名称
authors=e.xpath('//p[@class="author"]/a[1]/text()') # 获取小说作者
# print(names) # ['大奉打更人', '这个人仙太过正经', '从红月开始', '稳住别浪', 。。。]
# print(authors) # ['卖报小郎君', '言归正传', '黑山老鬼', '跳舞', '我最白', 。。。]
for name,author in zip(names,authors):
print(name, ":", author)
# 大奉打更人 : 卖报小郎君
# 这个人仙太过正经 : 言归正传
# 从红月开始 : 黑山老鬼
# 稳住别浪 : 跳舞
# ... ...
二、BeautifulSoup解析数据
1、BeautifulSoup
- BeautifulSoup
- 是一个可以从HTML或XML文件中提取数据的Python库。其功能简单而强大、容错能力强、文档相对完善,清晰易懂
- 非Python标准模块,需要安装才能使用
- 安装方式
- pip install bs4
- 测试方式
- import bs4
- 解析器
- BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果不安装第三方解析器,则Python会使用默认浏览器
序号 | 解析器 | 使用方法 | 优点 | 缺点 |
1 | 标准库 | BeautifulSoup(html,'html.parser') | 内置标准库,速度适中,文档容错能力强 | Python3.2版本前的文档容错能力差 |
2 | lxml HTML | BeautifulSoup(html, 'lxml') | 速度快,文档容错能力强 | 安装C语言库 |
3 | lxml XML | BeautifulSoup(html, 'xml') | 速度快,唯一支持XML | 安装C语言库 |
4 | html5lib | BeautifulSoup(html, 'html5lib') | 容错能力最强,可生成HTML5 | 运行慢,扩展差 |
from bs4 import BeautifulSoup
html='''
<html>
<head>
<title>今天又是美好的一天</title>
</head>
<body>
<h1 class="info bg" float="left">早起对自己说:我真美!</h1>
<a href="http://www.baidu.com">百度</a>
<h2><!--注释的内容--></h2>
</body>
</html>
'''
# bs=BeautifulSoup(html, 'html.parser')
bs=BeautifulSoup(html, 'lxml')
print(bs.title) # 获取标题 <title>今天又是美好的一天</title>
print(bs.h1.attrs) # 获取h1标签的所有属性 {'class': ['info', 'bg'], 'float': 'left'}
# 获取单个属性
print(bs.h1.get('class')) # ['info', 'bg']
print(bs.h1['class']) # ['info', 'bg']
print(bs.a['href']) # http://www.baidu.com
# 获取文本内容
print(bs.title.text) # 今天又是美好的一天
print(bs.title.string) # 今天又是美好的一天
# 获取内容
print('-----', bs.h2.string) # ----- 注释的内容
print('-----', bs.h2.text) # -----
# string可以获取注释的内容,但是text不能获取注释内容
2、BeautifulSoup的使用
- BeautifulSoup提取数据的常用方法
返回值类型 | 方法 | 功能 | 语法 | 举例 |
Tag | find() | 提取满足要求的首个数据 | bs.find(标签,属性) | bs.find('div', class_='books') |
Tag | find_all() | 提取满足要求的所有数据 | bs.find_all(标签,属性) | bs.find_all('div', class_='books') |
- CSS选择题
功能 | 举例 |
通过ID查找 | bs.select('#abc') |
通过classa查找 | bs.select('.abc') |
通过属性查找 | bs.select(a[' class="abc" ']) |
- Tag对象
功能 | 举例 |
获取标签 | bs.title |
获取所有属性 | bs.title.attrs |
获取单个属性的值 | bs.div.get('class') bs.div['class'] bs.a['href'] |
from bs4 import BeautifulSoup
html='''
<title>今天又是美好的一天</title>
<div class="info" float="left">今天又是美好的一天</div>
<div class="info" float="right" id="gb">
<span>好好学习,天天向上</span>
<a href="http://www.baidu.com">百度</a>
</div>
<span>人生苦短,唯有爱情</span>
'''
bs=BeautifulSoup(html, 'lxml')
print(bs.title, type(bs.title)) # 获取标题及其类型
# <title>今天又是美好的一天</title> <class 'bs4.element.Tag'>
print(bs.find('div',class_='info'), type(bs.find('div',class_='info'))) # 获取第一个满足条件的标签
# <div class="info" float="left">今天又是美好的一天</div> <class 'bs4.element.Tag'>
print(bs.find_all('div', class_='info')) # 得到的是一个标签的列表
# [<div class="info" float="left">今天又是美好的一天</div>, <div class="info" float="right" id="gb">
# <span>好好学习,天天向上</span>
# <a href="http://www.baidu.com">百度</a>
# </div>]
for item in bs.find_all('div',class_='info'):
print(item, type(item))
print(bs.find_all('div', attrs={'float':'right'})) # 得到属性为'float':'right'的div标签
print('---------------CSS选择器---------------------')
print(bs.select("#gb"))
print(bs.select(".info"))
print(bs.select('div>span')) # [<span>好好学习,天天向上</span>]
print(bs.select('div.info>span')) # [<span>好好学习,天天向上</span>]
for item in bs.select('div.info>span'):
print(item.text) # 好好学习,天天向上
3、使用BeautifulSoup爬取淘宝网首页
import requests
from bs4 import BeautifulSoup
url='https://www.taobao.com/'
headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}
resp=requests.get(url,headers)
# print(resp.text) # <html>... ...</html>
bs=BeautifulSoup(resp.text,'lxml')
a_list=bs.find_all('a')
# print(len(a_list)) # 128
for a in a_list:
url=a.get('href')
# print(url)
if url==None:
continue
if url.startswith('http') or url.startswith('https'):
print(url)
三、正则表达式
1、正则表达式
- 是一个特殊的字符序列,它能帮助用户便捷地检查一个字符串是否与某种模式匹配
- Python的正则模块是re,是Python的内置模块,不需要安装,导入即可
2、正则语法
序号 | 元字符 | 说明 |
1 | . | 匹配任意字符(不包括\n) |
2 | ^ | 匹配字符串的开头 |
3 | $ | 匹配字符的末尾 |
4 | * | 匹配前一个元字符0到多次 |
5 | + | 匹配前一个元字符1到多次 |
6 | ? | 匹配前一个元字符0到1次 |
7 | {m} | 匹配前一个字符m次 |
8 | {m,n} | 匹配前一个字符m到n次 |
9 | {m,n}? | 匹配前一个字符m到n次,并且取尽可能少的情况 |
10 | \\ | 对特殊字符进行转义 |
11 | [] | 一个字符的集合,可匹配其中任意一个字符 |
12 | | | 逻辑表达式“或”,比如 a|b 代表可匹配a或者b |
13 | (...) | 被括起来的表达式作为一个元组。findall()在有组的情况下只显示组的内容 |
3、特殊序列
序号 | 元字符 | 说明 |
1 | \A | 只在字符串开头进行匹配 |
2 | \b | 匹配位于开头或者结尾的空字符串 |
3 | \B | 匹配不位于开头或者结尾的空字符串 |
4 | \d | 匹配任意十进制数,相当于[0-9] |
5 | \D | 匹配任意非数字字符,相当于[^0-9] |
6 | \s | 匹配任意空白字符,相当于[\t\n\r\f\v] |
7 | \S | 匹配任意非空白字符,相当于[^\t\n\r\f\v] |
8 | \w | 匹配任意数字、字母、下划线,相当于[a-zA-Z0-9_] |
9 | \W | 匹配任意非数字、字母、下划线,相当于[^a-zA-Z0-9_] |
10 | \Z | 只在字符串结尾进行匹配 |
11 | [\u4e00-\u9fa5] | 中文 |
4、正则处理函数
序号 | 正则处理函数 | 说明 |
1 | re.match(pattern, string, flags=0) | 尝试从字符串的开始位置匹配一个模式,如果匹配成功,就返回一个匹配成功的对象,否则返回None |
2 | re.search(pattern, string, flags=0) | 扫描整个字符串并返回第一次成功匹配的对象,如果匹配失败,就返回None |
3 | re.findall(pattern, string, flags=0) | 获取字符串中所有匹配的字符串,并以列表的形式返回 |
4 | re.sub(pattern, repl, string, count=0,flags=0) | 用于替换字符串中的匹配项,如果没有匹配的项则返回没有匹配的字符串 |
5 | re.compile(pattern[ ,flags ]) | 用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()和search()函数使用 |
import re
s = 'I study Python3.8 every day'
print('--------match方法,从起始位置开始匹配--------')
print(re.match("I", s).group()) # I
print(re.match('\w', s).group()) # I
print(re.match('.', s).group()) # I
print('--------search方法,从任意位置开始匹配,匹配第一个--------')
print(re.search('study', s).group()) # study
print(re.search('s\w', s).group()) # st
print('--------findall方法,从任意位置开始匹配,匹配多个--------')
print(re.findall("y", s)) # 结果为数组 ['y', 'y', 'y', 'y']
print(re.findall("Python", s)) # ['Python']
print(re.findall("P\w+.\d", s)) # ['Python3.8']
print(re.findall("P.+\d", s)) # ['Python3.8']
print('--------sub方法的使用,替换功能--------')
print(re.sub('study', 'like', s)) # 将study替换成like I like Python3.8 every day
print(re.sub('s\w+', 'like', s)) # I like Python3.8 every day
5、课堂案例(下载糗事百科小视频)
- 请求的url
- 最新视频 - 糗事百科
爬取数据时,一定要记得先找F12代码,看看和爬取的数据是否一致,若一致,则可直接提取。
import re
import requests
url='http://www.qiushibaike.com/video/'
headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}
resp=requests.get(url,headers=headers)
# print(resp.text)
# 先随便找一个视频,看是否能提取到。单引号中的单引号用\转义:\'...\'
# info=re.findall('<source src="//qiubai-video.qiushibaike.com/1EXAHVPHKWXFJYR2_org.mp4" type=\'video/mp4\' />', resp.text)
# print(info) # ['<source src="//qiubai-video.qiushibaike.com/1EXAHVPHKWXFJYR2_org.mp4" type=\'video/mp4\' />']
info=re.findall('<source src="(.*)" type=\'video/mp4\' />', resp.text)
# print(info) # 给出所有URL的相对位置的数组
lst=[]
for item in info:
lst.append("https:"+item)
# print(lst)
# 循环
count=0
for item in lst:
count+=1
resp=requests.get(item, headers=headers)
# 转成二进制存储
with open('video/'+str(count)+'.mp4', 'wb') as file:
file.write(resp.content)
print('视频下载完毕')
6、课外案例
用正则表达式提取51job网页中的信息
import requests
import re
from pandas import DataFrame
# 网址
url = "https://search.51job.com/list/000000,000000,0000,32,9,99,Java%25E5%25BC%2580%25E5%258F%2591,2,1.html"
# 获得网页对象
res = requests.get(url)
# 设置编码格式
res.encoding = "gbk"
# 职位名
# 将要提取的用(.*)表示,每条数据不同的地方用.*表示
position_pat = '<a target="_blank" title="(.*)" href=".*" onmousedown=".*">'
position = re.findall(position_pat, res.text)
# 公司名
company_pat = '<span class="t2"><a target="_blank" title="(.*)" href=".*">.*</a></span>'
company = re.findall(company_pat, res.text)
# 工作地点
place_pat = '<div class="el">.*?<span class="t3">(.*?)</span>' # 非贪婪模式
place = re.findall(place_pat, res.text, re.S)
# 薪资
salary_pat = '<div class="el">.*?<span class="t4">(.*?)</span>' # 非贪婪模式
salary = re.findall(salary_pat, res.text, re.S)
# 将取出的信息放到数据框
jobInfo = DataFrame([position, company, place, salary]).T
# 设置列名
jobInfo.columns = ['职位名', '公司名', '工作地点', '薪资']
print(jobInfo.head())
# 将数据保存到本地
jobInfo.to_csv('51job2.csv')
四、pyquery解析数据
1、pyquery
- pyquery库是jQuery的Python实现,就能以jQuery的语法来操作解析HTML文档,易用性和解析速度都很好
- 前提条件:
- 你对CSS选择器与jQuery有所了解
- 非Python标准模块,需要安装
- 安装方式
- pip install pyquery
- 测试方式
- import pyquery
- 安装方式
2、pyquery的初始化方式
- 字符串方式
- url方式
- 文件
# 字符串方式
from pyquery import PyQuery as py
html='''
<html>
<head>
<title>PyQuery</title>
</head>
<body>
<h1>PyQuery</h1>
</body>
</html>
'''
doc=py(html) # 创建PyQuery的对象,实际上就是在进行一个类型转换,将str类型转成PyQuery类型
print(doc) # 和html一样的内容
print(type(doc)) # <class 'pyquery.pyquery.PyQuery'>
print(type(html)) # <class 'str'>
print(doc('title')) # <title>PyQuery</title>
# url方式
from pyquery import PyQuery
doc=PyQuery(url='http://www.baidu.com', encoding='utf-8')
print(doc) # 获取html
print(doc('title')) # <title>百度一下,你就知道</title>
# 文件
from pyquery import PyQuery
doc=PyQuery(filename='a1.html')
print(doc) # 获取html
print(doc('h1')) # <h1>PyQuery</h1>
3、pyquery的使用
序号 | 提取数据 | 举例 |
1 | 获取当前节点 | doc('#main') |
2 | 获取子节点 | doc('#main').children() |
3 | 获取父节点 | doc('#main').parent() |
4 | 获取兄弟节点 | doc('#main').siblings() |
5 | 获取属性 | doc('#main').attr('href') |
6 | 获取内容 | doc('#main').html() doc('#main').text() |
from pyquery import PyQuery
html='''
<html>
<head>
<title>PyQuery</title>
</head>
<body>
<div id="main">
<a href="http://www.baidu.com">百度</a>
<h1>百度一下</h1>
</div>
<h2>Python学习</h2>
</body>
</html>
'''
doc=PyQuery(html)
# 获取当前节点
print(doc("#main")) # 获取整个div
# 获取父节点
print(doc("#main").parent()) # 获取整个body
# 获取子节点
print(doc("#main").children()) # 获取<a>和<h1>
# # 获取兄弟节点
print(doc("#main").siblings()) # 获取<h2>
print('----------获取属性------------')
print(doc('a').attr('href')) # http://www.baidu.com
print('----------获取标签的内容------------')
print(doc("#main").html()) # 获取<a>和<h1>,div中的所有都获取到了
print(doc("#main").text()) # 百度 百度一下, 只获取了div中的文本
4、课堂案例(爬取起点小说网)
import requests
from pyquery import PyQuery
url='https://www.qidian.com/rank/yuepiao'
headers={'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'}
resp=requests.get(url,headers)
# print(resp.text)
# 初始化PyQuery对象
doc=PyQuery(resp.text) # 使用字符串初始化方式初始化PyQuery对象
# a_tag=doc('h4 a') # 获取h4的a标签
# print(a_tag)
names=[a.text for a in doc('h4 a')]
# print(names) # ['大奉打更人', '这个人仙太过正经', '从红月开始', '稳住别浪',....]
authors = doc('p.author a') # 找<p class="author">下的<a>标签
# print(authors)
authors_lst=[]
for index in range(len(authors)):
if index%2==0:
authors_lst.append(authors[index].text)
# print(authors_lst) # ['卖报小郎君', '言归正传', '黑山老鬼', '跳舞', '我最白', '白驹易逝', ...]
for name,author in zip(names,authors_lst):
print(name, ':', author)
# 大奉打更人 : 卖报小郎君
# 这个人仙太过正经 : 言归正传
# 从红月开始 : 黑山老鬼
# 稳住别浪 : 跳舞
# ... ...