go简单爬虫案例实践

go爬虫简单实践

使用go语言原生的net包实现简单爬虫

网络爬虫大致步骤

①、创建请求代码:包括设置请求url,设置代理服务器,设置请求头

②、执行请求,获取响应结果,关闭请求流

③、从响应结果中提取有效数据

④、将数据保存到本地,比如excel表

案例需求

需求:某网站的公开了国内某市各个县的的日均降雨量,网页内容就是某天的降雨量表,在请求url中包含了日期字段,可以通过修改请求url中的日期字段来获取不同日期的降雨量,现需要爬取该网站上若干年的降雨数据。

代码实现

首先创建单次的请求代码,也就是只获取一天的数据,可以借助http请求代码生成网站去生成对应代码。

具体步骤为:按F12打开浏览器开发者模式,刷新网页内容,找到返回了你想要的数据的那个http请求,右键这个请求,点复制为curl(bash),然后粘贴到http请求代码生成网站中,选择go语言,既可获取go语言的请求代码。

// 单次查询请求
func queryData(date string) {
	fmt.Println("=========================================================" + "正在获取:" + date + "的数据=========================================================")
	client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}}
	//设置请求url,依照具体网页而定
	var url = "http://XXX/XXX/XXX?date=" + date + "+type=1"
	//创建请求
	req, err := http.NewRequest("GET", url, nil)
	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,en-US;q=0.8,en;q=0.7")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Referer", "http://http://XXX/XXX/XXX")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36")
	req.Header.Set("X-Requested-With", "XMLHttpRequest")
	//执行请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	//关闭请求流
	defer resp.Body.Close()
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	var responsejson DictResponse
	//将json格式的bodyText反序列化为struct格式
	err = json.Unmarshal(bodyText, &responsejson)
	if err != nil {
		log.Fatal(err)
	}
	//打印responsejson中的具体字段
	for _, item := range responsejson.Rows {
		fmt.Println(item)
	}
	//通过save函数保存在本地
	save(responsejson, date)
}

当然,上述代码的反序列化操作前要定义好结构体,这里假设需要的数据包含【县区】、【日降雨】、【时间】这三个字段,其中的Rows数组的长度有多长就代表了一个表格里有多少行数据

type DictResponse struct {
	Rows []struct {
		COUNTY             string      `json:"COUNTY"`    //所在县
        RAINFALL           string      `json:"RAINFALL"`  //降雨量
		DATE               string      `json:"DATE"`      //日期
	} `json:"rows"`
}

然后具体定义save函数,用于把一次请求的数据保存到本地,这里以保存为excel表格数据为例,用到了第三方的库“github.com/360EntSecGroup-Skylar/excelize”库,需要提前安装。

// 保存为excel文件
func save(resp DictResponse, date string) {
	// 创建一个新的Excel文件
	f := excelize.NewFile()

	// 创建一个名为“Sheet1”的工作表
	const sheetName = "Sheet1"
	index := f.NewSheet(sheetName)

	//设置第一行的标题
    f.SetCellValue(sheetName, "A1", "县区")
	f.SetCellValue(sheetName, "B1", "降雨量")
	f.SetCellValue(sheetName, "C1", "日期")

	row := 2 // 从第二行开始写具体数据

	//将各个县的降雨数据写入表中
	for _, x := range resp.Rows {
		f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), x.COUNTY)
		f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), x.RAINFALL)
		f.SetCellValue(sheetName, fmt.Sprintf("C%d", row), x.DATE)
		row += 1
	}

	// 设置默认工作表
	f.SetActiveSheet(index)

	//按年份划分文件夹
	end := strings.Index(date, "-")
	dir := date[:end]
	//父文件夹不存在则创建
	if PathExists(dir) == false {
		os.MkdirAll(dir, os.ModePerm)
	}
	fmt.Println(dir)

	// 将工作表保存为文件
	if err := f.SaveAs("./" + dir + "/data_" + date + ".xlsx"); err != nil {
		panic(err)
	}
	fmt.Println("写入" + "data_" + date + ".xlsx" + "成功!")
}

// PathExists 判断一个文件或文件夹是否存在
// 输入文件路径,根据返回的bool值来判断文件或文件夹是否存在
func PathExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

最后,为了方便连续获取某一时间段的降雨数据,以起止时间作为参数,写一个批量请求函数,按照时间顺序依次请求每天的数据,每次请求后休眠0-1秒


func queryDataAll(startDate string, endDate string) {
	// 设置时间模板格式
	myLayout := time.DateOnly
	// 转为time
	start, _ := time.Parse(myLayout, startDate)
	end, _ := time.Parse(myLayout, endDate)
	fmt.Println("开始时间:" + start.String() + " 结束时间:" + end.String())
	for current := start; current.Before(end) == true || current.Equal(end); current = current.Add(time.Hour * 24) {
		temString := current.Format(myLayout)
		//执行请求
		queryData(temString)
		//随机休眠0-1s
		rand.Seed(time.Now().UnixNano())
		sleep_time := rand.Float32()
		fmt.Println("休眠", sleep_time, "秒")
		time.Sleep((time.Duration(sleep_time)) * (time.Second)) //休眠
	}
}

最后把如上代码整个到一个go文件汇中,并添如下的导包及main函数定义既可。

package main

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"
	"github.com/360EntSecGroup-Skylar/excelize"
)
func main() {
	queryDataAll("1990-01-01", "1999-12-31")
}

此时在main函数中调用queryDataAll函数即可实现爬取某一时间段的数据,并保存在本地。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值