PYTHON 七月 26, 2019

4.常用的数据解析方式

文章字数 19k 阅读约需 17 mins. 阅读次数 1000000


正则表达式解析


  • 单字符:

      . : 除换行以外所有字符
      [] :[aoe][a-w] 匹配集合中任意一个字符
      \d :数字  [0-9]
      \D : 非数字
      \w :数字、字母、下划线、中文
      \W : 非\w
      \s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ \f\n\r\t\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(正则表达式, 替换内容, 字符串)
  • 综合练习1 – 图片下载

    糗事百科

    需求:将指定页的图片下载后保存到指定文件夹中

import urllib.request
import urllib.parse
import os
import re
s_pageNum=int(input('enter start page num:'))
e_pageNum=int(input('enter end page num:'))

#创建文件夹
if not os.path.exists('qiutu'):
    os.mkdir('qiutu')

def get_request(url):
    headers={
        'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36' 
    }
    return urllib.request.Request(url,headers=headers)
def analysis_contente(content):
    url_list=[] #该列表是用来存储被拼接好的完整的图片的url
    pl='<div class="thumb">.*?<img src="(.*?)" .*?>.*?</div>'
    pa=re.compile(pl,re.S)
    #该列表中存储就是当前页中所有的图片的url
    imageUrl_list=pa.findall(content)
    for n_url in imageUrl_list:
        url='https:'+n_url
        url_list.append(url)
    return url_list

for page in range(s_pageNum,e_pageNum+1):
    url='https://www.qiushibaike.com/pic/page/%s/?s=5116392'
    url=format(url%str(page))

    request=get_request(url)

    response=urllib.request.urlopen(request)
    content=response.read().decode()
    #对请求到的网页源码进行指定内容的解析
    image_url_list=analysis_contente(content)

    for url in image_url_list:
        request_image=get_request(url)
        response_image=urllib.request.urlopen(request_image)
        #获取图片原有的名称
        image_name=url.split('/')[-1]
        image_path='qiutu/'+image_name
        #将图片写入本地进行存储
        with open(image_path,'wb') as fp:
            fp.write(response_image.read()) 
  • 综合练习2 – 抓取文章
#http://www.yikexun.cn/rensheng/ganwu/list_52_1.html
import re, urllib.request, urllib.parse, os
start = int(input('enter a start page:'))
end = int(input('enter a end page:'))

#新建文件夹
if not os.path.exists('一点点语录'):
    os.mkdir('一点点语录')

#返回一个响应
def get_request(url):
    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5792.400 QQBrowser/10.2.2101.400'}
    resquest = urllib.request.Request(url,headers=headers)
    response = urllib.request.urlopen(resquest)
    return response

path = '一点点语录/人生感悟(升级版).txt'
with open(path,'w',encoding='utf-8') as f:
    for page in range(start,end+1):
        # url_list = []
        url = 'http://www.yikexun.cn/rensheng/ganwu/list_52_'+str(page)+'.html'
        webpage = get_request(url).read().decode()
        url_list = re.findall(r'<div class="art-t">\s+<h3><a href="(.*?)">.*?</a></h3>',webpage)
        print('开始第{}页'.format(page))
        count = 1
        for url in url_list:
            pageInfo = get_request('http://www.yikexun.cn'+url).read().decode()
            title = re.findall(r'<div class="arc-title">\s+<h1>(.*?)</h1>', pageInfo)
            content = re.findall(r'<p>(.*?)</p>',pageInfo)
            print('写入第{}篇文章标题'.format(count))
            f.write(title[0]+'\n')
            print('开始写入第{}篇文章'.format(count))
            for paragraph in range(len(content) - 2):
                if not 'img' in content[paragraph]:
                    if '<br/>' in content[paragraph] or '&nbsp;' in content[paragraph]:
                        if len(re.split('<br/>|&nbsp;', content[paragraph])[0]) == 0:
                            f.write(re.split('<br/>|&nbsp;', content[paragraph])[1]+'\n')
                        else:
                            f.write(re.split('<br/>|&nbsp;', content[paragraph])[0]+'\n')
                    else:
                        f.write(content[paragraph]+'\n')
            f.write('\n\n')
            print('第{}篇文章写入完成'.format(count))
            count += 1
        print('第{}页结束'.format(page))

bs4:BeautifulSoup解析


  • 在pycharm中,可以通过设置直接进行搜索安装 ,无需配置源

  • 需要将pip源设置为国内源,阿里源、豆瓣源、网易源等

    • windows

       (1)打开文件资源管理器(文件夹地址栏中)
      
       (2)地址栏上面输入 %appdata%
      
       (3)在这里面新建一个文件夹  pip
      
       (4)在pip文件夹里面新建一个文件叫做pip.ini ,内容写如下即可

      [global]

      timeout = 6000

      index-url = https://mirrors.aliyun.com/pypi/simple/

      trusted-host = mirrors.aliyun.com

    • linux

      (1)cd ~

      (2)mkdir ~/.pip

       (3)vi ~/.pip/pip.conf
      
       (4)编辑内容,和windows一模一样
    • 需要安装:pip install bs4

      bs4在使用时候需要一个第三方库,把这个库也安装一下

      pip install lxml

  • 用法:

    • 导入:

      • from bs4 import BeautifulSoup

        使用方式:可以将一个html文档,转化为指定的对象,然后通过对象的方法或者属性去查找指定的内容

    • 转化本地文件:

      • soup = BeautifulSoup(open(‘本地文件’), ‘lxml’)
    • 转化网络文件:

      • soup = BeautifulSoup(‘字符串类型或者字节类型’, ‘lxml’)
        打印soup对象显示内容为html文件中的内容
    • 根据标签名查找:

      • soup.a 只能找到第一个符合要求的标签
    • 获取属性:

      • soup.a.attrs 获取a所有的属性和属性值,返回一个字典
      • soup.a.attrs[‘href’] 获取href属性
      • soup.a[‘href’] 也可简写为这种形式
    • 获取内容:

      • soup.a.string

      • soup.a.text

      • soup.a.get_text()

        如果标签内还包含有子标签,那么string获取到的结果为None,而其它两个,可以获取文本内容

    • find:查找

      • soup.find(‘a’) 找到第一个符合要求的a

        • soup.find(‘a’, title=”xxx”)
        • soup.find(‘a’, alt=”xxx”)
        • soup.find(‘a’, class_=”xxx”)
        • soup.find(‘a’, id=”xxx”)
      • find方法不仅soup可以调用,普通的div对象也能调用,会去指定的div里面去查找符合要求的节点 div = soup.find(‘div’, class_=”tang”)

        print(div.find(‘a’, class_=”du”))

      • find找到的都是第一个符合要求的标签

    • find_all

      • soup.find_all(‘a’)
      • soup.find_all([‘a’, ‘b’]) 找到所有的a和b标签
      • soup.find_all(‘a’, limit=2) 限制前两个
    • select:

      • soup.select(‘#feng’)

        根据选择器选择指定的内容

      • 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器

        a, .dudu, #lala, .meme
        div .dudu #lala .meme .xixi 下面好多级
        div > p > a > .lala 只能是下面一级

        select选择器返回永远是列表,需要通过下标提取指定的对象,然后获取属性和节点

        该方法也可以通过普通对象调用,找到都是这个对象下面符合要求的所有节点

from bs4 import BeautifulSoup
#bs对象返回的是一组文件源码
soup = BeautifulSoup(open('soup_text.html',encoding='utf-8'), 'lxml')
soup
#soup.div
#soup.a.attrs['href']
#soup.a['href']
#soup.a.string
#soup.a.text
#soup.a.get_text()
#oup.find('a')
#soup.find('a',{'class':'du'})
#soup.find('a',class_="du")
#div_soup=soup.find('div',class_="song")
#len(soup.find_all('a',limit=2))
#soup.find_all(['a','div'])
#soup.select('#feng')
#soup.select('a')
#soup.select('.song > img')[0]['src']

XPath解析


XPath简介

  • 概念:

    xpath是一种路径表达式,用来在xml中查找指定的元素。

  • xml:

    xml是用来存储和传输数据的一种格式。

  • xml和html的不同有两点:

    1. html用来显示数据,xml是用来传输数据
    2. html标签是固定的,xml标签是自定义的
  • xpath常用表达式:

    • 常用的路径表达式

      • // : 不考虑位置的查找
      • / : 从跟节点开始往下查找
      • @ : 选取属性
    • 实例:

      • /bookstore/book 选取根节点bookstore下面所有直接子节点book
      • //book 选取所有book
      • /bookstore//book 查找bookstore下面所有的book
      • /bookstore/book[1] bookstore里面的第一个book
      • /bookstore/book[last()] bookstore里面的最后一个book
      • /bookstore/book[position()<3] 前两个book
      • //title[@lang] 所有的带有lang属性的title节点
      • //title[@lang=’eng’] 所有的lang属性值为eng的title节点
    • 属性定位:

      • //li[@id=”hua”]
      • //div[@class=”song”]
    • 层级定位:(索引定位)

      • //div[@id=”head”]/div/div[2]/a[@class=”toindex”]

        【注】索引从1开始

      • //div[@id=”head”]//a[@class=”toindex”]

        【注】双斜杠代表下面所有的a节点,不管位置

    • 逻辑运算:

      • //input[@class=”s_ipt” and @name=”wd”]
    • 模糊匹配:

      1. contains

        • //input[contains(@class, “s_i”)]

          所有的input,有class属性,并且属性中带有s_i的节点

        • //input[contains(text(), “爱”)]

      2. starts-with

        • //input[starts-with(@class, “s”)]
          所有的input,有class属性,并且属性以s开头
    • 取文本:

      • //div[@id=”u1”]/a[5]/text() 获取节点内容
      • //div[@id=”u1”]//text() 获取节点里面不带标签的所有内容
    • 取属性:

      • //div[@id=”u1”]/a[5]/@href

new_book.xml

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>

</bookstore>
from lxml import etree
tree=etree.parse('new_book.xml')
#xpath表达式需要载入xpath函数中进行执行
#xpath返回的是列表
#tree.xpath('/bookstore') #从根节点开始导航定位到的节点信息
#tree.xpath('/bookstore/book')
tree.xpath('//book[2]') #索引从1开始
tree.xpath('//book[last()]')
tree.xpath('//book[position()<2]')
tree.xpath('//title[@lang="eng"]')
  • 代码中使用xpath

    from lxml import etree
    两种方式使用:将html文档变成一个对象,然后调用对象的方法去查找指定的节点

    (1)本地文件
    tree = etree.parse(文件名)

    (2)网络文件
    tree = etree.HTML(网页字符串)

    ret = tree.xpath(路径表达式)

    【注】ret是一个列表

  • 安装xpath插件:

    可以执行xpath相应的路径指令
    将xpath插件拖动到谷歌浏览器扩展程序(更多工具中)中,安装成功
    启动和关闭插件 ctrl + shift + x

  • 案例:

from lxml import etree
url='http://www.haoduanzi.com/category-10_2.html'
#//div[@class="log cate10 auth1"]/h3/a/text() #取标题
#//div[@class="log cate10 auth1"]/div[@class="cont"]//text() #取内容
headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
    }
request=urllib.request.Request(url,headers=headers)
response=urllib.request.urlopen(request)
url_content=response.read().decode()

#使用xpath对url_conten进行解析
#使用xpath解析从网络上获取的数据
tree=etree.HTML(url_content)
#解析获取当页所有段子的标题
title_list=tree.xpath('//div[@class="log cate10 auth1"]/h3/a/text()')
text_list=tree.xpath('//div[@class="log cate10 auth1"]/div[@class="cont"]//text()')
list=str(text_list).split('阅读全文')
text=list[0:12]

JsonPath解析


拓展:jsonPath-简单入门

  1. jsonpath: 用来解析json数据使用的

  2. 安装:

    pip install lxml
    pip install jsonpath
  3. jsonpath和xpath的对比

    xPath jsonPath 描述
    / $ 根元素
    . @ 当前元素
    / . or [] 子元素
    .. n/a 父元素
    // .. 递归下降,JSONPath是从E4X借鉴的。
    * * 通配符,表示所有的元素
    @ n/a 属性访问字符
    [] [] 子元素操作符
    | [,] 连接操作符在XPath 结果合并其它结点集合。JSONP允许name或者数组索引。
    n/a [start: end: step] 数组分割操作从ES4借鉴。
    [] ?() 应用过滤表示式
    n/a () 脚本表达式,使用在脚本引擎下面。
    () n/a Xpath分组

    注意:

     xpath         索引下标从1开始
     jsonpath     索引下标从0开始
  4. 处理json用到的函数

    • 导包:import json

    • json.dumps():将字典或者列表转化为json格式的字符串

    • json.dump():将字典或者列表转化为json格式字符串并且写入到文件中

    • json.loads():将json格式字符串转化为python对象(字典/列表)

    • json.load():从文件中读取json格式字符串,转化为python对象

import json
json_obj={

    "name":['张三','lisi','xiaohei'],
    "age":[10,20,30]
}
#将json对象转换成json串
str_json=json.dumps(json_obj,ensure_ascii=False)

obj=json.loads(str_json)

#将json_obj转换成json格式的字符串,然后写入到磁盘
#json.dump(json_obj,open('./a.json','w'))
#将a.json文件中的json字符串转换成json对象
obj=json.load(open('./a.json','r'))

test.json

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}
import jsonpath
import json
#jsonpath函数返回值是一个列表
#将test.json中的字符串提取出来
obj=json.load(open('./test.json','r'))
#使用jsonpath函数去执行jsonpath表达式

#获取书店里所有书的作者
#/store/book/author
#jsonpath.jsonpath(obj,'$.store.book[*].author')

#获取第三本书
#jsonpath.jsonpath(obj,'$.store.book[2]')[0]

#获取最后一本书
#jsonpath.jsonpath(obj,'$..book[(@.length-1)]')

#过滤出价格小于10的所有图书
#jsonpath.jsonpath(obj,'$..book[?(@.price<10)]')

#所有元素
jsonpath.jsonpath(obj,'$..*')
  • 案例:淘宝评论爬取

    要求:将评论中的评论内容,手机详情和用户昵称爬取存储写入json文件。
url='https://rate.tmall.com/list_detail_rate.htm?itemId=560257961625&spuId=893336129&sellerId=1917047079&order=3&currentPage=3&append=0&content=1&tagId=&posi=&picture=&ua=098%23E1hvmpvBvoGvUpCkvvvvvjiPPs5vtjibP2FvljrCPmPptjlWn2cOsj1UPsLW0jY8PghCvCB4cJrUyn147DIsWKwG8lID75qNh46Cvvyv2bgjtJZvO6ArvpvEvC2MmERLvhK99phv2HiJFMwXzHi47k7IzT6CvCvwhHeCeiIv%2B48nrsw6Zf5B3QhvCvvhvvm5vpvhvvmv9FyCvvpvvvvvvphvC9vhvvCvpbyCvm9vvvvvphvvvvvv99CvpvF4vvmmvhCv2UvvvUUvphvUgQvv99CvpvFFuphvmvvv9bUui7cWkphvC99vvOC0ppyCvhQUlC6vCld6u70xdByapbmxfwkK5ehhfw1l%2BboJEcqUaNspV13gprLO6nv7RAYVyO2vqbVQWl4v1WFIRfU6pwet9E7r5C69D7zZa40A2QhvCvvvvvmrvpvEvvp19aMNvmxhRphvCvvvvvvtvpvhvvvvvv%3D%3D&needFold=0&_ksTS=1535091159955_1320&callback=jsonp1321'
headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
    }
request=urllib.request.Request(url,headers=headers)
json_text=urllib.request.urlopen(request).read().decode()

#原始的json串存在内容:在开始的位置出现了一些字符数据。
json_text=json_text.strip('\r\njsonp1321()')
#将json格式的字符串转换成json对象
json_obj=json.loads(json_text)
#进行json解析
rate_list=json_obj['rateDetail']['rateList']
my_rate_list=[] #存储解析后的内容
for rate in rate_list:
    rate_content=rate['rateContent']
    phone_detail=rate['auctionSku']
    userName=rate['displayUserNick']

    rate_dic={

        '评论内容':rate_content,
        '商品详情':phone_detail,
        '用户昵称':userName
    }
    my_rate_list.append(rate_dic)

#将解析到的内容(json对象)写入磁盘
json.dump(my_rate_list,open('./rate.json','w',encoding='utf-8'),ensure_ascii=False)

上一篇:
下一篇:
0%