zoukankan      html  css  js  c++  java
  • doraemon的python 爬虫(数据解析——正则、bs4、xpath)

    用法解析和示例

    ### 3.数据解析(xpath、bs4、正则)
    
    **数据解析**
    
    - 解析:根据指定的规则对数据进行提取
    - 作用:实现聚焦爬虫
    - 聚焦爬虫的编码流程:
      - 指定url
      - 发起请求
      - 获取响应数据
      - 数据解析
      - 持久化存储
    - 数据解析的方式:
      - 正则
      - bs4
      - xpath
      - pyquery(拓展)
    - 数据解析的通用原理是什么?
      - 数据解析需要作用在页面源码中(一组html标签组成的)
      - html的核心作用是什么?
        - 展示数据
      - html是如何展示数据的呢?
        - html所要展示的数据一定是被放置在html标签之中,或者是在属性中.
      - 通用原理:
        - 1.标签定位
        - 2.取文本or取属性
    
    #### 3.1 正则实现的数据解析
    
    - 需求:爬取糗事百科中糗图数据
    
    - 如何爬取图片数据
    
    ```python
    import requests
    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'
    }
    ```
    
    ```python
    #方式1:
    url = 'https://pic.qiushibaike.com/system/pictures/12217/122176396/medium/OM37E794HBL3OFFF.jpg'
    img_data = requests.get(url=url,headers=headers).content #content返回的是byte类型的数据
    with open('./123.jpg','wb') as fp:
        fp.write(img_data)
    ```
    
    ```python
    #方式2:
    from urllib import request
    url = 'https://pic.qiushibaike.com/system/pictures/12217/122176396/medium/OM37E794HBL3OFFF.jpg'
    request.urlretrieve(url,'./456.jpg')
    ```
    
    - 方式1和方式2对于图片数据爬取的操作最大的不同之处是在哪?
      - 方式2不可以使用UA伪装的机制
    
    urllib就是一个比较老的网络请求的模块,在requests模块没有出现之前,请求发送的操作使用的都是urllib
    
    **正则的数据解析**
    
    ```python
    #糗事百科
    import re
    import os
    
    dir_name = './qiutuLibs'
    if not os.path.exists(dir_name):
        os.mkdir(dir_name)
    
    url = 'https://www.qiushibaike.com/pic/'
    page_text = requests.get(url,headers=headers).text
    #数据解析:图片地址
    ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
    img_src_list = re.findall(ex,page_text,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上的共性**
    
    ```python
    dir_name = './qiutuLibs'
    if not os.path.exists(dir_name):
        os.mkdir(dir_name)
    #指定一个通用的url模板(不可变)
    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(new_url,headers=headers).text
        #数据解析:图片地址
        ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
        img_src_list = re.findall(ex,page_text,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,'下载成功!!!')
    ```
    
    
    
    #### 3.2 bs4
    
    **bs4解析**
    
    - 环境的安装:
      - pip install bs4
      - pip install lxml
    - bs4的解析原理
      - 实例化一个BeautifulSoup的对象,并且将即将被解析的页面源码数据加载到该对象中
      - 调用BeautifulSoup对象中的相关属性和方法进行标签定位和数据提取
    - 如何实例化BeautifulSoup对象呢?
      - BeautifulSoup(fp,'lxml'):专门用作于解析本地存储的html文档中的数据
      - BeautifulSoup(page_text,'lxml'):专门用作于将互联网上请求到的页面源码数据进行解析
    
    **标签定位**
    
    - soup.tagName:定位到第一个TagName标签,返回的是单数
    - 属性定位:soup.find('tagName',attrName='value'),返回也是单数
      - find_all:和find用法一致,但是返回值是列表
    - 选择器定位:select('选择器'),返回值为列表
      - 标签,类,id,层级(>:一个层级,空格:多个层级)
    
    **提取数据**
    
    - 取文本:
      - tag.string:标签中直系的文本内容
      - tag.text:标签中所有的文本内容
    - 取属性:
      - tag['attrName']
    
    ```python
    # 用法解析
    from bs4 import BeautifulSoup
    fp = open('./test.html','r',encoding='utf-8')
    soup = BeautifulSoup(fp,'lxml') #将即将被解析的页面源码加载到该对象中
    soup.p
    soup.find('div',class_='song')
    soup.find_all('div',class_='song')
    soup.select('.tang')
    soup.select('#feng')
    soup.select('.tang > ul > li') #一层一层查找
    soup.select('.tang li') #多层查找
    li_6 = soup.select('.tang > ul > li')[6]
    i_tag = li_6.i
    i_tag.string
    soup.find('div',class_='tang').text
    soup.find('a',id="feng")['href']
    
    ```
    
    ```python
    #在首页中解析章节名称&每一个章节详情页的url
    url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
    page_text = requests.get(url,headers=headers).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']
        chap_title = a.string
        #对章节详情页的url发起请求,解析详情页中的章节内容
        detail_page_text = requests.get(detail_url,headers=headers).text
        soup = BeautifulSoup(detail_page_text,'lxml')
        chap_content = soup.find('div',class_="chapter_content").text
        fp.write(chap_title+':'+chap_content+'
    ')
        print(chap_title,'爬取成功!')
    fp.close()
    ```
    
    #### 3.3 xpath
    
    **xpath解析**
    
    - 环境的安装:pip install lxml
    - xpath的解析原理
      - 实例化一个etree类型的对象,且将页面源码数据加载到该对象中
      - 需要调用该对象的xpath方法结合着不同形式的xpath表达式进行标签定位和数据提取
    - etree对象的实例化
      - etree.parse(fileNane)
      - etree.HTML(page_text)
    - xpath方法返回的永远是一个列表
    
    
    
    **标签定位**
    
    - 在xpath表达式中最最侧的/表示的含义是说,当前定位的标签必须从根节点开始进行定位
    - xpath表达式中最左侧的//表示可以从任意位置进行标签定位
    - xpath表达式中非最左侧的//表示的是多个层级的意思
    - xpath表达式中非最左侧的/表示的是一个层级的意思
    - 属性定位://tagName[@arrtName='value']
    - 索引定位://tagName/li[3]
    
    **提取数据**
    
    - 取文本:
      - /text():取直系的文本内容
      - //text():取所有的文本内容
    - 取属性:
      - tag/@attrName
    
    ```python
    #用法解析
    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的招聘信息**
    
    - 岗位名称
    - 公司名称
    - 薪资
    - 岗位描述
    
    ```python
    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)
    ```
    
    - 另一种较为通用的xpath表达式的使用形式:管道符在xpath表达式中的应用,用于解析不规则布局的页面数据(比如糗事百科中有的作者有名字,有的没有注册名字)
    - 爬取糗事百科的段子内容和作者名称
    
    ```python
    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]  #  将两个匹配规则形成或的关系
        content = div.xpath('.//div[@class="content"]/span//text()')
        content = ''.join(content)
        print(author,content)
    ```
    
    
    
    **中文乱码处理的问题**
    
    - 爬取http://pic.netbian.com/4kmeishi/的图片和图片名称
    
    ```python
    #指定一个通用的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')#这类是关键
    ```
  • 相关阅读:
    Silverlight实用窍门系列:29.Silverlight碰撞测试、检测自定义控件碰撞,雷达扫描图之扫描雷达点状态【附带源码实例】
    Silverlight实用窍门系列:36.Silverlight中播放视频和打印文档【附带源码实例】
    Silverlight实用窍门系列:41.Silverlight中调用麦克风模拟录音机设备,存储为WAV音频【附带实例源码】
    Silverlight 5 beta新特性探索系列:7.结合上层元素属性绑定和Style Setter上的绑定
    Silverlight实用窍门系列:35.细解Silverlight冒泡路由事件和注册冒泡路由事件【附带实例源码】
    Silverlight实用窍门系列:40.Silverlight中捕捉视频,截图保存到本地【附带实例源码】
    Silverlight实用窍门系列:34.Silverlight中不得不了解使用的依赖属性【附带源码实例】
    Silverlight 5 beta新特性探索系列:1.安装Silverlight 5 beta环境以及OOB模式下Silverlight 5 多窗口支持
    Silverlight实用窍门系列:26.Silverlight多线程技术ThreadPool的使用【附带源码实例】
    Silverlight实用窍门系列:32.WebClient上传String、下载String、上传Stream流、下载Stream流【附带源码实例】
  • 原文地址:https://www.cnblogs.com/doraemon548542/p/11964373.html
Copyright © 2011-2022 走看看