在获取到响应数据后,需要针对性的提取其中有用的部分,这也是采集数据最常见的方式聚焦网络爬虫。
数据解析四种方式:
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())