数据解析
数据解析方式:
- 正则 (了解)
- bs4 (常用)
- xpath (最常用)
数据解析的作用?
用来实现聚焦爬虫。
数据解析的通用原理是什么?
原因:网页中显示的数据都是存储在html的标签中或者标签的属性中。
数据解析原理:
- 指定标签的定位
- 取出标签中存储的数据或者标签属性中的数据
正则(re)实例:
爬取校花网的图片(使用正则):http://www.521609.com/qingchunmeinv/
操作:需要将每一张图片的地址解析出来,然后对图片地址发起请求即可。
步骤:捕获到当前首页的页面源码数据
#!/usr/bin/env python # -*- coding: utf-8 -*- # author: 青城子 # datetime: 2021/7/16 20:15 # ide: PyCharm import re import os import requests url = "http://www.521609.com/qingchunmeinv/" headers = { "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', } page_text = requests.get(url=url, headers=headers).text ex = '<li>.*?<img src="(.*?)" width=.*?</li>' img_src_list = re.findall(ex, page_text, re.S) dirName = "ImgLibs" if not os.path.exists(dirName): os.mkdir(dirName) for src in img_src_list: scr = "http://www.521609.com" + src # 从当前获取的源码数据中解析图片地址 imaPath = "dirName".format(src.split("/")[-1]) print(src)
bs4
bs解析原理
- 实例化一个BeautifulSoup的对象,且将待解析的页面源码数据加载到该对象中。
- 调用BeautifulSoup对象中相关方法或者属性进行标签定位和文本数据提取
环境安装:
- pip install lxml # 解析器
- pip install bs4
BeautifulSoup对象的实例化
- BeautifulSoup(fp,'lxml') fp本地文件句柄,用来将本地存储的html文档中的数据进行解析。
- BeautifulSoup(page_text,'lxml') 用来将互联网上请求到的页面源码数据,进行解析。
标签定位:
from bs4 import BeautifulSoup soup.tagName # 只可以定位到第一次出现的tagName标签 soup.find("tagName", attrName="value") # 属性定位
soup.findAll() # 跟find一样用于属性定位,但是返回的list
soup.select('选择器'): # 选择器定位
- 类选择器
- id选择器
- 层级选择器
> 大于号: 表示一个层级
空格:表示多个层级
标签定位后取数据
.text 返回的是该标签下所有的文本内容
.string 返回的是该标签直系的文本内容
取属性
tag['attrName']
实例:
爬取三国演义全篇内容:https://www.shicimingju.com/book/sanguoyanyi.html
#!/usr/bin/env python # -*- coding: utf-8 -*- # author: 青城子 # datetime: 2021/7/16 22:33 # ide: PyCharm from bs4 import BeautifulSoup import requests headers = { "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', } main_url = "https://www.shicimingju.com/book/sanguoyanyi.html" response = requests.get(url=main_url, headers=headers) response.encoding = "utf-8" page_text = response.text fp = open('./sanguo.txt', 'w', encoding="utf-8") # 数据解析:章节标题、详情页url、章节内容 soup = BeautifulSoup(page_text, "lxml") # 定位到所有符合要求的a标签 a_list = soup.select(".book-mulu > ul > li > a") for a in a_list: title = a.string detail_url = "{0}{1}".format("https://www.shicimingju.com", a['href']) # 对详情页发起请求解析出章节内容 response = requests.get(url=detail_url, headers=headers) response.encoding = "utf-8" page_text_detail = response.text soup = BeautifulSoup(page_text_detail, "lxml") div_tag = soup.find("div", class_="chapter_content") content = div_tag.text fp.write(title+":"+content+'\n') print(title, "保存成功!!!") fp.close()
xpath解析
解析原理:
- 实例化一个etree的对象,且将待解析的页面源码数据加载到该对象中
- 调用etree对象的xpath方法结合着不同的xpath表达式标签定位和数据提取
实例化etree对象
- etree.parse("filename') 将本地html文档加载到该对象中
- etree.HTML(page_text) 将网站获取到的页面数据加载到该对象
标签定位
- 最左侧的/:如果xpath表达式最左侧是以/开头则表示该xpath表达式一定要从根表达式开始定位指定标签,所以要从html开始。
- 非最左侧的/:表示一个层级
- 非左侧的//:表示多个层级
- 最左侧的//:xpath表达式可以从任意位置进行标签定位
- 属性定位:tagName[@attrName="value"]
- 索引定位:tag[index]:索引是从1开始
取文本
- /text() 直系文本内容
- //text() 所有的文本内容
取属性
- /@attrName
局部数据解析
- 将定位到的页面中标签作为待解析的数据,再次使用xpath表达式解析待解析的数据。
- 在局部数据解析的时候,xpath表达式中要使用./的操作,./表示的就是当前的局部数据(xpath的调用者)。
实例
使用xpath爬取网站前5页图片名称和图片数据:地址:https://pic.netbian.com/4kmeinv/
#!/usr/bin/env python # -*- coding: utf-8 -*- # author: 青城子 # datetime: 2021/7/16 23:18 # ide: PyCharm import os import requests from lxml import etree dirName = "GirlsLib" if not os.path.exists(dirName): # 如果文件夹不存在就新建 os.mkdir(dirName) headers = { "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', } # 定义一个通用的url模板,不可变 url = "https://pic.netbian.com/4kmeinv/index_%d.html" for page in range(1, 6): if page == 1: new_url = "https://pic.netbian.com/4kmeinv/" else: new_url = format(url % page) response = requests.get(url=new_url, headers=headers) response.encoding = "gbk" page_text = response.text # 解析图片名称+图片数据 tree = etree.HTML(page_text) # 存储定位到的li标签 li_list = tree.xpath("//div[@class='slist']/ul/li") for li in li_list: print(type(li)) # li的数据类型和tree数据类型一样,li也可以调用xpath方法 title = li.xpath('./a/img/@alt')[0] + ".jpg" # 进行局部数据解析,必须加./。@alt属性就是图片名称 img_src = "http://pic.netbian.com" + li.xpath('./a/img/@src')[0] print(title, img_src) img_data = requests.get(url=img_src, headers=headers).content imPath = "{0}/{1}".format(dirName, title) with open(imPath, "wb") as fp: fp.write(img_data) print(title, "保存成功!!!")
需求:要求解析出携带html标签的局部数据?
- bs4 ,bs4在实现标签定位的时候返回的直接就是定位到标签对应的字符串数据
soup.span , soup.a 直接返回 <span class="nav-arrow"></span>标签
xpath表达式如何更加具有通用性?
- 在xpath表达式中使用管道符分割的作用,可以表示管道符左右两侧的子xpath表达式同时生效或者一个生效。
实例使用管道符:
需求:爬取热门城市和全部城市的名称 url :https://www.aqistudy.cn/historydata/
#!/usr/bin/env python # -*- coding: utf-8 -*- # author: 青城子 # datetime: 2021/7/17 9:21 # ide: PyCharm import os import requests from lxml import etree headers = { "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36', } url = "https://www.aqistudy.cn/historydata/" page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) # hot_cities = tree.xpath("//div[@class='bottom']/ul/li/a/text()") # all_cities = tree.xpath("//div[@class='bottom']/ul/div[2]/li/a/text()") # 上面可以写成一行,使用管道符 all_cities = tree.xpath("//div[@class='bottom']/ul/li/a/text() | //div[@class='bottom']/ul/div[2]/li/a/text() ") print(all_cities)
实例
需求:站长素材高清图片下载
反爬机制:图片懒加载,广泛应用在了一些图片的网站中。只有当图片被显示在浏览器可视化范围之内才会将img的伪属性变成真正的属性。如果是request发起的请求,request请求是没有可视化范围,因此我们一定要解析的是img伪属性的属性值(图片地址)