• python爬虫入门 之 数据解析


    第四章.数据解析

    • 解析 :根据指定的规则对数据进行提取

    • 作用 :实现聚焦爬虫

    • 聚焦爬虫编码流程:

      1.指定url
      2.发起请求
      3.获取响应数据
      4.数据解析
      5.持久化存储

    4.1数据解析通用原理

    • 数据解析作用地点

      • 页面源码(一组html标签组成的)

    • html标签核心作用

      • 用于展示数据

    • html是如何展示数据的

      • html所要展示的数据一定是被放置在html标签中,或者是在属性中

    • 通用原理 : 1.标签定位. 2.取文本或取属性

    4.2四种数据解析的方式

    4.2.1 正则

    • 需求 : 爬取xx百科中糗图数据

      链接地址 : https://www.qiushibaike.com

      两种爬取方式

      #方式一:
      import requests
      #即将发起请求对应的头信息
      headers = {
         "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
      }
      url = "https://pic.qiushibaike.com/system/pictures/12217/122176374/medium/XBOP3Y2YQM1SEEXA.jpg"
      img_data = requests.get(url=url,headers=headers).content    #content返回的是 byte 类型的数据
      with open("./123.jpg",'wb') as f:
         f.write(img_data)
      #方式二:
      from urllib import request
      url = "https://pic.qiushibaike.com/system/pictures/12217/122176374/medium/XBOP3Y2YQM1SEEXA.jpg"
      request.urlretrieve(url,"./456.jpg")
      • 方式一 和 方式二 对于图片操作最大的不同之处是什么?

        • 方式二不可以使用UA伪装的机制

      单页数据的爬取

      #糗事百科
      import re
      import os
      from urllib import request

      dir_name = "./qiutu"
      if not os.path.exists(dir_name):
         os.mkdir(dir_name)

      url = "https://www.qiushibaike.com/pic/"
      page_text = requests.get(url=url,headers=headers).text
      #数据解析,图片地址
      #正则表达式
      ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
      img_src_list = re.findall(ex,page_text,re.S)   # re.S单行匹配
      for src in img_src_list:
         src = "https:" + src
         img_name = src.split("/")[-1]
         #图片存储地址
         img_path = dir_name + "/" + img_name
         #对图片地址单独发起请求获取文件数据
         request.urlretrieve(src,img_path)
         print(img_name,"下载成功")

      爬取分页 分析每个页码对应的url是有共性的:https://www.qiushibaike.com/pic/page/3/?s=5222981

      #糗事百科
      import re
      import os
      from urllib import request

      dir_name = "./qiutumany"
      if not os.path.exists(dir_name):
         os.mkdir(dir_name)

      #指定一个通用模板(不可变)
      url = "https://www.qiushibaike.com/pic/page/%d/"

      for page in range(1,5):
         print("正在打印第{}页的数据".format(page))
         #形成某页码完整url
         new_url = format(url%page)
         page_text = requests.get(url=new_url,headers=headers).text
         #数据解析,图片地址
         #正则表达式
         ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
         img_src_list = re.findall(ex,page_text,re.S)   # re.S单行匹配
         for src in img_src_list:
             src = "https:" + src
             img_name = src.split("/")[-1]
             #图片存储地址
             img_path = dir_name + "/" + img_name
             #对图片地址单独发起请求获取文件数据
             request.urlretrieve(src,img_path)

    模块 :urllib

    • urllib就是一个比较老的网络请求模块,在requests没出现之前,请求发送的都是urllib

    4.2.2 bs4

    #环境的安装
    pip install bs4
    pip install lxml
    #解析原理
    1.实例化一个BeautifulSoup对象,并且将即将被解析的页面源码数据加载到该对象中
    2.调用BeautifulSoup对象的相关属性和方法来进行标签定位和数据提取
    #如何实例化BeautifulSoup对象
    BeautifulSoup(fp,"lxml")    #专门用于解析本地存储的html文档中的数据
    BeautifulSoup(page_text,"lxml")  #专门用于将互联网请求到的页面源码数据进行解析

    标签定位

    soup.tagName   : 定位到第一个tagName标签,返回的是单数
    #属性定位:
    soup.find(tagName,attrName="value")  返回的是单数
       soup.find_all(tagName,attrName="value")  返回的是列表
    #选择器定位:
    select("选择器")  返回的是列表
    标签,类,i, 层级   > :一个层级  空格:多个层级  
    from bs4 import BeautifulSoup
    fp = open("./test.html",'r',encoding='utf-8')
    soup = BeautifulSoup(fp,"lxml")    #将即将被解析的页面源码加载到对象中
    soup.p    #<p>百里守约</p>
    #find用于属性定位
    soup.find("div")      
    soup.find("div",class_="song")
    soup.find_all("div",class_="song")
    soup.select(".tang")
    soup.select("#feng")
    soup.select(".tang > ul > li ")
    li_6 = soup.select(".tang > ul > li")[6]    #<li><i>度蜜月</i></li>
    li_6.i          #<i>度蜜月</i>
    li_6.i.text     #'度蜜月'
    li_6.i.string   #'度蜜月'
    soup.find("a",id="feng")
    soup.find("a",id="feng")["href"]
    • soup.find("div")

     

    • soup.find("div",class_="song")_

       

    • soup.find_all("div",class_="song")

       

    • 类选择器 :soup.select(".tang")

       

    • id选择器 :soup.select("#feng")

       

    • 层级选择器 : soup.select(".tang > ul > li ")

       

    • soup.find("a",id="feng")

    • soup.find("a",id="feng")["href"]

    取数据

    #取文本
    tag.string   标签中直系的文本内容
        tag.text     标签中所有文本内容  
    #取属性
    • 需求:爬取三国演义整篇小说内容

      import requests
      #在首页中解析章节名称和每个章节详情页的url
      url = "http://www.shicimingju.com/book/sanguoyanyi.html"
      headers = {
         "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
      }
      page_text = requests.get(url=url,headers=headers).text
      page_text
      soup = BeautifulSoup(page_text,"lxml")
      a_list = soup.select(".book-mulu > ul > li > a")
      fp = open("sanguo.txt",'w',encoding="utf-8")
      for a in a_list:
         detail_url = "http://www.shicimingju.com"+ a["href"]
         title = a.text
         #对章节详情页的url发起请求,解析详情页的章节内容
         detail_page_text = requests.get(url=detail_url,headers=headers).text
         soup = BeautifulSoup(detail_page_text,"lxml")
         chap_content = soup.find("div",class_="chapter_content").text
         fp.write(title + ":"+chap_content)
         print(title,"爬取成功")
      fp.close()

    4.2.3 xpath

    #环境的安装: pip install lxml
    #xpath解析原理:
    1.实例化一个etree类型的对象,且将页面源码加载到该对象中
       2.需要调用该对象的xpath方法结合不同形式的xpath表达式进行标签定位和数据提取
    #etree对象的实例化
    etree.parse(fileName) #用于解析本地存储的html文档中的数据
    etree.HTML(page_text) #用于将互联网请求到的页面源码数据进行解析
    #xpath返回的永远是一个列表

    标签定位

    1.xpath表达式中"最"最左边的"/"表示的含义是,当前定位的标签必须从根节点开始进行定位
    2.xpath表达式中"最"最左边的"//"表示可以从任意位置进行标签定位
    3.xpath表达式中"非"最左边的"/"表示的是一个层级
    4.xpath表达式中"非"最左边的"//"表示的是多个层级
    #属性定位:
    //tageName[@class='value']
    #索引定位:

    提取数据

    #取文本
    /text()   取直系的文本
       //text()   取所有文本内容
    #取属性
    tag/@attrName
    from lxml import etree
    tree = etree.parse("./test.html")
    tree.xpath("/html/head/meta")   #[<Element meta at 0x84fbb08>] -->绝对路径
    tree.xpath("//meta")[0]         #[<Element meta at 0x8523748>] -->相对路径,将页面中所有的meta进行定位
    tree.xpath("/html//meta")[0]  
    #属性定位
    tree.xpath('//div[@class="song"]')
    #索引定位
    tree.xpath('//div[@class="tang"]/ul/li[3]')  #该索引从 1 开始
    #取文本
    tree.xpath('//p[1]/text()')   #['百里守约', '李清照']
    tree.xpath('//div[@class="song"]//text()')
    #取属性  
    tree.xpath('//a[@id="feng"]/@href')  #['http://www.haha.com']
    • 需求 :爬取xx直聘招聘信息

      • 岗位名称

      • 公司名称

      • 薪资

      • 岗位描述

      import requests
      from lxml import etree
      headers = {
         'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
         'cookie':'lastCity=101010100; __c=1566877560; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1566877561; _uab_collina=156687756118178796315757; __l=l=%2Fwww.zhipin.com%2F&r=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DidbSvNzz2fLSl1WXiEmtINauVHUZYSNqejHny725pc5RTwaHqh5uDx1LewpyGmaT%26wd%3D%26eqid%3Dbadf667700040677000000025d64a772&friend_source=0&friend_source=0; __zp_stoken__=91d9QItKEtUk5dMMnDG7lwzq8mBW1g%2FkEsFOHXIi%2FwMd%2FPRRXc%2FPMKjsDYwsfC4b7vAT3FVnTmYBjGp8gW1OeZ5TdA%3D%3D; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1566879753; __a=69160831.1566877560..1566877560.16.1.16.16'
      }
      url = 'https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&city=101010100&industry=&position='
      page_text = requests.get(url,headers=headers).text
      #数据解析
      tree = etree.HTML(page_text)
      li_list = tree.xpath('//div[@class="job-list"]/ul/li')
      for li in li_list:
      #     需要将li表示的局部页面源码数据中的相关数据进行提取
      #     如果xpath表达式被作用在了循环中,表达式要以./或者.//开头
         detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/h3/a/@href')[0]
         job_title = li.xpath('.//div[@class="info-primary"]/h3/a/div/text()')[0]
         salary = li.xpath('.//div[@class="info-primary"]/h3/a/span/text()')[0]
         company = li.xpath('.//div[@class="info-company"]/div/h3/a/text()')[0]
         #对详情页的url发请求解析出岗位职责
         detail_page_text = requests.get(detail_url,headers=headers).text
         tree = etree.HTML(detail_page_text)
         job_desc = tree.xpath('//div[@class="text"]//text()')
         job_desc = ''.join(job_desc)
         
         print(job_title,salary,company,job_desc)
    • 需求 :爬取xx百科的段子内容和作者名称

      链接地址 :https://www.qiushibaike.com/text/page/3/

      import requests
      from lxml import etree

      headers = {
         "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
      }
      url = "https://www.qiushibaike.com/text/page/4/"
      page_text = requests.get(url=url,headers=headers).text
      tree = etree.HTML(page_text)
      div_list = tree.xpath("//div[@id='content-left']/div")
      for div in div_list:
         author = div.xpath("./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()")[0]
         content = div.xpath("./a[1]/div[@class='content']/span//text()")
         content = "".join(content)
         print(author,content)
      #总结:
      另一种较为通用的xpath表达式的使用形式: #管道符"|"在xpath表达式中的应用
         #用处 : 用于解析不规则布局的页面数据

    中文乱码处理问题

    import requests
    from lxml import etree

    headers = {
       'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
    }
    #指定一个通用的url模板
    url = 'http://pic.netbian.com/4kmeishi/index_%d.html'

    for page in range(1,3):
       if page == 1:
           new_url = 'http://pic.netbian.com/4kmeishi/'
       else:
           new_url = format(url%page)
       response =  requests.get(new_url,headers=headers)
       #response.encoding = 'utf-8'
       page_text = response.text
       tree = etree.HTML(page_text)
       li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li')
       for li in li_list:
           img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
           img_name = li.xpath('./a/b/text()')[0]
           img_name = img_name.encode('iso-8859-1').decode('gbk')

    ConnectionPool错误

    #原因一.在短时间内向网站发起了一个高频的请求
    解决方式 :使用代理
    #原因二.连接池(http)中的资源被耗尽
    立即将请求断开 Connection:close

    xpath和bs4最明显的区别

    #解决除携带标签的局部内容
    bs4相关标签定位的方法或属性 返回值就是携带标签的内容

    反爬机制之三 :图片懒加载

    在img标签应用了伪属性

    4.2.4pyquery(扩展)

  • 相关阅读:
    map用法详解
    求用1,2,5这三个数不同个数组合的和为100的组合个数
    【雅虎笔试题】两个已经排好序的数组,找中位数
    C++ STL算法系列4---unique , unique_copy函数
    C++ STL算法系列3---求和:accumulate
    C++ STL算法系列2---find ,find_first_of , find_if , adjacent_find的使用
    C/C++面试小知识点
    C语言内存地址基础
    C语言函数指针基础
    C++ STL算法系列1---count函数
  • 原文地址:https://www.cnblogs.com/lilinyuan5474/p/11497938.html
Copyright © 2020-2023  润新知