数据解析

何为数据解析
  • 概念:就是将爬取到数据中局部的指定的数据进行提取
  • 作用:实现聚焦爬虫
  • 数据解析通用原理:
    • html是用来展示数据,html中展示的数据正是我们要爬取或者采集的数据
    • html所展示的数据/想要爬取的数据只会存在于标签之间或者标签的属性中
    • 数据解析的通用原理:
      • 标签定位
      • 提取标签中存储的数据
  • 聚焦爬虫编码流程
    • 指定url
    • 发起请求
    • 获取响应数据
    • 数据解析
    • 持久化存储
数据解析的主流策略
  • bs4(重点)

    • 环境安装:pip install bs4

    • bs4数据解析的流程

      • 1.实例化一个BeautifulSoup的对象,然后把即将被解析的页面源码数据加载到该对象中
        • BeautifulSoup(fp,‘lxml’):fp表示本地的一个文件,该种方式是将本地存储的html文件进行数据解析
        • BeautifulSoup(page_text,‘lxml’):page_text是网络请求到的页面源码数据,该种方式是直接将网络请求到的页面源码数据进行数据解析
      • 2.调用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')
          #参数1:被解析的html页面源码文件,参数2:表示lxml解析器
          soup = BeautifulSoup(fp,'lxml')
          #标签定位1:soup.tagName,注意:只可以定位到第一次出现的tagName标签
          tag = soup.div
          #标签定位2:属性定位  find(tagName,attrName='attrValue')
          #注意:find函数也是只可以定位到满足条件的第一个标签
          tag = soup.find('div',class_='song') #定位到class属性值为song的div标签
          tag = soup.find_all('a',class_='du')
          #标签定位3:选择器(css)定位
              #class选择器:.du(class的属性值为du的标签)
              #id选择器:#feng(id属性值为feng的标签)
              #注意:在html的标签中,可以有多个标签拥有相同的class属性值,但是id属性值对于标签得是唯一的
              #层级选择器:
          tag = soup.select('.du') #定位到class属性值为du的所有标签
          tag = soup.select('#feng') #定位到id属性值为feng的所有标签
          tag = soup.select('ul > li > a') #此处的>表示一个嵌套层级
          tag = soup.select('ul a') #此处的空格表示一个或者多个嵌套层级
          
          #提取数据(标签中间的数据)
          tag = soup.title #定位标签
          text = tag.string #提取标签的直系文本内容
          
          tag = soup.find('div',class_='song')
          text = tag.text #可以提取到定位标签下所有的文本内容
          
          #提取数据(标签属性中的数据)
          tag = soup.img
          text = tag['src'] #tag['AttrName']
          print(text)
          
    • 案例应用:小说批量爬取

      • url:https://www.shicimingju.com/book/sanguoyanyi.html

      • 需求:将每一个章节的标题和内容进行爬取然后存储到一个文件中

        • 步骤:
          • 1.请求主页的页面源码数据
          • 2.数据解析:
            • 章节标题
            • 章节详情页的链接
          • 3.解析章节详细内容
          • 4.将解析的章节标题和内容进行存储
      • #需求:将每一个章节的标题和内容进行爬取然后存储到一个文件中
        #网址:https://www.shicimingju.com/book/sanguoyanyi.html
        import requests
        from bs4 import BeautifulSoup
        
        #1.指定url(小说首页的url)
        main_url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
        #2.发起请求,获取响应对象
        headers = { #对爬虫的请求进行浏览器的伪装
            'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
        }
        response = requests.get(url=main_url,headers=headers)
        response.encoding = 'utf-8'
        #3.获取响应数据
        page_text = response.text
        
        #4.数据解析:章节名称  + 详情页的url
        soup = BeautifulSoup(page_text,'lxml')
        #标签定位:定位到了符合该层级关系所有的a标签
        a_list = soup.select('.book-mulu > ul > li > a')
        #创建一个文件
        fp = open('sanguo.txt','w')
        for a in a_list:
            title = a.string #章节标题
            #完整的详情页的url
            detail_url = 'https://www.shicimingju.com'+a['href']
            #对详情页的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')
            #select返回的是列表
            p_tag = detail_soup.select('.chapter_content')[0]
            #章节内容
            content = p_tag.text
            #持久化存储
            fp.write(title+':'+content+'\n')
            print(title,':爬取保存成功!')
        fp.close()
        
    • 案例应用:代理批量爬取

      • url:https://www.kuaidaili.com/free

      • 需求:将前5页的所有id和port解析且存储到文件中

      • #只爬取了第一页的内容
        from bs4 import BeautifulSoup
        import requests
        headers = {
            'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
        }
        url = 'https://www.kuaidaili.com/free'
        page_text = requests.get(url=url,headers=headers).text
        soup = BeautifulSoup(page_text,'lxml')
        trs = soup.select('tbody > tr')
        for tr in trs:
            t1 = tr.findAll('td')[0]
            t2 = tr.findAll('td')[1]
            ip = t1.string
            port = t2.string
            print(ip,port)
        
        
    • #爬取多页内容
      from bs4 import BeautifulSoup
      import requests
      import time
      headers = {
          'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
      }
      #爬取多页
      #1.创建一个通用的url(可以变换成任意页码的url)
      url = 'https://www.kuaidaili.com/free/inha/%d/'
      #2.通过循环以此生成不同页码的url
      for page in range(1,11):
          print('----------正在爬取第%d页的数据!-----------'%page)
          #format用来格式化字符串的(不可以修改url这个字符串本身)
          new_url = format(url%page)
          #循环发送每一页的请求
          #注意:get方法是一个阻塞方法!
          page_text = requests.get(url=new_url,headers=headers).text
          time.sleep(1)
          soup = BeautifulSoup(page_text,'lxml')
          trs = soup.select('tbody > tr')
          for tr in trs:
              t1 = tr.findAll('td')[0]
              t2 = tr.findAll('td')[1]
              ip = t1.string
              port = t2.string
              print(ip,port)
      
  • xpath(重点)

    • 环境安装:pip install lxml

    • xpath解析的编码流程

      • 1.创建一个etree类型的对象,然后把即将被解析的页面源码数据加载到该对象中
      • 2.调用etree对象的xpath方法结合着不同形式的xpath表达式,进行标签定位和数据提取
    • xpath表达式如何理解?

      • html中的标签是遵从树状结构的。

      • 切记:xpath表达式中不可以出现tbody标签,如果有直接将其删除跨过即可!

      • from lxml import etree #如果这种方式报错,使用下面方式导入etree
        # from lxml.html import etree
        fp = open('test.html','r')
        #1.将本地存储好的文件中的数据加载到etree对象中进行数据解析
        tree = etree.parse(fp)
        #2.调用etree对象的xpath方法结合不同形式的xpath表达式进行标签定位和数据提取
        #xpath返回的一定是列表,列表中存储的是定位到的标签对象
        # title_tag = tree.xpath('/html/head/title')
        # title_tag = tree.xpath('/html//title')
        # title_tag = tree.xpath('//head/title')
        # title_tag = tree.xpath('//title') #推荐
        #最左侧为/:表示必须从树的根标签(html标签)开始进行定位
        #最左侧为//:可以从任意位置进行标签的相对位置定位
        #非最左侧的/:表示一个层级
        #非最左侧的//:表示多个层级
        # tag = tree.xpath('//div') #定位所有的div标签
        
        #属性定位:根据标签的属性定位标签
        #//tagName[@attrName="attrValue"]
        # tag = tree.xpath('//div[@class="song"]')#定位class属性值为song的div标签
        # tag = tree.xpath('//a[@id="feng"]')
        # tag = tree.xpath('//div[@class="tang"]/ul/li/a[@id="feng"]')
        #索引定位:索引是从1开始的
        # tag = tree.xpath('//div[@class="tang"]/ul/li[3]')#定位到第三个li标签
        #获取定位到标签中的文本内容
            # /text()获取标签中直系的文本内容:返回的列表中只会有一个列表元素
            # //text()获取标签中所有的文本内容:通常返回列表中存在多个元素
        # tag = tree.xpath('//div[@class="song"]/p[3]/text()')
        # tag = tree.xpath('//div[@class="song"]//text()')
        #获取定位到标签中的属性值://tag/@attrName
        tag = tree.xpath('//img/@src')
        print(tag)
        
    • 如何爬取多媒体资源(图片,音频,视频,动图)

      • 方式1:编写麻烦,但是可以实现UA伪装

        • import requests
          headers = {
              'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36X-Requested-With: XMLHttpRequest'
          }
          #获取图片地址:通常为img标签的src属性值
          img_src = 'http://img.itlun.cn/uploads/allimg/180506/1-1P5061TS6-lp.jpg'
          #对图片发起请求
          response = requests.get(url=img_src,headers=headers)
          #获取图片数据:content返回的是二进制形式的响应数据
          img_data = response.content
          #持久化存储
          with open('./123.jpg','wb') as fp:
              fp.write(img_data)
          
      • 方式2:编写简单,但是无法实现UA伪装

        • from urllib import request
          
          img_src = 'http://img.itlun.cn/uploads/allimg/180506/1-1P5061TS6-lp.jpg'
          #urlretrieve可以直接对图片发起请求,且将请求到的图片数据进行持久化存储
          request.urlretrieve(img_src,'./456.jpg')
          
    • 案例应用:

      • 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 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
          }
          
          url = 'https://pic.netbian.com/4kmeinv/index.html'
          response = requests.get(url=url,headers=headers)
          response.encoding = 'gbk'
          page_text = response.text
          
          #数据解析:图片地址+图片名称
          tree = etree.HTML(page_text)#HTML()专门用来解析网络请求到的页面源码数据
          #该列表中存储的是每一个li标签
          li_list = tree.xpath('//div[@class="slist"]/ul/li')
          for li in li_list:
              #局部解析:将li标签中指定的内容解析出来
              img_title = li.xpath('./a/b/text()')[0]+'.jpg'# 左侧./表示xpath的调用者对应的标签
              img_src = 'https://pic.netbian.com'+li.xpath('./a/img/@src')[0]
          
              #对图片发起请求,存储图片数据
              img_data = requests.get(url=img_src,headers=headers).content
              # girls/123.jpg
              img_path = dirName + '/' + img_title
              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 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
          }
          #创建一个通用的url:除了第一页其他页码的通用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/index.html'
              else:
                  new_url = format(url%page)
              print('----------正在请求下载第%d页的图片数据----------'%page)
              response = requests.get(url=new_url,headers=headers)
              response.encoding = 'gbk'
              page_text = response.text
          
              #数据解析:图片地址+图片名称
              tree = etree.HTML(page_text)#HTML()专门用来解析网络请求到的页面源码数据
              #该列表中存储的是每一个li标签
              li_list = tree.xpath('//div[@class="slist"]/ul/li')
              for li in li_list:
                  #局部解析:将li标签中指定的内容解析出来
                  img_title = li.xpath('./a/b/text()')[0]+'.jpg'# 左侧./表示xpath的调用者对应的标签
                  img_src = 'https://pic.netbian.com'+li.xpath('./a/img/@src')[0]
          
                  #对图片发起请求,存储图片数据
                  img_data = requests.get(url=img_src,headers=headers).content
                  # girls/123.jpg
                  img_path = dirName + '/' + img_title
                  with open(img_path,'wb') as fp:
                      fp.write(img_data)
                  print(img_title,'下载保存成功!')
          
      • https://www.aqistudy.cn/historydata/

        • 爬取热门城市和全部城市的名称

        • 该网址不是安全链接,因此需要verify=False关闭安全认证

        • #第一种写法
          from lxml import etree
          import requests
          
          headers = {
              'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
          }
          url = 'https://www.aqistudy.cn/historydata/'
          page_text = requests.get(url=url,headers=headers,verify=False).text
          tree = etree.HTML(page_text)
          #解析热门城市
          hot_li_list = tree.xpath('//div[@class="bottom"]/ul/li')
          for li in hot_li_list:
              city_name = li.xpath('./a/text()')[0]
              print(city_name)
          
          #解析全部城市
          all_li_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
          for li in all_li_list:
              city_name = li.xpath('./a/text()')[0]
              print(city_name)
          
        • #第二种写法
          from lxml import etree
          import requests
          
          headers = {
              'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
          }
          url = 'https://www.aqistudy.cn/historydata/'
          page_text = requests.get(url=url,headers=headers,verify=False).text
          tree = etree.HTML(page_text)
          #解析热门城市+所有城市
          #此处xpath表达式的管道符(|)可以是的xpath表达式更加具有通用性
          li_list = tree.xpath('//div[@class="bottom"]/ul/li | //div[@class="bottom"]/ul/div[2]/li')
          for li in li_list:
              city_name = li.xpath('./a/text()')[0]
              print(city_name)
          
          
          
      • 作业练习:https://sc.chinaz.com/jianli/free.html

        • 下载当前页所有的建立模板

          • 简历名称+简历的下载链接
          • 根据简历的下载链接 下载简历文件
          • 根据下载地址下载的压缩包,压缩包是二进制的数据

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部