聚焦爬虫:爬取页面中指定的页面内容。
数据解析分类
- 正则
- bs4
- xpath(学习重点)
数据解析原理概述
解析的局部的文本内容都会在标签之间或者标签对应的属性中进行存储。
- 进行指定标签的定位
- 将标签或者标签对应的属性中存储的数据进行提取(解析)
编码流程
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
正则
常用的正则表达式
单字符:
. | 除换行以外所有字符 |
---|---|
[ ] | 匹配[]中列举的字符 |
\d | 匹配数字,0到9 |
\D | 匹配非数字 |
\s | 匹配空白,就是空格和Tab |
\S | 匹配非空白 |
\w | 匹配字母、数字或下划线字符,a-z,A-Z,0-9,_ |
\W | 匹配非字母、数字或下划线字符 |
数量修饰:
* | 前面的字符出现了0次或无限次,即可有可无 |
---|---|
+ | 前面的字符出现了1次或无限次,即最少一次 |
? | 前面的字符出现了0次或者1次,要么不出现,要么只出现一次 |
{m} | 前一个字符出现m次 |
{m,} | 前一个字符至少出现m次 |
{m,n} | 前一个字符出现m到n次 |
边界:
^ | 字符串开头 |
---|---|
$ | 字符串结尾 |
\b | 单词边界,即单词和空格间的位置,比如’er\b’可以匹配"never"中的’er’,但不能匹配"verb"中的’er’ |
\B | 非单词边界,和上面的\b相反 |
\A | 匹配字符串的开始位置 |
\Z | 匹配字符串的结束位置 |
分组:
I | 匹配左右任意一个表达式 |
---|---|
(re) | 匹配括号内的表达式,也表示一个组 |
(?:re) | 同上,但是不表示一个组 |
正则练习
import re
#提取出python
key="javapythonc++pho"
re.findall('python',key)[0]
#########################################################################
#提取出hello world
key="<html><hi>hello world<hi></html>"
re.findall('<hi>(.*)<hi>',key)[0]
#########################################################################
#提取170
string = '我喜欢身高170的女孩'
re.findall('\d*',string)
#########################################################################
#提取出http://和https://
key='http://www.baidu.com and https://boob.com'
re.findall('https?://',key)
#########################################################################
#提取出hello
key='lalala<hTml>hello(/HtMl>hahah' #输出<hTml>hello</HtMl>
re.findall('<[Hh][Tt][mM][lL]>(.*)</[Hh][Tt][mM][lL]>',key)
#########################################################################
#提取出hit
key='bobo@hit.edu.com' #想要匹配到hit
re.findall('h.*?\.',key)
#########################################################################
#匹配sas和saas
key='saas and sas and saaas'
re.findall('sa(1,2)s',key)
实战
需求:批量爬取壁纸图片
import requests
if __name__ == "__main__":
#如何爬取图片数据 url复制图片链接
url = 'https://t7.baidu.com/it/u=2621658848,3952322712&fm=193&f=GIF'
#content返回的是二进制形式的图片数据
#text(字符串) content(二进制) json() (对象)
img_data = requests.get(url=url).content
with open('./tupian.jpg','wb') as fp:
fp.write(img_data)
打开开发者工具,定位到Elements,定位到一张图片时可以发现,这张图片在源码当中是与一个img标签所存在的。img标签中有一个src属性,这个属性值即为这张图片的图片地址。
对src的属性值单独发一次请求,就可以请求到这张图片二进制的数据。
要爬取该页面所有图片,意味着在当前源码当中有很多组img标签,我们需要拿到满足我们需求的这些img标签的src属性值。
拿到src属性值后,我们需要批量对这些src属性值单独发起请求,就可以拿到批量的壁纸图片的二进制数据,在对这些二进制数据进行存储。
在当前源码中,所选行即包含当前页面的所有图片,而下方的子div则分别为该页面每一张图片。
import requests
import re
import os
if __name__ == "__main__":
#创建一个文件夹,保存所有的图片
if not os.path.exists('./bizhiLibs'):
os.mkdir('./bizhiLibs')
url = 'http://www.netbian.com/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
#使用通用爬虫对url对应的一整张页面进行爬取
page_text = requests.get(url=url, headers=headers).text
#使用聚焦爬虫将页面中所有的图片进行解析/提取
ex = '<li><a href="/desk.*?><img src="(.*?)" alt=.*?</a></li>'
img_src_list = re.findall(ex,page_text,re.S) #re.S表示单行匹配,re.M表示多行匹配
# print(img_src_list)
for src in img_src_list:
# 拼接出一个完整的图片url(若src没有携带http或httsp)
# src = 'https:'+src
#请求到了图片的二进制数据
img_data = requests.get(url=src,headers=headers).content
#生成图片名称
img_name = src.split('/')[-1]
#图片存储的路径
imgPath = './bizhiLibs/'+img_name
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')
需求:实现壁纸图片分页爬取
import requests
import re
import os
if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
#创建一个文件夹,保存所有的图片
if not os.path.exists('./bizhiLibs'):
os.mkdir('./bizhiLibs')
#设置一个通用的url模板
url = 'http://www.netbian.com/index_%d.htm'
# pageNum = 1
for pageNum in range(1,3):
#对应页码的url
new_url = format(url%pageNum)
#使用通用爬虫对url对应的一整张页面进行爬取
page_text = requests.get(url=new_url, headers=headers).text
#使用聚焦爬虫将页面中所有的图片进行解析/提取
ex = '<li><a href="/desk.*?><img src="(.*?)" alt=.*?</a></li>'
img_src_list = re.findall(ex,page_text,re.S) #re.S表示单行匹配,re.M表示多行匹配
# print(img_src_list)
for src in img_src_list:
# 拼接出一个完整的图片url(若src没有携带http或httsp)
# src = 'https:'+src
#请求到了图片的二进制数据
img_data = requests.get(url=src,headers=headers).content
#生成图片名称
img_name = src.split('/')[-1]
#图片存储的路径
imgPath = './bizhiLibs/'+img_name
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')
bs4
bs4只能运用在python语言当中
bs4数据解析的原理
:
1.实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
2.通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
环境安装:
pip install bs4
pip install lxml
也可以在PyCharm的settings中搜索安装
如何实例化BeautifulSoup 对象
from bs4 import BeautifulSoup
对象的实例化(2种形式):
- 将本地的html文档中的数据加载到该对象中
fp = open('./test.html','r',encoding='utf-8') #'r'表示读取数据
soup = BeautifulSoup(fp,'lxml')
- 将互联网上获取的页面源码加载到该对象中
page_text = response.text
soup = BeautifulSoup(page_text,'lxml')
提供的用于数据解析的方法和属性:
-
soup.tagName:返回的是html中第一次出现的tagName标签
-
soup.find():
1.find(‘tagName’):等同于soup.tagName
2.属性定位:soup.find(‘tagName’,class_/id/attr=’ ') -
soup.find_all(‘tagName’):返回符合要求的所有标签(列表)
-
select:
1.select(‘某种选择器(id,class,标签…选择器)’) 返回的是一个列表
2.层级选择器:
soup.select(‘.tang > ul > li > a’) 其中 > 表示的是一个层级
soup.select(‘.tang > ul a’) 空格表示的是多个层级 -
获取标签之间的文本数据:
soup.a.text/string/get_text()
区别:text/get_text()可以获取某一个标签中所有的文本内容;string只可以获取该标签下面直系的文本内容 -
获取标签中属性值:soup.a[‘href’]
bs4 实战
需求:爬取三国演义小说所有的章节标题和章节内容http://www.shicimingju.com/book/sanguoyanyi.html
import requests
from bs4 import BeautifulSoup
if __name__ == "__main__":
#对首页的页面数据进行爬取
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}
url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
page_text = requests.get(url=url,headers=headers).text
#在首页中解析出章节的标题和详情页的url
#1.实例化BeautifulSoup对象,需要将页面源码数据加载到该对象中
soup = BeautifulSoup(page_text,'lxml')
#解析章节标题和详情页的url
li_list = soup.select('.book-mulu > ul > li')
fp = open('./sanguo.txt','w',encoding='utf-8')
for li in li_list:
title = li.a.string
detail_url = 'https://www.shicimingju.com'+li.a['href']
#对详情页发起请求,解析出章节内容
detail_page_text = requests.get(url=detail_url,headers=headers).text
#解析出详情页中相关的章节内容
detail_soup = BeautifulSoup(detail_page_text,'lxml')
div_tag = detail_soup.find('div',class_='chapter_content')
#解析到了章节的内容
content = div_tag.text
fp.write(title+':'+content+'\n')
print(title,'爬取成功!!!')
xpath
xpath是最常用且最便捷高效的一种解析方式。通用性高(多种语言可用)
xpath解析原理
- 实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中。
- 调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
环境的安装:
pip install lxml
如何实例化一个etree对象
from lxml import etree
- 将本地的html文档中的源码数据加载到etree对象中
etree.parse(filePath)
- 可以将从互联网上获取的源码数据加载到该对象中
etree.HTML('page_text')
调用xpath方法
xpath('xpath表达式')
xpath表达式
- / 表示的是从根节点开始定位。表示的是一个层级
- // 表示的是多个层级;可以表示从任意位置开始定位。
- 属性定位://div[@class=‘song’] tag[@attrName=“attrVale”]
- 索引定位://div[@class=“song”]/p[3] 索引是从1开始的
- 取文本:
/text() 获取的是标签中直系的文本内容
//text() 标签中非直系的文本内容(所有的文本内容) - 取属性:
/@attrName ==>img/src
xpath实战
一、需求:爬取58二手房中的房源信息