爬虫02 /数据解析
爬虫02 /数据解析
1. 数据解析概述
-
什么是数据解析,数据解析可以干什么?
- 概念:就是将一组数据中的局部数据进行提取。
- 作用:使用来实现聚焦爬虫
-
数据解析的通用原理
- 问题:html展示的数据可以存储在哪里?
- 标签之中
- 属性中
- 1.标签定位
- 2.取文本或者取属性
- 问题:html展示的数据可以存储在哪里?
-
数据解析的常用方法
-
re
-
bs4
-
xpath
-
pyquery
-
2. 正则实现的数据解析
-
如何对图片(二进制)数据进行爬取
方法一:
import requests headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36' } url = 'http://duanziwang.com/usr/uploads/2019/02/3334500855.jpg' pic_data = requests.get(url=url,headers=headers).content # content返回的是二进制类型的响应数据 with open('1.jpg','wb') as fp: fp.write(pic_data)
方法二:urllib就是一个低版本的requests
import urllib url = 'http://duanziwang.com/usr/uploads/2019/02/3334500855.jpg' urllib.request.urlretrieve(url=url,filename='./2.jpg')
-
两种图片爬取的方法的区别是什么?
- 方法1可以进行UA伪装,方法2不行
-
抓包工具中response中显示的页面源码和开发者工具的Element选项卡显示的页面源码的区别是什么?
-
Element:显示的页面源码内容是当前网页加载完毕后对应的所有数据(包含动态加载的数据)
-
response:显示的内容仅仅是当前一个请求请求到的数据(不包含动态加载的数据)
-
-
代码示例
- 需求的实现:爬取了一页的数据
import re import os url = 'http://duanziwang.com/category/%E6%90%9E%E7%AC%91%E5%9B%BE/' page_text = requests.get(url,headers=headers).text # 页面源码数据 # 新建一个文件夹 dirName = 'imgLibs' if not os.path.exists(dirName): os.mkdir(dirName) # 数据解析:每一张图片的地址 ex = '<article.*?<img src="(.*?)" alt=.*?</article>' img_src_list = re.findall(ex,page_text,re.S) # 爬虫中使用findall函数必须要使用re.S for src in img_src_list: imgName = src.split('/')[-1] imgPath = dirName+'/'+imgName urllib.request.urlretrieve(url=src,filename=imgPath) print(imgName,'下载成功!!!')
- 需求的实现:进行全站数据的爬取:爬取所有页码的图片数据
# 制定一个通用的url模板,不可以被改变 url = 'http://duanziwang.com/category/搞笑图/%d/' for page in range(1,4): new_url = format(url%page) page_text = requests.get(new_url,headers=headers).text # 页面源码数据 # 新建一个文件夹 dirName = 'imgLibs' if not os.path.exists(dirName): os.mkdir(dirName) # 数据解析:每一张图片的地址 ex = '<article.*?<img src="(.*?)" alt=.*?</article>' img_src_list = re.findall(ex,page_text,re.S) # 爬虫中使用findall函数必须要使用re.S for src in img_src_list: imgName = src.split('/')[-1] imgPath = dirName+'/'+imgName urllib.request.urlretrieve(url=src,filename=imgPath) print(imgName,'下载成功!!!')
3. bs4实现数据解析
-
环境的安装:
- pip install bs4
- pip install lxml
-
解析原理
- 实例化一个BeautifulSoup的一个对象,把即将被解析的页面源码内容加载到该对象中
- 调用BeautifulSoup对象中相关的方法和属性进行标签定位和本文数据的提取
-
BeautifulSoup对象的实例化的方式:
- BeautifulSoup(fp,'lxml'):将本地的文件内容加载到该对象中进行数据解析
- BeautifulSoup(page_text,'lxml'):将互联网上请求到的数据加载到该对象中进行数据解析
-
bs4相关解析操作
-
标签定位:返回值一定是定位到的标签
- soup.tagName:定位到第一个出现的tagName标签.返回的是单数
- 属性定位:soup.find('tagName',attrName='value'),返回的是单数
- find_all('tagName',attrName='value')返回的是复数(列表)
- 选择器定位:select('选择器'),返回的也是一个列表
- 层级选择器:
- 大于号:表示一个层级
- 空格:标识多个层级
- 层级选择器:
-
取文本
- string:只可以将标签中直系的文本取出
- text:可以将标签中所有的内容取出
-
取属性
- tag['attrName']
-
代码示例
from bs4 import BeautifulSoup fp = open('./test.html','r',encoding='utf-8') soup = BeautifulSoup(fp,'lxml') soup.p soup.find('div',class_='tang') soup.find('a',id='feng') soup.find_all('div',class_='tang') soup.select('#feng') soup.select('.tang > ul > li') soup.select('.tang li') tag = soup.title tag.text li_list = soup.select('.tang > ul > li') li_list[6].text div_tag = soup.find('div',class_='tang') div_tag.text a_tag = soup.select('#feng')[0] a_tag['href']
-
-
需求:http://www.shicimingju.com/book/sanguoyanyi.html 进行全篇小说内容的爬取
-
分析:
- 首页:解析出章节的名称+详情页的url
- 详情页:解析章节的内容
-
代码示例
# 爬取到首页的页面数据 main_url = 'http://www.shicimingju.com/book/sanguoyanyi.html' page_text = requests.get(main_url,headers=headers).text fp = open('./sanguo.txt','a',encoding='utf-8') # 解析章节名称+详情页的url soup = BeautifulSoup(page_text,'lxml') a_list = soup.select('.book-mulu > ul > li > a') for a in a_list: title = a.string # 章节标题 detail_url = 'http://www.shicimingju.com'+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,'下载成功!!!') fp.close()
4. xpath解析
-
环境的安装:
- pip install lxml
-
解析原理(流程)
- 实例化一个etree的对象,将解析的数据加载到该对象中
- 需要调用etree对象中的xpath方法结合着不同的xpath表达式进行标签定位和文本数据的提取
-
etree对象的实例化
- etree.parse('filePath'):将本都数据加载到etree中
- etree.HTML(page_text):将互联网上的数据加载到该对象中
-
html中所有的标签都是遵从了树状的结构,便于我们实现高效的节点的遍历或者查找(定位)
-
xpath方法的返回值一定是复数(列表)
-
标签定位
- 最左侧的/:xpath表达式式一定要从根标签开始进行定位
- 非最左侧的/:表示一个层级
- 最左侧的//:从任意位置进行标签定位(常用)
- 非最左侧//:表示多个层级
- //tagName:定位到所有的tagName标签
- 属性定位://tagName[@attrName="value"]
- 索引定位://tagName[index],index索引是从1开始
- 模糊匹配:
- //div[contains(@class, "ng")]
- //div[starts-with(@class, "ta")]
-
取文本
- /text():取直系的文本内容。列表只有一个元素
- //text():所有的文本内容。列表会有多个列表元素
-
取属性
- /@attrName
-
代码示例
from lxml import etree tree = etree.parse('./test.html') tree.xpath('/html/head/meta') tree.xpath('/html//meta') tree.xpath('//meta') tree.xpath('//div') tree.xpath('//div[@class="tang"]') tree.xpath('//li[1]') tree.xpath('//a[@id="feng"]/text()')[0] tree.xpath('//div[2]//text()') tree.xpath('//a[@id="feng"]/@href')
-
需求:爬取解析虎牙中直播的房间名称,热度,详情页的url
url = 'https://www.huya.com/g/lol' page_text = requests.get(url=url,headers=headers).text # 数据解析 tree = etree.HTML(page_text) li_list = tree.xpath('//div[@class="box-bd"]/ul/li') for li in li_list: # 实现局部解析:将局部标签下指定的内容进行解析 # 局部解析xpath表达式中的最左侧的./表示的就是xpath方法调用者对应的标签 title = li.xpath('./a[2]/text()')[0] hot = li.xpath('./span/span[2]/i[2]/text()')[0] detail_url = li.xpath('./a[1]/@href')[0] print(title,hot,detail_url)
- xpath图片数据爬取+乱码的处理
# url模板 url = 'http://pic.netbian.com/4kmeinv/index_%d.html' for page in range(1,11): new_url = format(url%page) # 只可以表示非第一页的页码连接 if page == 1: new_url = 'http://pic.netbian.com/4kmeinv/' page_text = requests.get(new_url,headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li') for li in li_list: img_name = li.xpath('./a/img/@alt')[0]+'.jpg' img_name = img_name.encode('iso-8859-1').decode('gbk') img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0] print(img_name,img_src)
- 使用xpath管道符
url = 'https://www.aqistudy.cn/historydata/' page_text = requests.get(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() | //div[@class="bottom"]/ul/li/a/text()') all_cities
- xpath表达式中管道符的应用
- 目的:使得xpath表达式具有更强的通用性
总结:
-
content返回的是二进制类型的响应数据
data = requests.get(url=url,headers=headers).content
-
正则re.S的使用
-
img_src_list = re.findall(ex,page_text,re.S)
-
如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始,不会跨行
而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,将“\n”当做一个普通的字符加入到这个字符串中,在整体中进行匹配
-
-
new_url = format(url%page)
def format(value, format_spec='', /) Return value.__format__(format_spec)
-
urllib用的是request请求数据,不是requests
urllib.request.urlretrieve(url=src,filename=imgPath)
-
bs4根据class类值进行解析的时候,用class_代替class