一次学以致用的经历|青训营笔记

179 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记

摘要

我的一次学以致用的经历,用在青训营里面学到知识,实实在在的帮助到同学,我觉得很开心,这使我认识到我们学的东西是非常有意义的,并且能够清晰的感觉到自己存在的价值。这种被需要的感觉还挺好的。我想把它记录下来。

背景介绍

第一节课上王老师给我们讲了一个简单在线词典程序,教我们如何分析网页请求,然后拿到结果进行处理,以达到我们的预期的目的,里面有两个工具还挺好用的,当时就随手放到收藏夹吃灰了。没想到会有翻出来的一天。

  1. Convert curl commands to code)

  2. JSON转Golang Struct - 在线工具 - OKTools

昨天恰好有个同学call我,ta说ta有个任务需要完成,就是在一个系统上进行审批,但是大概有509050 * 90个条目需要审批。ta进行这样的重复劳动好累,问我可以不可以帮ta完成(内心os:当然不可以了,一个一个点我也会好累)。所以我的回答是那必不可能那必不可能。但是也许可以偷偷懒,写个程序解决。

需求分析

听完ta的描述,我大概知道这应该是一个可以用程序解决的问题。我的脑海想到了两个方案:

  1. 用手机中比如说按键精灵之类的软件,循环模拟点击。
  2. 在浏览器里模拟点击也行。

但是这个看起来不够优雅,关键点来了,就在这个时候我想起了课上学的知识,突然就涌入我的脑海。我迅速打开浏览器,登录系统,开启检查,找到network分析了一下这个审批过程,原来每一次点击审批,都是对服务器发起的一个post请求,就是每次的参数都不一样。我内心一想!这。。。这不是和课上的简单在线词典程序差不多吗?

概要设计

我初步想了一下这个程序的流程应该怎么写

  1. 模拟登录系统
  2. 爬取网页内容
  3. 筛选需要的参数
  4. 对每一个参数发起请求 第一步很好实现,因为ta把账号密码告诉我了,我直接拿到了cookie,然后登录即可

第二步开始百度,哈哈哈哈哈,笑死,百度了一个用go爬网页的程序,然后改改改。书到用时方恨少,这个时候用到了第一个工具,到网页中找到获取网页内容的那个get请求,然后copy as cURL(bash)

image.png

拿到这个之后,到第一个工具的网址,ctrl C + V,就生成了我们想要的东西,不得不说确实非常的方便!!!

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	client := &http.Client{}
	req, err := http.NewRequest("GET", "https://zhsz.csedu.gov.cn/archive/content?stuId=2191367&reportState=1&audit=1", nil)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Connection", "keep-alive")
        //本行是cookie信息,我已经替换
	req.Header.Set("Cookie", "xxxx")
	req.Header.Set("Pragma", "no-cache")
	req.Header.Set("Referer", "https://zhsz.csedu.gov.cn/archive/audit?stuId=2191367&reportState=1")
	req.Header.Set("Sec-Fetch-Dest", "iframe")
	req.Header.Set("Sec-Fetch-Mode", "navigate")
	req.Header.Set("Sec-Fetch-Site", "same-origin")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Mobile Safari/537.36")
	req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`)
	req.Header.Set("sec-ch-ua-mobile", "?1")
	req.Header.Set("sec-ch-ua-platform", `"Android"`)
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", bodyText)
}

仔细观察后我才发现原来是有规律的,注意到这些urls是用stuId的不同来区分的,然后到系统里找到第一个和最后一个,一个循环可以搞定。(当时的我这一步想了好久,还以为要一个一个爬下来)拿到目标urls之后,对每一个url发起get请求即可。

第三步 开始获取目标参数,同样也是分析网页,发现每一次的审核其实就是对服务器的一次post请求,就是每次的参数不一样。所以我们就需要从第二步中拿到HTML进行筛选,拿到这个网页中所有的gid,实现这一步的方法有很多,可是我一个都不会,我裂开

xpathselector正则等等都可以完成我们的目的

image.png

那没办法,现学咯,咔咔咔,又是一个小时过去了,以我现在的视角来看,我应该几分钟就学完的,虽然磕磕碰碰,但所幸好像学到一点东西,还是能筛选出来的,就是有重复gid凑合用吧

第四步 就是拿到gids之后,对每一个gid发起请求即可,还是同样的复用上面的步骤。

image.png

到第一个工具中生成如下代码,然后封装成一个Post函数

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`gid=52f5d0b0a4df4383aeb1fd413756a390&auditState=-1`)
	req, err := http.NewRequest("POST", "https://zhsz.csedu.gov.cn/archive/doAuditOne", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	// 本行已经替换
        req.Header.Set("Cookie", "xxxx")
	req.Header.Set("Origin", "https://zhsz.csedu.gov.cn")
	req.Header.Set("Pragma", "no-cache")
	req.Header.Set("Referer", "https://zhsz.csedu.gov.cn/archive/content?stuId=2191360&reportState=1&audit=1")
	req.Header.Set("Sec-Fetch-Dest", "empty")
	req.Header.Set("Sec-Fetch-Mode", "cors")
	req.Header.Set("Sec-Fetch-Site", "same-origin")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Mobile Safari/537.36")
	req.Header.Set("X-Requested-With", "XMLHttpRequest")
	req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`)
	req.Header.Set("sec-ch-ua-mobile", "?1")
	req.Header.Set("sec-ch-ua-platform", `"Android"`)
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", bodyText)
}

详细设计

func GetUrls(start, end int) []string

输入:开始stuid和结束stuid 闭区间[start, end]

输出:一个包含urls的字符串切片

func GetHtml(url string) string

输入:一个url字符串

输出:该地址的html文本

func GetGids(htmls string) []string

输入:一串html文本

输出:一个包含gids的字符串切片

func Post(gid string)

输入:字符串gid

输出:打印请求返回的响应,以此判断请求是成功还是失败。

编码

我的详细代码就不放了,写得太垃圾了,我不想放出来丢人现眼,就放一个main

func main(){
	urls := GetUrls(2191405, 2191405)
	for _, url := range urls{
		// fmt.Println(url)
		html := GetHtml(url)
		// fmt.Print(html)
		gids := GetGids(html)
		for _, gid := range gids{
			Post(gid)
			// fmt.Println(gid)
		}
	}
}

总结

其实事后回顾这个过程,其实会了就非常之简单。不会就千难万难,但是我却这样磕磕碰碰才完成,说明我以前欠下了很多债,终究是要还的!所以这一次的经历给我的体会很深,活到老,学到老。