爬虫之四种数据解析
在获取到响应数据后,需要针对性的提取其中有用的部分,这也是采集数据最常见的方式聚焦网络爬虫。
数据解析四种方式:
3.bs4解析
案例:爬取糗事百科---糗图:https://www.qiushibaike.com/pic/
1 ''' 2 <div class="article block untagged mb15" id="qiushi_tag_122080461"> 3 4 <div class="author clearfix"> 5 <a href="/users/42609417/" target="_blank" rel="nofollow"> 6 <img src="//pic.qiushibaike.com/system/avtnew/4260/42609417/medium/20190802105831.jpg" alt="灿烂的你"> 7 </a> 8 <a href="/users/42609417/" target="_blank" title="灿烂的你"> 9 <h2>灿烂的你</h2> 10 </a> 11 <div class="articleGender womenIcon">0</div> 12 </div> 13 14 15 16 <a href="/article/122080461" target="_blank" class="contentHerf"> 17 <div class="content"> 18 19 20 21 <span>求大神把中间图片p掉看看长啥样</span> 22 23 24 </div> 25 </a> 26 27 28 29 30 <div class="thumb"> 31 32 <a href="/article/122080461" target="_blank"> 33 <img src="//pic.qiushibaike.com/system/pictures/12208/122080461/medium/3N2UVCLSJEHGAUAZ.jpg" alt="求大神把中间图片p掉看看长啥样"> 34 </a> 35 36 </div> 37 38 39 40 <div class="stats"> 41 <span class="stats-vote"><i class="number">33</i> 好笑</span> 42 <span class="stats-comments"> 43 44 45 46 47 </span> 48 </div> 49 <div id="qiushi_counts_122080461" class="stats-buttons bar clearfix"> 50 <ul class="clearfix"> 51 <li id="vote-up-122080461" class="up"> 52 <a href="javascript:voting(122080461,1)" class="voting" data-article="122080461" id="up-122080461" rel="nofollow"> 53 <i></i> 54 <span class="number hidden">33</span> 55 </a> 56 </li> 57 <li id="vote-dn-122080461" class="down"> 58 <a href="javascript:voting(122080461,-1)" class="voting" data-article="122080461" id="dn-122080461" rel="nofollow"> 59 <i></i> 60 <span class="number hidden">0</span> 61 </a> 62 </li> 63 64 <li class="comments"> 65 <a href="/article/122080461" id="c-122080461" class="qiushi_comments" target="_blank"> 66 <i></i> 67 </a> 68 </li> 69 70 </ul> 71 </div> 72 <div class="single-share"> 73 <a class="share-wechat" data-type="wechat" title="分享到微信" rel="nofollow">微信</a> 74 <a class="share-qq" data-type="qq" title="分享到QQ" rel="nofollow">QQ</a> 75 <a class="share-qzone" data-type="qzone" title="分享到QQ空间" rel="nofollow">QQ空间</a> 76 <a class="share-weibo" data-type="weibo" title="分享到微博" rel="nofollow">微博</a> 77 </div> 78 <div class="single-clear"></div> 79 80 81 82 83 </div> 84 '''
数据采集之re模块正则解析脚本
1 # 糗事百科---糗图:https://www.qiushibaike.com/pic/ 2 import os 3 import re 4 import requests 5 from fake_useragent import UserAgent 6 UA=UserAgent() 7 headers={ 8 'User-Agent':UA.random 9 } 10 start_page=1 11 end_page=36 12 reg='<div class="article block untagged mb15".*?<h2>(.*?)</h2>.*?<img src="(.*?)" alt.*?</div>'#正则表达式编写 13 14 15 if not os.path.exists('糗图'): 16 os.mkdir('糗图') 17 18 for page in range(start_page,end_page): 19 print(f'第{page}页糗图数据获取中............') 20 url = f'https://www.qiushibaike.com/pic/page/{page}/?s=5215717' 21 response=requests.get(url,headers=headers) 22 res=re.findall(reg,response.text,re.S)#正则进行匹配提取解析----注意re.S处理换行和回车 23 # print(res) 24 for pic in res: 25 pic_url='https:'+pic[1] 26 pic_name=pic[1].rsplit('/')[-1] 27 pic_path=os.path.join('糗图',pic_name) 28 29 if pic_name in os.listdir('糗图'): 30 continue 31 response_pic=requests.get(pic_url,headers=headers) 32 33 with open(pic_path,'wb')as f: 34 f.write(response_pic.content) 35 print(f'{pic_name}下载成功!!!')
依赖模块: xlml pip install lxml 模块导入: from lxml.html.clean import etree #python 3.5之后 #from lxml import etree #python 3.5之前 解析原理: 实例化一个etree类型的对象,且将即将被解析的页面源码数据加载到该对象中 调用该对象中的xpath方法结合着不同的xpath表达式进行标签定位和数据提取 实例化对象: tree=etree.parse(fileName)----本地文件 tree=etree.HTML(page_text)----获取的页面内容
xpath方法返回值永远是一个列表
xpath表达式中最左侧的/和//的区别: /表示必须从根标签进行定位(从html开始) //表示可以从任意位置标签定位
xpath表达式中非最左侧的/和//的区别: /表示直属层级目录 //表示后代多层级目录
xpath标签定位: 属性定位: tree.xpath('//div[@id="content"]') tree.xpath('//div[@class="c1"]')
tree.xpath('//div/sapn|/p')找到div下所有直属的span和p标签
层级、索引定位:
tree.xpath('//div[@id="content"]/ul/li[2]')找到id为content的div下ul中的第2(索引从1开始)个直系li标签
逻辑运算定位:
tree.xpath('//a[@id="content" and @href=""]')找到id为content并且href为空的a标签
tree.xpath('//a[@id="content" or @href=""]')找到id为content或者href为空的a标签
模糊定位:
tree.xpath('//div[contains(@class,"c")]')找到class属性中含有c的div标签
tree.xpath('//div[start-with(@class,"c")]')找到class属性以c开头的div标签
位置属性定位:
tree.xpath('//title[@*]") 选取所有带有属性的 div 元素
xpath取值:
取文本
/text():获取的是标签下直系的文本数据
//text():获取的是标签下所有的文本数据
取属性@
/@attr
更多可参考菜鸟教程:https://www.runoob.com/xpath/xpath-syntax.html
案例一:糗事百科----文字:https://www.qiushibaike.com/text/
1 # 糗事百科----文字:https://www.qiushibaike.com/text/ 2 import requests 3 from lxml.html.clean import etree 4 from fake_useragent import UserAgent 5 UA=UserAgent() 6 headers={'User-Agent':UA.random} 7 8 url_model='https://www.qiushibaike.com/text/page/%d/' 9 fp=open('糗事百科---段子.txt','w',encoding='utf-8') 10 for page in range(1,14): 11 url=format(url_model%page) 12 response=requests.get(url,headers=headers) 13 tree=etree.HTML(response.text) 14 nick_list=tree.xpath('//div[@class="author clearfix"]/a[2]/h2/text()|//div[@class="author clearfix"]/span[2]/h2/text()')#解析昵称(使用了‘|’多条件选择匹配) 15 link_list=tree.xpath('//a[@class="contentHerf"]/@href')#解析内容的连接 16 17 content_list=[] 18 url_='https://www.qiushibaike.com' 19 for nick,link in zip(nick_list,link_list): 20 content_list.append((nick,url_+link)) 21 # print(content_list) 22 23 for i,content in enumerate(content_list): 24 nick_name=content[0] 25 url_detail=content[1] 26 response_detail=requests.get(url_detail,headers=headers) 27 content_tree=etree.HTML(response_detail.text) 28 29 content_detail=content_tree.xpath('//div[@class="content"]/text()') 30 content_str='\n'.join(content_detail) 31 str='<'+nick_name.strip('\n')+'>\n'+content_str+'\n\n' 32 fp.write(str) 33 print(f'第{page}页第{i}条段子下载完成!') 34 fp.close()
案例二:彼岸图图片采集:http://pic.netbian.com/
1 ''' 2 彼岸图图片采集:http://pic.netbian.com/ 3 ''' 4 import os 5 from lxml.html.clean import etree#xpath解析 6 import requests 7 from fake_useragent import UserAgent 8 UA=UserAgent() 9 headers={'User-Agent':UA.random} 10 11 #首页获取图片分类 12 url_index='http://pic.netbian.com' 13 response_index=requests.get(url_index,headers=headers) 14 response_index.encoding='gbk' 15 # tree = etree.parse(filename)#解析本地html文件 16 tree_index=etree.HTML(response_index.text) 17 picture_list=tree_index.xpath('//div[@class="classify clearfix"]/a/@href') 18 title_list=tree_index.xpath('//div[@class="classify clearfix"]/a/text()') 19 all_pictures=[] 20 for title,pic in zip(title_list,picture_list): 21 all_pictures.append((title,url_index+pic)) 22 print('图片分类如下:') 23 for i,j in enumerate(all_pictures): 24 print(i,'\t',j[0]) 25 26 # print(all_pictures) 27 #输入数字序号进行专辑分类采集: 28 num=int(input('请输入你要采集的图片的序号>>>>')) 29 30 31 #创建专辑文件夹 32 dir_pic=all_pictures[num][0] 33 if not os.path.exists(dir_pic): 34 os.mkdir(dir_pic) 35 36 #请求每个分类的第一页拿到总页码,并保存第一页的内容 37 print(f'{dir_pic}加载中......') 38 print('获取第1页图片:') 39 url=all_pictures[num][1] 40 41 #请求首页,获取首页数据及总页码 42 response = requests.get(url, headers=headers) 43 tree = etree.HTML(response.text) 44 pages = int(tree.xpath('//div[@class="page"]/a[7]/text()')[0]) 45 print(pages,type(pages)) 46 47 li_list = tree.xpath('//div[@class="slist"]/ul[@class="clearfix"]/li') 48 for i,li in enumerate(li_list): 49 name = '' 50 if li.xpath('./a/b/text()'): 51 name = li.xpath('./a/b/text()')[0].encode('iso-8859-1').decode('gbk') 52 url_ = url_index + li.xpath('./a/img/@src')[0] 53 54 file_name = name + url_.rsplit('/')[-1] 55 response_ = requests.get(url_, headers=headers) 56 file_path = os.path.join(dir_pic, file_name) 57 if file_name in os.listdir(dir_pic): 58 continue 59 with open(file_path, 'wb')as f: 60 f.write(response.content) 61 print(f"第1页第{i}张{file_name}下载完成!") 62 print(url) 63 64 #第二页开始爬取 65 for i in range(2,pages): 66 url_=url+f'index_{i}.html' 67 response = requests.get(url_, headers=headers) 68 tree = etree.HTML(response.text) 69 70 li_list = tree.xpath('//div[@class="slist"]/ul[@class="clearfix"]/li') 71 for j,li in enumerate(li_list): 72 name = '' 73 if li.xpath('./a/b/text()'): 74 name =li.xpath('./a/b/text()')[0].encode('iso-8859-1').decode('gbk') 75 url_ = url_index + li.xpath('./a/img/@src')[0] 76 77 file_name = name + url_.rsplit('/')[-1] 78 response_ = requests.get(url_, headers=headers) 79 file_path = os.path.join(dir_pic, file_name) 80 if file_name in os.listdir(dir_pic): 81 continue 82 with open(file_path, 'wb')as f: 83 f.write(response_.content) 84 print(f"第{i}页第{j}张{file_name}下载完成!")
依赖模块: (1)bs4 pip install bs4 (2)xlml pip install lxml 模块导入: from bs4 import BeautifulSoup bs4解析原理 (1)实例化一个BeautifulSoup的对象,且将即将被解析的页面源码加载到该对象中 (2)使用该对象中的属性或者方法进行标签定位和数据提取 使用方式: (1)将本地存储的html文档加载到该对象中 soup=BeautifulSoup(fp,'lxml') (2)将互联网上获取的html源码加载到该对象中 soup=BeautifulSoup(page_text,'lxml') 内容解析: (1)定位标签 a. soup.tagName返回一个标签 soup.div
b. soup.find(’tagName',attr_=value)返回第一个标签 soup.find('div',class_='home') c. soup.find_all(’tagName',attr_=value)返回标签列表 soup.find('div',class_='home') d. soup.select('选择器')返回列表 类选择器,id选择器,标签选择器,层级选择器(>表示一个层级,空格表示多个层级)soup.select('.tang > ul > li > a')
(2)获取标签内容或者属性值 a. element.string string获取的是标签中直系的文本内容
b. element.text text获取的是标签中所有的文本内容
c. element.attrs/element.attrs['属性']/soup.tagName['属性'] 获取标签的所有/指定属性值
1 环境安装 2 - 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等 3 - windows 4 (1)打开文件资源管理器(文件夹地址栏中) 5 (2)地址栏上面输入 %appdata% 6 (3)在这里面新建一个文件夹 pip 7 (4)在pip文件夹里面新建一个文件叫做 pip.ini ,内容写如下即可 8 [global] 9 timeout = 6000 10 index-url = https://mirrors.aliyun.com/pypi/simple/ 11 trusted-host = mirrors.aliyun.com 12 - linux 13 (1)cd ~ 14 (2)mkdir ~/.pip 15 (3)vi ~/.pip/pip.conf 16 (4)编辑内容,和windows一模一样 17 - 需要安装:pip install bs4 18 bs4在使用时候需要一个第三方库,把这个库也安装一下 19 pip install lxml 20 基础使用 21 使用流程: 22 - 导包:from bs4 import BeautifulSoup 23 - 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容 24 (1)转化本地文件: 25 - soup = BeautifulSoup(open('本地文件'), 'lxml') 26 (2)转化网络文件: 27 - soup = BeautifulSoup('字符串类型或者字节类型', 'lxml') 28 (3)打印soup对象显示内容为html文件中的内容 29 30 基础巩固: 31 (1)根据标签名查找 32 - soup.a 只能找到第一个符合要求的标签 33 (2)获取属性 34 - soup.a.attrs 获取a所有的属性和属性值,返回一个字典 35 - soup.a.attrs['href'] 获取href属性 36 - soup.a['href'] 也可简写为这种形式 37 (3)获取内容 38 - soup.a.string 39 - soup.a.text 40 - soup.a.get_text() 41 【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容 42 (4)find:找到第一个符合要求的标签 43 - soup.find('a') 找到第一个符合要求的 44 - soup.find('a', title="xxx") 45 - soup.find('a', alt="xxx") 46 - soup.find('a', class_="xxx") 47 - soup.find('a', id="xxx") 48 (5)find_all:找到所有符合要求的标签 49 - soup.find_all('a') 50 - soup.find_all(['a','b']) 找到所有的a和b标签 51 - soup.find_all('a', limit=2) 限制前两个 52 (6)根据选择器选择指定的内容 53 select:soup.select('#feng') 54 - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器 55 - 层级选择器: 56 div .dudu #lala .meme .xixi 下面好多级 57 div > p > a > .lala 只能是下面一级 58 【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
案例: 糗事百科----文字首页:https://www.qiushibaike.com/text/
1 # 糗事百科----文字首页:https://www.qiushibaike.com/text/ 2 import requests 3 from bs4 import BeautifulSoup 4 from fake_useragent import UserAgent 5 UA=UserAgent() 6 headers={'User-Agent':UA.random} 7 8 url='https://www.qiushibaike.com/text/' 9 response=requests.get(url,headers=headers) 10 soup=BeautifulSoup(response.text,'lxml')#依赖lxml模块,需提前安装 11 12 content_list=soup.select('.content>span') 13 for i,content in enumerate(content_list): 14 print(i+1,content.text.strip())