整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

json数据解析及网络请求

json数据解析及网络请求

文已经介绍了ListView和RecyclerView等各种列表控件,在实际的开发中,会经常遇到大批量的数据加载和展示的问题,本节课来具体的学习一下常见的方式和步骤。

一、json解析

移动端和服务器交互一般用得较多的数据传递方式都是 Json 字符串的形式, 保存对象,我们也可以写成一个 Json 字符串然后存储。

常见的解析 Json的方式有Android自带Json解析器, Gson,Fastjson,jackson 等。

1.0 http://Json.cn工具

通过一个在线网站工具可以实时的实现json数据的格式化:http://www.json.cn/

1.1 Android自带的Json解析器

自带的解析器的API都存在于org.json包下,用到的类有下面这写:

  • JSONObject: Json对象,可以完成Json字符串与Java对象的相互转换
  • JSONArray: Json数组,可以完成Json字符串与Java集合或对象的相互转换,[]
  • JSONStringer: Json文本构建类,这个类可以帮助快速和便捷的创建JSON text, 每个JSONStringer实体只能对应创建一个JSON text
  • JSONTokener:Json解析类
  • JSONException:Json异常

使用方法参见案例17的原生解析方法,如下:

private List<Lesson> parseJsonByNative(String jsonStr) {
  List<Lesson> lessons=new ArrayList<>();
  try {
    JSONObject jsonObject=new JSONObject(jsonStr);
    int code=jsonObject.getInt("status");
    if (code !=1) {
      return null;
    }
    JSONArray dataArray=jsonObject.getJSONArray("data");
    for (int i=0; i < dataArray.length(); i++) {
      JSONObject lessonObject=dataArray.getJSONObject(i);
      int id=lessonObject.getInt("id");
      String name=lessonObject.getString("name");
      String picSmall=lessonObject.getString("picSmall");
      String picBig=lessonObject.getString("picBig");
      String description=lessonObject.getString("description");
      int learner=lessonObject.getInt("learner");

      Lesson lesson=new Lesson();
      lesson.setId(id);
      lesson.setName(name);
      lesson.setPicSmall(picSmall);
      lesson.setPicBig(picBig);
      lesson.setDescription(description);
      lesson.setLearner(learner);
?
      lessons.add(lesson);
    }
  } catch (JSONException e) {
    e.printStackTrace();
  }
  return lessons;
}

1.2 Gson解析库

Gson是google开源的一款用于json解析的库,受到很多开发者的喜爱。

Gson在github的开源库地址:https://github.com/google/gson

在Android中使用gson有两种方式:

  • 在build.gradle文件中设置库依赖:implementation 'com.google.code.gson:gson:2.8.6'
  • 下载gson.jar包,放在Android项目恶libs目录中

设置依赖后,即可在项目中使用Gson解析数据,如下所示:

private Lesson[] parseByGson(String jsonStr) {
  Gson gson=new Gson();
  Type type=new TypeToken<Common<Lesson>>() {
  }.getType();
  Common<Lesson> common=gson.fromJson(jsonStr, type);
  return common.data;
}

1.3 FastJson解析库

fastjson是阿里巴巴团队开源的一款json解析库,使用的项目和团队也很多。在github上的star数达到了21.2k,超过了gson。

FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。

fastjson开源库在github的开源地址:https://github.com/alibaba/fastjson

在Android中使用fastjson,需要在项目的build.gradle文件中设置依赖:

compile 'com.alibaba:fastjson:1.1.71.android'

同样的和gson一样,也可以下载最新的jar文件,放在android项目的libs目录中进行依赖设置。

public static List<Lesson> parseByFastJson(String jsonStr) {
  com.alibaba.fastjson.JSONObject jsonObject=JSON.parseObject(jsonStr);
  com.alibaba.fastjson.JSONArray jsonArray=jsonObject.getJSONArray("data");
  return  jsonArray.toJavaList(Lesson.class);
}

1.4 Jackson

Jackson是当前用的比较广泛的,用来序列化和反序列化json的Java开源框架。Jackson是最流行的json解析器之一,Java应用框架中很多都是使用Jackson库。目前github上Jackson的star数量达到5.5K。

Jackson在github上的地址如下:https://github.com/FasterXML/jackson

使用Jackson:

  • 先下载jar包
  • 放入到项目的libs目录中,设置依赖为library
  • 在x项目中进行使用

此处省略案例,可自行练习。

1.5 解析库对比和总结

  • 反序列化操作:总体上JSONObject处理性能比较突出,但是JSONObject的缺点是啰嗦,代码量多,需要些try...catch等冗余代码。
  • Gson:随着数据量的增大,Gson库耗时会增加。
  • FastJson操作:性能比较好,数据的量级对内存消耗影响较小,且消耗内存比较小。
  • JSONObject原生:数据量大时,JSONObject内存消耗很明显。
  • Jackson:耗时和内存消耗上都较大。

① 数据量小时,可以选择JSONObject进行处理,要接收代码量冗余。Gson和FastJson差不多。

② 数据量大时,或者数据量会有明显的量级的增加变化,FastJson综合表现更好。

二、加载网络数据的注意事项

json数据通常都是通过网络接口请求而来,因此需要进行网络访问获取数据,包括图片的加载也是需要网络的。

uses-permission网络权限

使用网络,需要在AndroidMainfest.xml文件中设置使用网络权限。

<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>

Https规范

在Android P版本开始,为了安全起见,不再允许使用http形式的接口。为了解决这个问题,可以通过在AndroidManifest.xml文件的Application文件中,设施usesCleartextTraffic属性为true来,表示使用明文网络流量。推荐还是尽快转换接口升级为https。

数据请求更新规范

① 在Android中,不能在主线程请求网络,否则会报错,网络请求需要放在工作子线程中。

② 在Android中,请求数据完毕,不能在子线程中操作UI,否则会报错。

图片加载开源库

图片加载在android中可以使用比较成熟的开源库来解决,常用的图片加载开源库是Glide,picasso等。

glide

glide在github上的开源地址是:https://github.com/bumptech/glide

使用时首先需要引入glide库的依赖:

//图片网络库
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

在图片控件加载的地方,执行如下操作:

 Glide.with(mContext).load(lesson.getPicBig()).into(holder.imgCover);

picasso

picasso在github上的开源地址是:https://github.com/square/picasso

使用时首先需要引入glide库的依赖:

implementation 'com.squareup.picasso:picasso:2.71828'

在图片控件加载的地方,执行如下操作:

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

三、Android常见的网络请求

3.1 HttpURLConnection

与java的基础相同,HttpURLConnection可以实现网络请求。具体步骤为:

  • 构建URL。
  • openConnection打开HttpURLConnection对象。
  • 通过IO流读取数据。

3.2 Volley库

在Goole I/O 2013上发布的网络通信库,取名为Volley。

Volley的优势及特点是:自动调度网络请求、支持请求优先级、支持取消请求,可以取消单个请求或多个,并且Volley回调时候是在主线程,可以直接操作UI。

Volley库的操作和使用说明文档,可以访问如下地址:https://developer.android.com/training/volley/index.html

设置库依赖

implementation 'com.android.volley:volley:1.1.1'

RequestQueue请求列队

请求之前,首先需要构建RequestQueue请求列队,全局也只初始化一次就好。

Request请求对象

Request请求对象,有StringRequest、ImageRequest、ClearCacheRequest、JsonRequest这四个子类。

可以给Request设置一个tag,并通过RequestQueue.cancelAll(tag)可以进行取消。

3.3 Okhttp

第二种方式是使用成熟的网络请求库,比如说Okhttp,非常火的一个网络库,由Square公司提供。

Okhttp开源库的github地址如下:https://github.com/square/okhttp

最新的Okhttp库的版本是4.6.0,在Android应用中使用该库的操作方法是设置库依赖,在build.gradle文件中:

implementation("com.squareup.okhttp3:okhttp:4.6.0")

3.3.1 使用步骤

  • 构建OkHttpClient对象:全局只需要初始化一次即可。
  • Request对象:通过Builder进行构建和参数设置。
  • 请求:get请求直接使用.get();post请求通过.post(RequestBody),携带数据。
OkHttpClient client=new OkHttpClient();

String run(String url) throws IOException {
  Request request=new Request.Builder()
      .url(url)
      .build();
  try (Response response=client.newCall(request).execute()) {
    return response.body().string();
  }
}

OkHttp库的请求,回调方法是运行在子线程中,因此不能直接在回调函数更新ui操作,如果在子线程中操作UI,刷新界面操作,会报如下错误:

Only the original thread that created a view hierarchy can touch its views

如果出现该错误,可以使用如下的方法解决。该方法用于回到主线程操作UI:

面已经介绍了HttpServletResponse响应对象中的一些常用方法,这一小节介绍如何使用HttpServletResponse响应对象,将结果返回给浏览器客户端。

1.1、返回HTML内容

使用Servlet返回HTML内容给客户端,是一个比较常见的功能,因为Servlet本来就是用于JavaWeb开发的,而HTML就是Web开发的三大核心语言之一。这里给出一个响应HTML内容给客户端的案例代码:

package com.gitcode.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @version 1.0.0
 * @Date: 2024/2/10 20:20
 * @Author ZhuYouBin
 * @Description:
 */
public class HtmlResponseServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 响应HTML内容
        response.setContentType("text/html;charset=UTF-8");
        // 获取输出流
        PrintWriter writer=response.getWriter();

        // 输出HTML内容
        writer.println("<!DOCTYPE html>");
        writer.println("<html lang=\"en\">");
        writer.println("<head>");
        writer.println("    <meta charset=\"UTF-8\">");
        writer.println("    <title>响应HTML内容</title>");
        writer.println("</head>");
        writer.println("<body>");
        writer.println("    <h3>Hello World!你好,世界!</h3>");
        writer.println("</body>");
        writer.println("</html>");

        // 关闭流
        writer.close();
    }
}

运行结果如下所示:

1.2、返回JSON内容

JSON格式在实际的Web开发中,可以说是最常用的数据交互格式啦,在一些前后端分离的项目中,都是采用JSON来进行数据交互的,所以这里给出一个响应JSON内容的案例代码:

package com.gitcode.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @version 1.0.0
 * @Date: 2024/2/10 20:20
 * @Author ZhuYouBin
 * @Description:
 */
public class JsonResponseServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 响应JSON内容
        response.setContentType("application/json;charset=UTF-8");
        // 获取输出流
        PrintWriter writer=response.getWriter();

        // 输出JSON内容
        writer.println("{");
        writer.println(" \"username\":\"名字是Tom-2024\",");
        writer.println(" \"password\":\"123456\"");
        writer.println("}");

        // 关闭流
        writer.close();
    }
}

运行结果如下所示:

以上,就是使用HttpServletResponse对象返回HTML和JSON数据格式。

今天就到这里,未完待续~~

家好,我是Python进阶者。

背景介绍

我们知道再爬虫的过程中我们对于爬取到的网页数据需要进行解析,因为大多数数据是不需要的,所以我们需要进行数据解析,常用的数据解析方式有正则表达式,xpath,bs4,这次我们来介绍一下另一个数据解析库--jsonpath,在此之前我们需要先了解一下什么是json。


一、初识Json

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和生成。适用于进行数据交互的场景,比如网站前台与后台之间的数据交互。


Python 2.7及之后版本,自带了JSON模块,直接import json就可以使用了。

官方文档:http://docs.python.org/library/json.html

Json在线解析网站:http://www.json.cn/#


二、Json的基本使用

简介

json简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构;

  1. 对象:对象在js中表示为{ }括起来的内容,数据结构为 { key:value, key:value, ... }的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象.key 获取属性值,这个属性值的类型可以是数字、字符串、数组、对象这几种。
  2. 数组:数组在js中是中括号[ ]括起来的内容,数据结构为 ["Python", "javascript", "C++", ...],取值方式和所有语言中一样,使用索引获取,字段值的类型可以是 数字、字符串、数组、对象几种。


使用

json模块提供了四个功能:dumpsdumploadsload,用于字符串 和 python数据类型间进行转换。

把Json格式字符串解码转换成Python对象 从json到python的类型转化对照如下:

JSON

Python

object

dict

array

list

string

unicode

number(int)

int,long

number(real)

float

true(false)

True(False)

null

None


1.json.loads()

import json


strDict='{"city": "广州", "name": "小黑"}'


r=json.loads(strDict) # json数据自动按Unicode存储


print(r)

结果如下:

{'city': '广州', 'name': '小黑'}


2. json.load()

读取文件中json形式的字符串元素 转化成python类型

import json
s=json.load(open('test.json','r',encoding='utf-8'))
print(s,type(s))

结果如下:

{'city': '广州', 'name': '小黑'} <class 'dict'>


3. json.dumps()

实现python类型转化为json字符串,返回一个str对象 把一个Python对象编码转换成Json字符串

import json


listStr=[1, 2, 3, 4]
dictStr={"city": "北京", "name": "大猫"}


s1=json.dumps(listStr)
s2=json.dumps(dictStr,ensure_ascii=False)


print(s1,type(s1))
print(s2)

结果如下:

[1, 2, 3, 4] <class 'str'>{"city": "北京", "name": "大猫"} <class 'str'>

注意:

  1. json.dumps() 序列化时默认使用的ascii编码
  2. 添加参数 ensure_ascii=False 禁用ascii编码,按utf-8编码


4. json.dump()

将Python内置类型序列化为json对象后写入文件

import json


json_info="{'age': '12'}"
file=open('ceshi.json','w',encoding='utf-8')
json.dump(json_info,file)

结果如下:

ceshii,json(目录文件产生)



三、JsonPath

JsonPath 是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript, Python, PHP 和 Java。

JsonPath 对于 JSON 来说,相当于 XPATH 对于 XML。

下载地址:https://pypi.python.org/pypi/jsonpath

安装方法:点击Download URL链接下载jsonpath,解压之后执行python setup.py install

官方文档:http://goessner.net/articles/JsonPath


JsonPath与XPath语法对比

Json结构清晰,可读性高,复杂度低,非常容易匹配,下表中对应了XPath的用法。

XPath

JSONPath

描述

/

$

根节点

.

@

现行节点

/

.or[]

取子节点

..

n/a

取父节点,Jsonpath未支持

//

..

就是不管位置,选择所有符合条件的条件

*

*

匹配所有元素节点

@

n/a

根据属性访问,Json不支持,因为Json是个Key-value递归结构,不需要。

[]

[]

迭代器表示(可以在里边做简单的迭代操作,如数组下标,根据内容选值等)

|

[,]

支持迭代器中做多选。

[]

?()

支持过滤操作.

n/a

()

支持表达式计算

()

n/a

分组,JsonPath不支持


四、案例测试

我们爬取淘票票官网的城市信息,保存为json文件,进行jsonpath语法测试,获取所有城市名称。

请求

import requests
import time


url='https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1632211792156_137&jsoncallback=jsonp138&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'


headers={
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36',
} 


res=requests.get(url,headers=headers)


result=res.content.decode('utf-8')


print(result) # xxx省略

注意:

headers里面的键值对最好都加上,还是有反爬的,该网站,这里为了简便省去了;


保存数据

content=result.split('(')[1].split(')')[0] # 由于文件首尾的字符不需要需要剔除掉做字符串切割


with open('tpp.json','w',encoding='utf-8')as fp:
    fp.write(content)

打开json文件如下所示:


解析数据

这里我们获取全部城市名称

import json
import jsonpath


obj=json.load(open('tpp.json','r',encoding='utf-8')) # 注意,这里是文件的形式,不能直接放一个文件名的字符串


city_list=jsonpath.jsonpath(obj,'$..regionName') # 文件对象   jsonpath语法


print(city_list)

结果如下:


五、总结

我们知道json是一种常见的数据传输形式,所以对于爬取数据的数据解析,json的相关操作是比较重要的,能够加快我们的数据提取效率,本文简单介绍了json和jsonpath的相关操作,对于测试网站(淘票票)的json做了简单的数据解析,感兴趣的小伙伴可以把其他数据解析一下。