爬虫&逆向--Day03--数据解析
一、何为数据解析
-
概念:可以将爬取到的数据中指定的数据进行单独提取。
-
数据解析通用原理:
-
在一张页面中,想要解析的数据是存在于相关的html的标签中。
-
可以先将指定的标签进行定位,然后可以将该标签中展示的数据进行提取。
-
-
爬虫编码流程:
-
指定url
-
发起请求
-
获取页面源码数据
-
数据解析
-
持久化存储
-
-
python中可以实现数据解析的技术:
-
正则表达式(复杂度高)
-
bs4(python独有,学习成本较低)
-
xpath(通用性最强,最重要)
-
二、数据解析的主流策略
2.1、bs4
-
环境安装:
-
pip install bs4
-
pip install lxml
-
-
bs4数据解析的流程:
-
创建一个BeautifulSoup对象,把被解析的数据加载到该对象中。
-
调用BeautifulSoup对象相关的属性或者方法进行标签定位和数据提取
-
-
具体解析的操作:
-
在当前目录下新建一个test.html文件,然后将下述内容拷贝到该文件中
-
<html lang="en"> <head> <meta charset="UTF-8" /> <title>测试bs4</title> </head> <body> <div> <p>百里守约</p> </div> <div class="song"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href="http://www.song.com/" title="赵匡胤" target="_self"> <span>this is span</span> 宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a> <a href="" class="du">总为浮云能蔽日,长安不见使人愁</a> <img src="http://www.baidu.com/meinv.jpg" alt="" /> </div> <div class="tang"> <ul> <li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li> <li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li> <li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li> <li><a href="http://www.sina.com" class="du">杜甫</a></li> <li><a href="http://www.dudu.com" class="du">杜牧</a></li> <li><b>杜小月</b></li> <li><i>度蜜月</i></li> <li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li> </ul> </div> </body> </html>
- 有了test.html文件后,在练习如下操作
-
from bs4 import BeautifulSoup # 1.创建一个BeautifulSoup的工具对象,然后把即将被解析的页面源码数据加载到该对象中 fp = open('test.html', 'r', encoding='utf-8') # 参数1:被解析的页面源码数据 # 参数2:固定形式的lxml(一种解析器) soup = BeautifulSoup(fp, 'lxml') # 2.可以调用BeautifulSoup对象的相关函数和属性进行标签定位和数据提取 # ********************************标签定位---方式1--标签名定位************************** # 标签定位---方式1:soup.tagName(tagName是标签名,只可以定位到第一次出现的该标签) title_tag = soup.title print("soup.title:", title_tag) # soup.title: <title>测试bs4</title> p_tag = soup.p print("p.tag:", p_tag) # p.tag: <p>百里守约</p> # ********************************标签定位---方式2--属性定位************************** # 标签定位---方式2:(属性定位):soup.find(tagName,attrName='value') tagName是标签名,attrName是属性名 """ 注意: 1、find只可以定位满足要求的第一个标签 2、如果使用class属性值的话,find参数class_ """ # 定位到class属性值为song的div标签 div_tag = soup.find('div', class_='song') print("div_tag:", div_tag) """ div_tag: <div class="song"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href="http://www.song.com/" target="_self" title="赵匡胤"> <span>this is span</span> 宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a> <a class="du" href="">总为浮云能蔽日,长安不见使人愁</a> <img alt="" src="http://www.baidu.com/meinv.jpg"/> </div> """ # 定位到class属性值为du的a标签 a_tag = soup.find('a', class_='du') print("a_tag:", a_tag) # a_tag: <a class="du" href="">总为浮云能蔽日,长安不见使人愁</a> # 定位到id的属性值为feng的a标签 a_tag = soup.find('a', id='feng') print("a_tag:", a_tag) # a_tag: <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a> # *****************************标签定位---方式3--属性定位--find_all*********************** """ 注意: 1、find_all和find使用方式相同, 2、find_all会定位满足要求的所有的标签,并以列表的形式输出 3、find只会输出满足要求的第一个标签 """ p_all_tag = soup.find_all('p') print("p_all_tag:", p_all_tag) # p_all_tag: [<p>百里守约</p>, <p>李清照</p>, <p>王安石</p>, <p>苏轼</p>, <p>柳宗元</p>] div_all_tag = soup.find_all('div', class_='song') print("div_all_tag:", div_all_tag) """ div_all_tag: [<div class="song"> <p>李清照</p> <p>王安石</p> <p>苏轼</p> <p>柳宗元</p> <a href="http://www.song.com/" target="_self" title="赵匡胤"> <span>this is span</span> 宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a> <a class="du" href="">总为浮云能蔽日,长安不见使人愁</a> <img alt="" src="http://www.baidu.com/meinv.jpg"/> </div>] """ # *************************标签定位---方式4--选择器定位--class选择器和id选择器和层级选择器******************** # 常用的选择器:class选择器(.class属性值) id选择器(#id的属性值) tags = soup.select('#feng') # 定位到id的属性值为feng对应的所有标签,以列表形式输出 print("id选择器:", tags) # id选择器: [<a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>] tags = soup.select('.du') # 定位到class属性值为du对应的所有标签,以列表的形式输出 print("class选择器:", tags) """ class选择器: [<a class="du" href="">总为浮云能蔽日,长安不见使人愁</a>, <a class="du" href="http://www.sina.com">杜甫</a>, <a class="du" href="http://www.dudu.com">杜牧</a>] """ # 层级选择器:>表示一个层级 一个空格可以表示多个层级 # tags = soup.select('.tang > ul > li > a') tags = soup.select('.tang a') print("层级选择器:", tags) """ 层级选择器: [<a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a>, <a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a>, <a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a>, <a class="du" href="http://www.sina.com">杜甫</a>, <a class="du" href="http://www.dudu.com">杜牧</a>, <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>] 层级选择器: [<a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a>, <a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a>, <a alt="qi" href="http://www.126.com">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a>, <a class="du" href="http://www.sina.com">杜甫</a>, <a class="du" href="http://www.dudu.com">杜牧</a>, <a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a>] """ # *************************定位到的标签的内部数据的提取方式******************** # 方式1:提取标签内的文本数据 # tag.string:只可以将标签直系的文本内容提取出 tag = soup.select("#feng")[0] # 因为select获取的标签以列表的形式输出,所以可以通过列表的形式[0]获取 print("文本值提取:", tag.string) # 文本值提取: 凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘 # tag.text:可以将标签内部所有的文本内容提取出,包括当前标签 tag = soup.find("div", class_='tang') print("文本值提取:", tag.text) """ 文本值提取: 清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村 秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山 岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君 杜甫 杜牧 杜小月 度蜜月 凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘 """ # 方式2:提取标签的属性值 tag['attrName'] img_tag = soup.find('img') # 获取这个图片标签 print("图片标签属性值:", img_tag) # 图片标签属性值: <img alt="" src="http://www.baidu.com/meinv.jpg"/> img_src = img_tag['src'] # 获取该标签的src属性的值 print("图片标签属性值:", img_src) # 图片标签属性值: http://www.baidu.com/meinv.jpg
-
-
import requests from bs4 import BeautifulSoup fp = open('xiaoshu.txt', 'w', encoding='utf-8') headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36" } url = "https://bixuejian.5000yan.com/" # 获取首页对应的页面源码数据 response = requests.get(url=url, headers=headers) response.encoding = 'utf-8' page_text = response.text # 只要不是二进制数据,都可以使用text接收 # 对获取到的首页源码数据进行数据解析,获取章节标题和每个章节对应的链接 soup = BeautifulSoup(page_text, 'lxml') # 将所有的a标签定位到,并都保证到列表中 a_list = soup.select('.mx-auto > li > a') # 便利该列表,获取a标签中的文本和章节详情页的url for a in a_list: # 章节标题 title = a.string # 章节标题 detail_url = a['href'] # 章节详情页的url # 对详情页的url进行请求,为了获取详情页的页面源码数据,将章节内容进行解析 detail_response = requests.get(url=detail_url, headers=headers) detail_response.encoding = 'utf-8' detail_page_text = detail_response.text # 解析详情页,将章节内容进行提取 detail_soup = BeautifulSoup(detail_page_text, 'lxml') div_tag = detail_soup.find('div', class_='grap') # 章节内容 content = div_tag.text # 往文件内写入数据 fp.write(title + ":" + content + "\n") print(title, ":章节爬取保存成功!") fp.close()
2.2、xpath(重点)
-
环境安装:
-
-
xpath解析的编码流程:
-
创建一个etree类型的对象,把被解析的数据加载到该对象中
-
调用etree对象中xpath函数结合不同形式的xpath表达式进行标签定位和数据的提取
-
-
xpath表达式如何理解?
-
from lxml import etree # 1.创建一个etree的工具对象,然后把即将被解析的页面源码数据加载到该对象中 tree = etree.parse('test.html') # 2.调用etree对象的xpath函数然后结合着不同形式的xpath表达式进行标签定位和数据提取 # xpath函数返回的是列表,列表中存储的是满足定位要求的所有标签 # /html/head/title定位到html下面的head下面的title标签 title_tag = tree.xpath('/html/head/title') print("title_tag:", title_tag) # title_tag: [<Element title at 0x1c5aa941380>] # //title在页面源码中定位到所有的符合要求的title标签 title_tag = tree.xpath('//title') print("title_tag:", title_tag) # title_tag: [<Element title at 0x1c5aa941380>] # 定位到所有的符合要求的div标签 div_tags = tree.xpath('//div') print("div_tags:", div_tags) # div_tags: [<Element div at 0x1c0b9731380>, <Element div at 0x1c0b97310c0>, <Element div at 0x1c0b9731680>] # ************************* 属性定位://tagName[@attrName='value'] ******************** # 属性定位 # 定位到class属性值为song的div标签 //tagName[@attrName='value'] div_tag = tree.xpath('//div[@class="song"]') print("div_tag:", div_tag) # div_tag: [<Element div at 0x22229e11300>] # ************************* 索引定位://tag[index] ******************** """ 注意: 索引是从1开始的 """ div_tag = tree.xpath('//div[1]') # 获取第一个div print("div_tag:", div_tag) # div_tag: [<Element div at 0x1cf38c2d840>] # ************************* 层级定位: /表示一个层级 //表示多个层级 ******************** """ 注意: /表示一个层级 //表示多个层级 """ a_list = tree.xpath('//div[@class="tang"]/ul/li/a') print("a_list:", a_list) a_list = tree.xpath('//div[@class="tang"]//a') print("a_list:", a_list) """ a_list: [<Element a at 0x238eef11a80>, <Element a at 0x238eef11ac0>, <Element a at 0x238eef11b00>, <Element a at 0x238eef11b40>, <Element a at 0x238eef11b80>, <Element a at 0x238eef11c00>] a_list: [<Element a at 0x238eef11a80>, <Element a at 0x238eef11ac0>, <Element a at 0x238eef11b00>, <Element a at 0x238eef11b40>, <Element a at 0x238eef11b80>, <Element a at 0x238eef11c00>] """ # ********************************************* 数据提取 **************************************** # 方式1: """ 注意: 1、提取标签中的文本内容:/text()提取直系文本 //text()提取所有文本 2、/text() //text() 也都是以列表的形式输出 """ a_content = tree.xpath('//a[@id="feng"]/text()') print("a_content:", a_content) # a_content: ['凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘'] a_content_0 = tree.xpath('//a[@id="feng"]/text()')[0] print("a_content_0:", a_content_0) # a_content_0: 凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘 div_content = tree.xpath('//div[@class="song"]//text()') print("div_content:", div_content) """ div_content: ['\n\t\t', '李清照', '\n\t\t', '王安石', '\n\t\t', '苏轼', '\n\t\t', '柳宗元', '\n\t\t', '\n\t\t\t', 'this is span', '\n\t\t宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱', '\n\t\t', '总为浮云能蔽日,长安不见使人愁', '\n\t\t', '\n\t'] """ div_content_3 = tree.xpath('//div[@class="song"]//text()')[3] print("div_content_3:", div_content_3) # div_content_3: 王安石 # 2.提取标签的属性值://tag/@attrName img_src = tree.xpath('//img/@src') print("img_src:", img_src) # img_src: ['http://www.baidu.com/meinv.jpg'] img_src_0 = tree.xpath('//img/@src')[0] print("img_src_0:", img_src_0) # img_src_0: http://www.baidu.com/meinv.jpg
备注:Xpath在浏览器中如果获取某个标签,是可以通过:右击该标签-->Copy-->Copy XPath 进行复制获取的。
-
-
http://pic.netbian.com/4kmeinv/
-
将爬取到的图片存储到指定的文件夹中
-
-
from lxml import etree import requests import os # 新建一个文件夹 dirName = 'girls' # 如果文件夹不存在,则新建,否则不新建 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/128.0.0.0 Safari/537.36" } url = "http://pic.netbian.com/4kmeinv/" response = requests.get(url=url, headers=headers) response.encoding = 'gbk' # 中文编码如果是乱码,不是 utf-8 就是 gbk page_text = response.text # 数据解析:图片地址+图片名称 parse存放的是html文档的文件 tree = etree.HTML(page_text) # HTML()专门用来解析网络请求到的页面源码数据 接收的是字符串 # 该列表中存储的是每一个li标签 # li_list = tree.xpath('//div[@class="slist"]/ul/li') # 手写xpath函数 li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li') # 复制得到的xpath函数,*是通配符,可以表示任何标签的名称 for li in li_list: # li.xpath()局部解析:将li标签中指定的内容解析出来 img_src = "http://pic.netbian.com" + li.xpath('./a/img/@src')[0] # 局部解析 img_title = li.xpath('./a/b/text()')[0] + '.jpg' # 左侧./表示xpath的调用者对应的标签,当前标签 # print(img_title, img_src) # 对图片发起请求,存储图片数据 img_data = requests.get(url=img_src, headers=headers).content img_path = dirName + '/' + img_title # girls/123.jpg with open(img_path, 'wb') as fp: fp.write(img_data) print(img_title, '下载保存成功')
-
-
from lxml import etree import requests import os # 新建一个文件夹 dirName = 'girls' # 如果文件夹不存在,则新建,否则不新建 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/128.0.0.0 Safari/537.36" } for page in range(1, 4): print("正在爬取第%d页的图片数据....." % page) if page == 1: url = "http://pic.netbian.com/4kmeinv/" else: url = 'https://pic.netbian.com/4kmeinv/index_%d.html' % page response = requests.get(url=url, headers=headers) response.encoding = 'gbk' # 中文编码如果是乱码,不是 utf-8 就是 gbk page_text = response.text # 数据解析:图片地址+图片名称 parse存放的是html文档的文件 tree = etree.HTML(page_text) # HTML()专门用来解析网络请求到的页面源码数据 接收的是字符串 # 该列表中存储的是每一个li标签 # li_list = tree.xpath('//div[@class="slist"]/ul/li') # 手写xpath函数 li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li') # 复制得到的xpath函数,*是通配符,可以表示任何标签的名称 for li in li_list: # li.xpath()局部解析:将li标签中指定的内容解析出来 img_src = "http://pic.netbian.com" + li.xpath('./a/img/@src')[0] # 局部解析 img_title = li.xpath('./a/b/text()')[0] + '.jpg' # 左侧./表示xpath的调用者对应的标签,当前标签 # print(img_title, img_src) # 对图片发起请求,存储图片数据 img_data = requests.get(url=img_src, headers=headers).content img_path = dirName + '/' + img_title # girls/123.jpg with open(img_path, 'wb') as fp: fp.write(img_data) print(img_title, '下载保存成功')
-
2.3、作业:
简历模版下载:https://sc.chinaz.com/jianli/free.html
-
载当前页所有的简历模板
-
简历名称+简历的下载链接
-
根据简历的下载链接 下载简历文件
-
根据下载地址下载的压缩包,压缩包是二进制的数据
-
-
import os from lxml import etree import requests import time # 新建一个文件夹 dirName = 'jianli' # 如果文件夹不存在,则新建,否则不新建 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/128.0.0.0 Safari/537.36" } url = "https://sc.chinaz.com/jianli/free.html" response = requests.get(url=url, headers=headers) response.encoding = 'utf-8' page_text = response.text # 数据解析: tree = etree.HTML(page_text) div_list = tree.xpath('//*[@id="container"]/div') # 获取每一个div for div in div_list: # 获取每个简历的标题和每个简历的详情链接 jianli_src = div.xpath('./a/@href')[0] # 获取a标签下的href属性 jianli_title = div.xpath('./p/a/text()')[0] + '.rar' # 获取a表现下的直系文本 # 对简历的详情发起请求 response = requests.get(url=jianli_src, headers=headers) response.encoding = 'utf-8' detail_page_text = response.text # 对详情页面的数据进行数据解析,获取下载地址 tree = etree.HTML(detail_page_text) # 得到下载地址列表li_list,从下载地址列表中,获取下标为0的第一个下载地址 li_list = tree.xpath('//*[@id="down"]/div[2]/ul/li')[0] detail_rar_url = li_list.xpath('./a/@href')[0] # 下载地址 福建电信下载 detail_rar_text = li_list.xpath('./a/text()')[0] # 福建电信下载 print(detail_rar_text, detail_rar_url) # 进行下载操作 detail_data = requests.get(url=detail_rar_url, headers=headers).content time.sleep(10) detail_path = dirName + '/' + jianli_title # jianli/xxx.rar with open(detail_path, 'wb') as fp: fp.write(detail_data) print(jianli_title, '下载保存成功')
- 图片下载:url:https://sc.chinaz.com/tupian/meinvtupian.html
-
from lxml import etree import requests import os # 新建一个文件夹 dirName = 'zuoye_girls' # 如果文件夹不存在,则新建,否则不新建 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/128.0.0.0 Safari/537.36" } url = 'https://sc.chinaz.com/tupian/meinvtupian.html' response = requests.get(url=url, headers=headers) response.encoding = 'utf-8' page_text = response.text # 数据解析:图片地址+图片名称 tree = etree.HTML(page_text) div_list = tree.xpath('/html/body/div[3]/div[2]/div') for div in div_list: # 解析图片的下载地址 用data-original这个伪属性,当图片没出现在可视化界面的时候,不会加载------懒加载 img_src = "https:" + div.xpath('./img/@data-original')[0] # 解析图片的名称,拼接成为文件名 img_title = div.xpath('./div/a/text()')[0] + '.jpg' # 下载图片 img_data = requests.get(url=img_src, headers=headers).content # 拼接文件保存的位置 img_path = dirName + '/' + img_title with open(img_path, 'wb') as fp: fp.write(img_data) print(img_title, '下载保存成功')
浙公网安备 33010602011771号