• 数据解析


    数据解析

    • re
    • bs4
    • xpath
    • pyquery
    • 什么是数据解析,数据解析可以干什么?
      • 概念:就是将一组数据中的局部数据进行提取。
      • 作用:使用来实现聚焦爬虫
    • 数据解析的通用原理
      • 问题:html展示的数据可以存储在哪里?
        • 标签之中
        • 属性中
      • 1.标签定位
      • 2.取文本或者取属性

    什么是数据解析及作用

    概念:就是将一组数据中的局部数据进行提取
    作用:来实现聚焦爬虫
    

    数据解析的通用原理

    标签定位
    取文本或者属性
    

    简单爬取一张图片入门

    需求:http://duanziwang.com/category/%E6%90%9E%E7%AC%91%E5%9B%BE/,将该网站中的图片数据进行爬取

    #方法1:
    import requests
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
    }
    url = 'http://duanziwang.com/usr/uploads/2019/02/3334500855.jpg'
    pic_data = requests.get(url=url,headers=headers).content #content返回的是二进制类型的响应数据
    with open('1.jpg','wb') as fp:
        fp.write(pic_data)
        
        
    #方法2:urllib就是一个低版本的requests
    import urllib
    url = 'http://duanziwang.com/usr/uploads/2019/02/3334500855.jpg'
    urllib.request.urlretrieve(url=url,filename='./2.jpg')
    
    • 两种图片爬取的方法的区别是什么?
      • 方法1可以进行UA伪装,方法2不行
    • 抓包工具中response中显示的页面源码和开发者工具的Element选项卡显示的页面源码的区别是什么?
      • Element:显示的页面源码内容是当前网页加载完毕后对应的所有数据(包含动态加载的数据)
      • response:显示的内容仅仅是当前一个请求请求到的数据(不包含动态加载的数据)

    正则解析

    正则回顾

    单字符:
        . : 除换行以外所有字符
        [] :[aoe] [a-w] 匹配集合中任意一个字符
        d :数字  [0-9]
        D : 非数字
        w :数字、字母、下划线、中文
        W : 非w
        s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ f
    
    	v]。
        S : 非空白
            
    数量修饰:
        * : 任意多次  >=0
        + : 至少1次   >=1
        ? : 可有可无  0次或者1次
        {m} :固定m次 hello{3,}
        {m,} :至少m次
        {m,n} :m-n次
            
    边界:
        $ : 以某某结尾 
        ^ : 以某某开头
            
    分组:
        (ab)  
        贪婪模式: .*
        非贪婪(惰性)模式: .*?
    
        re.I : 忽略大小写
        re.M :多行匹配
        re.S :单行匹配
    
        re.sub(正则表达式, 替换内容, 字符串)
    

    正则练习

    import re
    #提取出python
    key="javapythonc++php"
    res = re.findall('python',key)[0]   #re.findall('python',key)返回的结果是列表类型的数据
    print(res)
    
    #提取出hello world
    key="<html><h1>hello world<h1></html>"
    re.findall('<h1>(.*)<h1>',key)[0]
    
    #提取170
    string = '我喜欢身高为170的女孩'
    re.findall('d+',string)
    
    #提取出http://和https://
    key='http://www.baidu.com and https://boob.com'
    re.findall('https?://',key)
    
    #提取出hello
    key='lalala<hTml>hello</HtMl>hahah' #输出<hTml>hello</HtMl>
    re.findall('<[Hh][Tt][mM][lL]>(.*)</[Hh][Tt][mM][lL]>',key)
    
    #提取出hit. 
    key='bobo@hit.edu.com'#想要匹配到hit.
    re.findall('h.*?.',key)
    
    #匹配sas和saas
    key='saas and sas and saaas'
    re.findall('sa{1,2}s',key)
    

    正则爬取

    # 进行全站数据的爬取:爬取所有页码的图片数据
    # 需求的实现
    
    import re
    import os
    import urllib
    import request
    
    # 进行全站数据的爬取:爬取所有页码的图片数据
    # 需求的实现
    
    # 制定一个通用的url模板,不可以被改变
    url = 'http://duanziwang.com/category/搞笑图/%d/'
    
    for page in range(1, 4):#页面规律
        new_url = format(url % page)
        page_text = requests.get(new_url, headers=headers).text  # 页面源码数据
    
        # 新建一个文件夹
        dirName = 'imgLibs'
        if not os.path.exists(dirName):
            os.mkdir(dirName)
    
        # 数据解析:每一张图片的地址
        ex = '<article.*?<img src="(.*?)" alt=.*?</article>'
    
        # 第一个参数是正则 第二参数是被匹配的字符串,第3个参数作用re.S是处理空格的
        img_src_list = re.findall(ex, page_text, re.S)  # 爬虫中使用findall函数必须要使用re.S
    
        for src in img_src_list:
            imgName = src.split('/')[-1]
            imgPath = dirName + '/' + imgName
            urllib.request.urlretrieve(url=src, filename=imgPath)
            print(imgName, '下载成功!!!')
    

    bs4

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

    bs4的基本语法

    基础语法:
        soup = BeautifulSoup(page_text,'lxml')
        (1)根据标签名查找
            - soup.a   只能找到第一个符合要求的标签a标签
        (2)获取属性
            - soup.a.attrs  获取a所有的属性和属性值,返回一个字典
            - soup.a.attrs['href']   获取href属性
            - soup.a['href']   也可简写为这种形式
        (3)获取内容 
            - soup.a.string      获取a标签的直系文本  多
            - soup.a.text     这是属性,获取a子类的所有文本 少
            - soup.a.get_text()  这是方法,获取a标签子类的所有文本
           【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
        (4)find:找到第一个符合要求的标签
            - soup.find('a')  找到第一个符合要求的a标签
            - soup.find('a', title="xxx") 具有title=a属性的且是a标签
            - soup.find('a', alt="xxx") 类推
            - soup.find('a', class_="xxx")
            - soup.find('a', id="xxx")
        (5)find_all:找到所有符合要求的标签
            - soup.find_all('a')
            - soup.find_all(['a','b']) 找到所有的a和b标签
            - soup.find_all('a', limit=2)  限制前两个
        (6)根据选择器选择指定的内容 返回的是列表  
                   select:soup.select('#feng')
            - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
                - 层级选择器:
                    大于号:表示一个层级
                    空格:标识多个层级
                    div .dudu #lala .meme .xixi  下面好多级
                    div > p > a > .lala          只能是下面一级
            select就是css选择器
            【注意】select选择器返回永远是列表,需要通过索引提取指定的对象
    

    select 和 find 和findall

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html, ‘lxml‘)
    s = soup.select(‘div .lily‘)#select的写法和find有区别,select是标签和class都在一个字符串里,find是两个字符串,用逗号隔开
    f = soup.find(‘div‘,class_ = ‘lily‘) #find只取第一个值,返回的是字符串
    fa = soup.find_all(‘div‘,class_ = ‘lily‘)#find——all是全部的值和select一样,是一个列表
    fal = soup.find_all(‘div‘,class_ = ‘lily‘,limit=1)#find——all是全部的值和select一样,是一个列表,加limit属性后只返回第一个
    print(s)
    print(f)
    print(fa)
    print(fal)
    >>>
    
    [<div class="lily" id="ben">大笨蛋</div>, <div class="lily" id="ben">是个大笨蛋吗?</div>]
    <div class="lily" id="ben">大笨蛋</div>
    [<div class="lily" id="ben">大笨蛋</div>, <div class="lily" id="ben">个大笨蛋吗?</div>]
    [<div class="lily" id="ben">大笨蛋</div>]
    
    属性定位:soup.find('tagName',attrName='value'),返回也是单数
            find_all:和find用法一致,但是返回值是列表
                
    1. name参数的四种过滤器
      soup=Beautifulsoup('page','lxml')
        不带过滤器: print(soup.find_all())  #没有过滤,查找所有标签
        字符串过滤器: print (soup.find_all())  #字符串过滤器,即标签名
        列表: print(soup.find_(['a','b'])) #找到所有的a标签和b标签,任一即可
        正则: print(soup.find_all(re.complie('^b'))) #找到所有b开头的标签
        方法: def has_class_but_no_id(tag):
             return tag.has_attr('class') and not tag.has_attr('id')
             print(soup.find_all(has_class_but_no_id))
    2、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
        print(soup.find_all('a',class_='sister')) #查找类为sister的a标签 
        print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功 
        print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签
    
    3、attrs 
        print(soup.find_all('p',attrs={'class':'story'}))
    
    4、text: 值可以是:字符,列表,True,正则 
        print(soup.find_all(text='Elsie')) 
        print(soup.find_all('a',text='Elsie'))  
    
    5、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,
        当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
        print(soup.find_all('a',limit=2))
    
    
    6、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
        print(soup.html.find_all('a'))
        print(soup.html.find_all('a',recursive=False))
    
    7.  find和find_all一样
    

    爬取三国演义的章节信息和文章内容

    分析:

    1.先获取三国演义的主页面,里面包含了三国演义的文章章节标题,每个文章的章节都是一个a标签,访问这个a标签,就能查看文章的内容
    2.发送请求,请求三国演义的主界面
    3.在三国演义的主页面的html源码中找到章节的标签位置,定位标签位置
    4.拿到列表数据,循环列表,循环发送章节的内容的请求
    import requests
    from bs4 import BeautifulSoup   # 导入BeautifulSoup
    url = 'http://www.shicimingju.com/book/sanguoyanyi.html'  # 
    headers={
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
    }
    sg_list = requests.get(url=url,headers=headers).text
    soup = BeautifulSoup(sg_list,'lxml')
    content = soup.select('.book-mulu > ul > li > a')  #章节的标签
    f = open('sanguo.txt','w',encoding='utf-8')
    for i in content:
        new_url = 'http://www.shicimingju.com'+i['href']   #拼接标签的访问路径
        title = i.string
        detail = requests.get(url=new_url,headers=headers).text  #循环发送对文章内容的请求
        soup = BeautifulSoup(detail,'lxml')
        new_detail = soup.find('div',class_="chapter_content").text
        f.write(new_detail)
        print(title+'爬取成功')
    f.close()
    

    xpath

    • 环境的安装:

      • pip install lxml
    • 解析原理(流程)

      • 实例化一个etree的对象,将解析的数据加载到该对象中
      • 需要调用etree对象中的xpath方法结合着不同的xpath表达式进行标签定位和文本数据的提取
    • etree对象的实例化

      • etree.parse('filePath'):将本都数据加载到etree中
      • etree.HTML(page_text):将互联网上的数据加载到该对象中
    • html中所有的标签都是遵从了树状的结构,便于我们实现高效的节点的遍历或者查找(定位)

    • xpath方法的返回值一定是复数(列表)

    • 标签定位

      • 最左侧的/:xpath表达式式一定要从根标签开始进行定位
      • 非最左侧的/:表示一个层级
      • 最左侧的//:从任意位置进行标签定位(常用)
      • 非最左侧//:表示多个层级
      • //tagName:定位到所有的tagName标签
      • 属性定位://tagName[@attrName="value"]
      • 索引定位://tagName[index],index索引是从1开始
      • 模糊匹配:
        • //div[contains(@class, "ng")]
        • //div[starts-with(@class, "ta")]
    • 取文本

      • /text():取直系的文本内容。列表只有一个元素
      • //text():所有的文本内容。列表会有多个列表元素
    • 取属性

      • /@attrName

    xpath安装及基本语法

    环境的安装:pip install lxml
    
    xpath的解析原理
        实例化一个etree类型的对象,且将页面源码数据加载到该对象中
        需要调用该对象的xpath方法结合着不同形式的xpath表达式进行标签定位和数据提取
            
    etree对象的实例化
        etree.parse(fileNane) #本地的html文件
        etree.HTML(page_text) #声明了一段HTML文本,调用HTML类进行初始化,构造了一个XPath解析对象
        xpath方法返回的永远是一个列表
        
    标签定位
        xpath表达式中最最侧的/表示的含义是说,当前定位的标签必须从根节点开始进行定位
        xpath表达式中最左侧的//表示可以从任意位置进行标签定位
        xpath表达式中非最左侧的//表示的是多个层级的意思
        xpath表达式中非最左侧的/表示的是一个层级的意思
        
    属性定位
        //标签名[@arrtName='value']
        循环中标签定位: ./表示当前标签
        索引定位://标签名/li[3] #第三个li标签
    
    提取数据
        取文本:
            /text():取直系的文本内容
            //text():取所有的文本内容,循环中不能再用索引,例如文本中有br标签分割
        取属性:
            tag/@attrName
    
            
    举例:      
    from lxml import etree
    tree = etree.parse('./test.html')
    tree.xpath('/html/head/meta')[0] #绝对路径
    tree.xpath('//meta')[0] #相对路径,将整个页面源码中所有的meta进行定位
    tree.xpath('/html//meta')[0] 
    
    #属性定位
    tree.xpath('//div[@class="song"]')
    
    #索引定位
    tree.xpath('//div[@class="tang"]/ul/li[3]') #该索引是从1开始
    tree.xpath('//div[@class="tang"]//li[3]') #该索引是从1开始
    
    #取文本
    tree.xpath('//p[1]/text()')
    tree.xpath('//div[@class="song"]//text()')
    
    #取属性
    tree.xpath('//a[@id="feng"]/@href')
    

    爬取boss的招聘信息

    爬取的内容:岗位名称,公司名称,薪资,岗位描述
    url = 'https://www.zhipin.com/job_detail/?  query=python%E7%88%AC%E8%99%AB&city=101010100&industry=&position='   # python爬虫岗位的url
    headers={
        'cookie': '_uab_collina=157338945037017905889165; __c=1573389450; __g=-; __l=l=%2Fwww.zhipin.com%2Fweb%2Fcommon%2Fsecurity-check.html%3Fseed%3Dl%252FiZaxWImFKXsBkSlPZFk9r1hTxzYO%252BbuuzP3sRZC3A%253D%26name%3Dcb43d3e3%26ts%3D1573389319454%26callbackUrl%3D%252Fjob_detail%252F%253Fquery%253Dpython%2525E7%252588%2525AC%2525E8%252599%2525AB%2526city%253D101010100%2526industry%253D%2526position%253D%26srcReferer%3D&r=&friend_source=0&friend_source=0; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1573389451,1573390721; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1573392780; __zp_stoken__=48121bb20qltoY5SdLcZPo5yn7gLjgHQMQN16Gyq4%2B26dpVXYTOQYI8oCaMpwStY3B5JC%2Fg5rjYSnOw4oEuk5fSh4A%3D%3D; __a=96130844.1573389450..1573389450.7.1.7.7',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'
    }
    #cookies反爬机制,cookies有可能随时变化
    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)#job_desc获取列表数据,通过join变成字符串形式的数据
        
        print(job_title,salary,company,job_desc)
    

    爬取糗事百科的段子内容和作者名称

    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 = 'https://www.qiushibaike.com/text/page/4/'
    page_text = requests.get(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]
        # 糗事百科中有作者和段子内容,作者分为实名用户和匿名用户,但通过对糗事百科的源码,当是匿名用户的时候,文本内容就取不到,所以返回None,但是爬取到的内容也就是None,不是想要的结果,解决:  ./div[1]/a[2]/h2/text()取实名用户,./div[1]/span[2]/h2/text()取匿名用户
        content = div.xpath('.//div[@class="content"]/span//text()')
        content = ''.join(content)
        print(author,content)
        
    

    爬取糗事百科笑话的标题和内容

    http://www.lovehhy.net/Joke/Detail

    from lxml import etree
    import requests
    url = 'http://www.lovehhy.net/Joke/Detail/QSBK/'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
        'Cookie':'bdshare_firstime=1573442373487; ASP.NET_SessionId=0yoewt3nnhet3apass0u4hj5; ASPSESSIONIDQADBDDCA=AAKLABFACJHJNHHCMCKEJGJB; __51cke__=; Hm_lvt_03da7ad267ef3d61ce133d6c12f67140=1573442375,1573478536; ASPSESSIONIDSACCBCCA=BCOLDPEALOKMHFJJMHODNHGB; Hm_lpvt_lovehhy=1573479577; Hm_lvt_lovehhy=1573479577; Hm_lpvt_03da7ad267ef3d61ce133d6c12f67140=1573479707; __tins__2343955=%7B%22sid%22%3A%201573478536404%2C%20%22vd%22%3A%2011%2C%20%22expires%22%3A%201573481507039%7D; __51laig__=11'
    }
    joke_text = requests.get(url=url,headers=headers).text
    tree = etree.HTML(joke_text)
    url_text = tree.xpath('//*[@id="footzoon"]/h3/a/@href')
    url_text
    for i in url_text:
        qiu_url = 'http://www.lovehhy.net' + i
        content_text = requests.get(url=qiu_url,headers=headers).text
        tree = etree.HTML(content_text)
        title = tree.xpath('//*[@id="read"]/h1/text()')[0]
        content = tree.xpath('//*[@id="fontzoom"]/text()')[0]
        print(title,content)
    

    正则 bs4与xpath对比

    xpath
    	树
    bs4
    	实列化
    

    1583224090142

  • 相关阅读:
    vue 2.x 之父组件修改子组件变量
    vue 2.x 之滚动到页面底部加载数据
    sass中calc操作变量失效
    VsCode 同步配置到Gitee(码云)云端之code settings sync 插件
    VSCode常用插件之code settings sync使用
    Vue开发规范
    vue-router 3.x(路由)详细教程
    java设计模式之十桥接模式(Bridge)
    java设计模式之九外观模式(Facade)
    装饰模式与代理模式的区别(转载)
  • 原文地址:https://www.cnblogs.com/saoqiang/p/12403734.html
Copyright © 2020-2023  润新知