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

智能推荐

毕业设计django新闻发布和评论管理系统-程序员宅基地

文章浏览阅读587次,点赞6次,收藏14次。因为媒体的宣传能够带给我们重要的信息资源,新闻发布和评论管理是国家管理机制重要的一环,,面对这一世界性的新动向和新问题,新闻发布如何适应新的时代和新的潮流,开展有效的信息服务工作,完成时代赋予的新使命?在新闻发布和评论管理系统开发之前所做的市场调研及其他的相关的管理系统,都是没有任何费用的,都是通过开发者自己的努力,所有的工作的都是自己亲力亲为,在碰到自己比较难以解决的问题,大多是通过同学和指导老师的帮助进行相关信息的解决,所以对于新闻发布和评论管理系统的开发在经济上是完全可行的,没有任何费用支出的。

python中set详解_python set-程序员宅基地

文章浏览阅读1.7w次,点赞44次,收藏177次。python中set方法详解以及案例说明超详细_python set

【贝壳】数据分析岗位笔试+四面面经_贝壳如果去新的城市扩张需要看哪些指标-程序员宅基地

文章浏览阅读1.3w次,点赞5次,收藏16次。贝壳是今年秋招得第一个offer,因此回馈网友,记录一下贝壳得笔试+四面:【笔试】笔试是去公司现场面之前,给的一套题目,包括一下几题:1.找出数据中的错误数据,并给出理由2.贝壳如果去新的城市扩张,需要看哪些指标3.聚类分析有哪几种,说说其中一种得原理以及计算步骤4.一道SQL,需要用到rank()函数即可笔试不刷人,然后直接带着自己做的笔试卷子,去一面【一面】1.万年自我介绍..._贝壳如果去新的城市扩张需要看哪些指标

java计算机毕业设计计算机公共课程学习资源管理系统源码+mysql数据库+系统+lw文档+部署-程序员宅基地

文章浏览阅读58次。java计算机毕业设计计算机公共课程学习资源管理系统源码+mysql数据库+系统+lw文档+部署。springboot基于Springboot的滑雪场学具租赁管理系统。ssm基于HTML的“牧经校园疫情防控网站”的设计与实现。springboot基于springBoot公告管理。JSP企业进销存系统的设计与实现SQLServer。JSP服装销售网站的设计与实现sqlserver。ssm基于Vue.js的音乐播放器设计与实现。springboot多维分类的知识管理系统。

什么是前端开发?什么是后端开发?-程序员宅基地

文章浏览阅读1.9w次,点赞29次,收藏46次。什么是前端开发?概念我们通常所说的前端开发通常指的是网站的创建网站是一个用户界面,也就是你在网站上看到的的文本、按钮、图像还有视图。那么创建这些可交互界面的实践就叫做WEB界面的开发就像是你在银行里取钱不是直接往库存里伸手去拿,而是通过ATM这个银行的前端去进行一些操作尽管我们说的前端开发尽管通常指网站开发,但是现如今的前端开发已经是全平台的开发了,例如手机APP、微信小程序等。所以准确的来说前端开发,应该是直接给予用户的可交互式界面的开发,是创建用户界面的实践让用户以安全且友好的方式与数据_后端开发

Binary XML file line #40: Error inflating class ImageView_binary xml file line #40: binary xml file line #40-程序员宅基地

文章浏览阅读1.6k次。ImageView中src的图片放到mipmap中基本就好了_binary xml file line #40: binary xml file line #40: error inflating class an

随便推点

c# controls.add 控件的使用 ,间接引用还是值引用_this.controls.add-程序员宅基地

文章浏览阅读8.6k次。c# controls.add 控件的使用 10函数内部用下面代码增加控件:Button btn = new Button();btn.Location = new Point( 20, 20);btn.Size = new Size( 60,40);btn.Text = "btn'sText";this.Controls.Add( btn );问题:函数执行完后,bt_this.controls.add

占位式插件化一Activity的跳转_activity跳转 插件-程序员宅基地

文章浏览阅读329次。原理宿主APP安装在手机中的APP,并且通过该APP加载插件中的Activity插件APP没有安装的apk,通过宿主直接打开其内部Activity标准(协议)宿主APP和插件APP通信的桥梁。宿主APP通过一个空壳Activity(代理Activity)加载插件app中的Activity,实际上插件app中的Activity并没有入栈,也没法入栈,因为插件app没有安装,没有上下文和..._activity跳转 插件

PTA 剥洋葱(C语言 + 详细注释 + 代码超简单)_c语言pta怎么使用-程序员宅基地

文章浏览阅读1.0k次,点赞9次,收藏13次。输入格式:一行,一个整数,即图形的层数输出格式:如上述图形输入样例:3输出样例:AAAAAABBBAABCBAABBBAAAAAA//打印图形题关键是找规律,一般只需两重循环(行循环、列循环)#include<stdio.h>#include<string.h>int main() { int i, n; char ..._c语言pta怎么使用

docker配置国内镜像源_docker国内镜像源-程序员宅基地

文章浏览阅读3.3w次,点赞9次,收藏25次。刚开始学习docker,发现下载镜像非常的慢。如果不经过,docker的镜像下载都来源于国外,因此需要配置国内的镜像源。Docker中国区官方镜像。_docker国内镜像源

Unity中怎么播放视频_unity 播放视频-程序员宅基地

文章浏览阅读1.9w次,点赞40次,收藏209次。一.首先在场景中新建UI中的Raw Image可以按住Alt再点击下图红色箭头所示将Raw Image铺满游戏全屏(也可以自己调整大小)二.给Raw Image添加Video Player组件三.在Assets或者自己想要的文件夹中创建Render Texture四.将准备好的视频(这里用到的视频格式是mp4)拖入项目中并做如下修改这里我把新建的Render Texture命名为2,拖入的视频也命名为2(随便命的,不要在意)这里我们看到这个Render Te..._unity 播放视频

使用BOOTICE 恢复系统启动项_bootice保存后没用-程序员宅基地

文章浏览阅读9.7k次,点赞2次,收藏9次。使用BOOTICE 恢复系统启动项我在安装deepin 系统的时候,经常遇到重启进不去系统,每次重启都会进入windows 系统,这让我感到特别头疼,试了好多次都不成功,有些情况是,成功后再次重启又回到了windows系统。后来终于在PE中利用一款叫做BOOT ICE的工具成功解决。BOOTICE— 引导扇区维护工具简介BOOTICE 是一个启动相关的维护的小工具,主要用于安装、修复、备份和恢复磁盘_bootice保存后没用