GoLang—爬虫—解析JSON数据_golang 爬虫json解析-程序员宅基地

技术标签: 网络爬虫  爬虫  JSON  GoLand  数据处理  GoLang  

JSON作为一种重要的数据格式,具有良好的可读性以及自描述性,广泛地应用在各种数据传输场景中。在网络爬虫中,当网页采用AJAX方式渲染数据时,我们必须找出AJAX的异步请求方式,并且模拟发送AJAX,从中获取数据内容,AJAX的响应数据大部分采用JSON格式表示。
GoLang可以使用标准库encoding/json解析JSON数据,此外还有第三方包ffjsoneasyjsonjsoniterjsonparser等等。在性能上,第三方包完胜标准库encoding/json,本文将分别讲述标准库encoding/json和第三包jsoniter的使用。

标准库encoding/json

回顾GoLang—爬虫入门基础—数据清洗(goquery),我们将HTTP发起请求封装在函数SendHttp,主函数main实现函数SendHttp调用和响应内容的数据处理。本文继续沿用上一节的功能代码,将发送HTTP的网址改为12306的余票查询,当我们在网页上输入查询信息并点击查询按钮即触发AJAX请求,如图所示。
在这里插入图片描述
图上的AJAX请求的响应内容为JSON格式,标准库encoding/json解析JSON有两种方式:根据JSON内容格式定义对应的结构体(struct)、使用map[string]interface{}加载JSON数据。
实际工作中,个人不建议采用根据JSON内容格式定义对应的结构体(struct),当JSON内容格式过于复杂的时候,对应的结构体(struct)会随之增加,这样会增加大量的代码。我们采用使用map[string]interface{}加载JSON数据,实现代码如下。

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)


// 使用映射传递函数参数,requestMode作为HTTP的请求方式
func SendHttp(urls string, method string, rawurl string, cookie []http.Cookie)string{
	req, _ := http.NewRequest(method ,urls, nil)
	//为请求对象NewRequest设置请求头
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36")

	//设置Cookies信息
	if cookie != nil {
		for _, v := range cookie{
			req.AddCookie(&v)
		}
	}

	//设置代理IP,代理IP必须以为fun形式表示
	client := &http.Client{}
	if rawurl != "" {
		proxy := func(_ *http.Request) (*url.URL, error) {
			return url.Parse(rawurl)
		}
		transport := &http.Transport{Proxy: proxy}
		//在Client对象设置参数Transport即可实现代理IP
		client.Transport = transport
	}

	//执行HTTP请求
	resp, _ := client.Do(req)

	//读取响应内容
	body, _ := ioutil.ReadAll(resp.Body)
	return string(body)

}

func main() {
	urls := "https://kyfw.12306.cn/otn/leftTicket/queryT?leftTicketDTO.train_date=2019-09-17&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=GZQ&purpose_codes=ADULT"
	method := "GET"
	//rawurl := "http://111.231.93.66:8888"
	rawurl := ""
	var cookie []http.Cookie
	c := http.Cookie{Name: "clientcookieid", Value: "121", Expires: time.Now().Add(111 * time.Second)}
	cookie = append(cookie, c)
	result := SendHttp(urls, method, rawurl, cookie)
	fmt.Println(result)

	// 定义make(map[string]interface{})
	r := make(map[string]interface{})
	fmt.Println([]byte(result))
	// 调用标准库encoding/json的Unmarshal
	// 将JSON数据(JSON以字符串形式表示)转换成[]byte,并将数据加载到对象r的内存地址
	json.Unmarshal([]byte(result), &r)
	// r["data"]是读取JSON最外层的key
	// 如果嵌套JSON数据,则使用map[string]interface{}读取下一层的JSON数据
	// 如读取key为data里面嵌套的result:r["data"].(map[string]interface{})["result"]
	// 如果JSON的某个key的数据以数组表示,则使用([]interface{})[index]读取数组中某个数据。
	// 如读取key为result的第四个数据:r["data"].(map[string]interface{})["result"].([]interface{})[3]
	fmt.Println(r["data"].(map[string]interface{})["result"].([]interface{})[3])
}


运行上述代码,其运行结果如图所示。
在这里插入图片描述
除了可以使用Unmarshal函数来解封 JSON,还可以使用Decoder手动地将 JSON 数据解码到结构里面,以此来处理流式的 JSON 数据(即JSON数据过大的时候),如图所示。
在这里插入图片描述
通过调用NewDecoder并传入一个包含 JSON 数据的io.Reader,程序创建出了一个新的解码器。在把指向Post结构的引用传递给解码器的Decode方法之后,被传入的结构就会填充上相应的数据,然后这些数据就可以为程序所用了。当所有 JSON 数据都被解码完毕时,Decode方法将会返回一个EOF,而程序则会在检测到这个EOF之后退出for循环。

在面对 JSON 数据时,我们可以根据输入决定使用Decoder还是Unmarshal:如果 JSON 数据来源于io.Reader流,如http.Request的Body,那么使用Decoder更好;如果 JSON 数据来源于字符串或者内存的某个地方,那么使用Unmarshal更好。

**除此之外,标准库encoding/json还可以调用函数Marshal或MarshalIndent(MarshalIndent将JSON数据格式化输出,会将数据自动分段分行处理),将特定的数据转化成JSON数据。此外还可以使用Decoder,代码如下。
在这里插入图片描述
程序会创建一个用于存储 JSON 数据的 JSON 文件,并通过把这个文件传递给NewEncoder函数来创建一个编码器。接着,程序会调用编码器的Encode方法,并向其传递一个指向Post结构的引用。在此之后,Encode方法会从结构里面提取数据并将其编码为 JSON 数据,然后把这些 JSON 数据写入创建编码器时给定的 JSON 文件里面。

第三包jsoniter

第三包jsoniter是100% 兼容原生库,但是性能超级好,预先缓存了对应struct的decoder实例,然后unsafe.Pointer省掉了一些interface{}的开销,还有一些文本解析上的优化。
首先在CMD里输入第三包jsoniter的安装指令,如下所示:

go get github.com/json-iterator/go

jsoniter的使用方式也相对简单,只需定义ConfigCompatibleWithStandardLibrary对象即可,由该对象调用Unmarshal或Marshal函数即可实现JSON的解析和转换,比如上述代码中,我们只需修改包的引入和定义ConfigCompatibleWithStandardLibrary即可,代码如下

import (
	"fmt"
	"github.com/json-iterator/go"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)
……………………(省略相同代码)
func main() {
	……………………(省略相同代码)
	// 定义make(map[string]interface{})
	r := make(map[string]interface{})
	var json = jsoniter.ConfigCompatibleWithStandardLibrary
	json.Unmarshal([]byte(result), &r)
	fmt.Println(r["data"].(map[string]interface{})["result"].([]interface{})[3])
}

如果想要了解第三包jsoniter的实现原理,可以参考官方的Github地址:jsoniter

综合上述,本博文只简单讲述了标准库encoding/json和第三包jsoniter如何解析JSON数据,下一节将讲述如何将爬取的数据进行入库处理。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/HuangZhang_123/article/details/100512207

智能推荐

JAVA常见基础面试问题汇集_#java基础面试选择题-程序员宅基地

文章浏览阅读2.3w次,点赞77次,收藏344次。1.Java 的多态表现在哪里?多态要有动态绑定,否则就不是多态,方法重载也不是多态(因为方法重载是编译期决定好的,没有后期也就是运行期的动态绑定)多态当满足这三个条件 1.有继承 2. 有重写 3. 要有父类引用指向子类对象2.抽象类与接口的区别(1)一个类只能继承一个抽象类,一个类可以实现多个接口(2)抽象类中可以存在非抽象方法,接口中的方法都是抽象方法(3)抽象类可以有私..._#java基础面试选择题

linux交叉编译头文件,交叉编译头文件位置-程序员宅基地

文章浏览阅读1k次。7th of March 2013头文件的查找方式和库的搜索路径作者:程姚根,华清远见嵌入式学院讲师。对于以压缩包发布的软件,在它的目录下通常都有一个配置脚本configure,它的作用确定编译参数(比如头文件位置、连接库位置等),然后生成Makefile以编译程序。可以进入该软件的目录,执行"./configure --help"命令查看使用帮。一个程序能正确编译、链接、运行需要满足3个条件:预..._linux内核交叉编译安转头文件

usestate中的回调函数_TypeScript 中使用React Hook-程序员宅基地

文章浏览阅读657次。TypeScript 中使用React Hook从 React V 16.8.0 和 React Native 0.59.0 版本开始, 引入了React Hook的概念。React Hook 在开发支持就考虑到了类型,所以很多Hook函数可以直接推断出他们的参数、返回值等类型,但也有一些场景需要我们显示声明类型。阅读本文前你需要了解React Hook 的基本用法,参考这里。下面会总结一下我们如..._react usestate 回调函数

MapTiler介绍-程序员宅基地

文章浏览阅读7.8k次。maptiler是基于GDAL编写的商业软件(收费),可以进行地图切片和发布,官网https://www.maptiler.com/MapTiler 提供了一套简单的解决方案,用于对任何具备地理参考的地图图像生成切片。这些切片可以用于网络地图服务。MapTiler支持栅格地理数据(TIFF / GeoTIFF,MrSID,ECW,JPEG2000,Erdas HFA,NOAA BSB ..._maptiler

IntelliJ IDEA 模板注释设置指南-程序员宅基地

文章浏览阅读352次,点赞5次,收藏10次。在编码过程中,注释是代码的重要组成部分,它可以帮助我们理解代码的意图和功能。然而,手动编写注释可能会浪费大量时间。IntelliJ IDEA 提供了一种方便的方法来设置自定义模板,以便快速生成注释。本文介绍如何设置 IntelliJ IDEA 的模板注释。首先,打开 IntelliJ IDEA 的设置对话框。在 Windows/Linux 上,选择File>Settings(或使用快捷...

python加解密文本文件算法_Python实现的文本简单可逆加密算法示例-程序员宅基地

文章浏览阅读404次。本文实例讲述了Python实现的文本简单可逆加密算法。分享给大家供大家参考,具体如下:其实很简单,就是把一段文本每个字符都通过某种方式改变(比如加1)这样就实现了文本的加密操作,解密就是其逆运算# -*-coding:utf-8 -*-import sysreload(sys)sys.setdefaultencoding('utf8')#加密def jiami():filename=raw_inp..._python中对密文按逆运算进行解密,并比较解密之后是否和原文一致

随便推点

主播都在播的王牌战争:代号英雄是款什么样的游戏?王牌战争模拟器电脑版教程-程序员宅基地

文章浏览阅读2.1k次。王牌战争:代号英雄好玩吗?王牌战争:代号英雄吃鸡手游怎么玩?王牌战争:代号英雄是英雄互娱发行的首款生存竞技手游,8v8血存血战百人竞技激战生存吃鸡手游。最近很多主播都在直播一款叫王牌战争:代号英雄的吃鸡手游,那么这款手游到底好不好玩?跟刺激战场和全军出击又有什么区别呢?手机屏幕太小,想在电脑上玩代号英雄怎么操作?游戏优点:1、300M内存,不占用手机配置。当你的手机玩不起荒野行动,玩不...

Vue框架Element UI教程-左侧导航栏(四)-程序员宅基地

文章浏览阅读1.2w次。Element UI手册:https://cloud.tencent.com/developer/doc/1270中文文档:http://element-cn.eleme.io/#/zh-CNgithub地址:https://github.com/ElemeFE/element接前三篇,Vue框架Element UI教程-安装环境搭建(一)htt..._elementui弹框设置导航栏

vue项目图片预览v-viewer插件使用-程序员宅基地

文章浏览阅读1.3w次,点赞3次,收藏19次。前言:项目管理后台有个需求,管理人员可通过点击进行预览用户上传的图片,因产品对预览的样式之类的要求不高,只需能预览、缩小、放大、旋转、显示图片类名等基本功能即可,所以楼主选择v-viewer插件来实现。v-viewer插件链接:https://github.com/mirari/v-viewer#readme安装配置npm install v-viewer --save使用:v-viewer的使用有两种方式,一种是全局使用,直接在main.js中引入,另一种是在要使用的组件引入使用,建议_v-viewer

Java语言学习思维导图_java语句思维导图-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏2次。Java语言学习思维导图_java语句思维导图

node vue 实时推送_如何使用Node,Vue和ElasticSearch构建实时搜索引擎-程序员宅基地

文章浏览阅读337次。node vue 实时推送 介绍 (Introduction)Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. Elasticsearch is built on top of Apache Lucene, wh..._vue elasticsearch

CCS中的IER和IFR寄存器:Symbol ‘IER‘ could not be resolved_symbol 'ier' could not be resolved-程序员宅基地

文章浏览阅读9.1k次,点赞16次,收藏53次。问题现象main函数初始化时,关闭CPU的中断使能,清除不断标志,一般都是这么写的: IER = 0x0000; IFR = 0x0000;但是,CCS却提示:Symbol 'IER' could not be resolved可是呢,编译整个工程时,也不会报错。<Linking>Finished building target: "DCDC.out""D:/ti/ccs1040/ccs/utils/tiobj2bin/tiobj..._symbol 'ier' could not be resolved

推荐文章

热门文章

相关标签