Go红队开发—CLI框架(二)

news/2025/3/31 3:15:12/文章来源:https://www.cnblogs.com/dhan/p/18797190

目录
  • CLI开发框架
    • cobra 集成库
      • 目录规范
      • 搭建框架
        • 根命令
        • 参数添加
        • 子命令
        • 帮助信息
  • 爬虫功能(趁热打铁)
    • Goquery处理响应
    • 编码处理
    • 收集百度热搜榜
    • 爬虫功能所有源码

CLI开发框架

师傅们久等了,为了加快进度,这章节添加了一个爬虫功能,也是后面写工具要用到的。
学习成果:能够集成一个爬虫功能到工具中
如下图所示

cobra 集成库

cobra 是一个cli程序脚手架,大是大了点,但是有规范模版,同时也很好用,代码分明(本人比较喜欢用这个)

以下仅仅是个人开发中常用到的,涉及比较浅,但是用来设计属于自己的小工具应该是足够的哈!依旧是修行靠个人。

  • 下载
go get github.com/spf13/cobra/cobra

目录规范

虽然说这个库也可以随便创建来使用,但是我十分推荐下面这个模版,清晰而且显得专业。

▾ 项目/▾ cmd/cmd1.gocmd2.gocmd3.goroot.go▾其他(util)util.gosql.go...main.go

这里先讲一下目录结构:

  • cmd:
    就是你的命令放在该目录的root里面,子命令就是你创建的一些比如cmd1或者cmd2文件就是一些以后扩展的时候自行扩展,不是说一定要这些文件,主要文件需要一个就行,root也可以不叫root,只不过他是告诉你需要一个核心命令文件
  • main.go是必须的,一般都叫main,你也可以重命名其他,只不过代码中package main的main要换名字(小知识点在这里补充了)

搭建框架

细节:

  • 一般接收参数的变量都是放在全局
  • 重复一遍:记得go mod tidy 导入包,这是三方的,不是go默认自带,需要导入使用,就算你下载了也要导入。

根命令

root.go根命令
记住两点:

  • 结构体实现:&cobra.Command
  • Execute()函数实现,这个是写给main函数调用的,所以你起什么名字都行,不一定按照我这个名字。
package cmdimport (    "fmt"    "github.com/spf13/cobra")var rootCmd = &cobra.Command{    Use:   "命令名字",    Short: "短描述",    Long:  `长描述`,    Run: func(cmd *cobra.Command, args []string) {        fmt.Println("Hello, cobra!")
    },
}func Execute() error { 
    return rootCmd.Execute()//执行命令
}

main.go

  • 调用root中给的Execute函数即可
package mainimport (
    "go_cobra/cmd"
)func main() {cmd.Execute()
}

参数添加

  • 实现init()函数,这个就是库会自动调用,我们只要负责实现,该函数即可, 这个函数主要是用来配合rootCmd的,也就是说我们的根命令,init就是来给他添加东西,比如添加参数,添加子命令(后面会讲)
  • 全局与作用在单个命令中
    单个:Flags
    全局:PersistentFlags
    单个即作用与某一个命令之下,比如root也算一个命令,但是他是根命令,假设我们有一个子命令,子命令下使用了这个Flags就是你该参数只能作用与这个子命令中
    如果你写的是PersistentFlags作用是:你当前命令下的其他子命令也能使用

我这里添加两个参数,一个是本地的,意思是只有在root根命令才能使用,另一个是全局,当后面创建了子命令的时候,子命令也能使用我这个参数
变量如下:

var (    P  string //打印参数    PP string //全局打印参数)

初始化函数init()
默认值为:nil

func init() {
    rootCmd.Flags().StringVarP(&P, "print", "p", "nil", "打印")              //添加参数
    rootCmd.PersistentFlags().StringVarP(&PP, "Print", "P", "nil", "全局打印") //添加全局参数}

解释一下添加参数的函数,Flags为例子,PersistentFlags一样的。
这俩函数下有参数类型可以选择,所以说不仅仅是string可以作为参数值传入,当你的变量类型为bool的时候可以是:
rootCmd.Flags().BoolVarP

  • 第一个参数:接收用户传入的参数值了,用变量接收,变量类型就是要看啊刚刚说的你用什么类型的函数了。
  • 第二个参数:用户完整选项,也就是长选项
  • 第三个参数:用户短选项
  • 第四个参数:该选项的默认值
  • 第五个参数“:该参数命令描述

运行结果

全局命令区别在下面的子命令中区分实现。

子命令

在cmd文件夹里创建一个version.go文件

  • 结构体实现:&cobra.Command,和根命令以一样的类型,所以很多东西都是可以用的,添加子命令也时添加这个类型到根命令中。
  • 依旧是实现 init() 函数,在函数里面将你的子命令添加进去即可。

这里就加一个版本命令,工具经常要写的一个子命令。
在init中使用:rootCmd.AddCommand(versionCmd),意思就是根命令中添加一个子命令versionCmd
在Run中同时也写了之前的全局参数PP的操作:也就是说全局虽然说是全局,但是在代码里面并非真的自动调用,而是需要你手动写进去,他全局的意思是全局接受,负责操作的依旧是在你当前子命令中, 他不会因为是某个命令下的全局参数而直接在你这个子命令中自动调用哈!!

代码如下:
cmd/version.go

package cmdimport (
    "fmt"    "github.com/spf13/cobra"
)var versionCmd = &cobra.Command{
    Use:   "version",
    Short: "显示版本",
    Long:  `显示xxxx版本`,
    Run: func(cmd *cobra.Command, args []string) {
        fmt.Println("工具当前版本:v1.0.0")
        if PP != "nil" {            fmt.Println(PP)        }
    },
}func init() {
    //将versionCmd添加到rootCmd
    //这样在执行命令时,就可以使用version这个子命令了
    rootCmd.AddCommand(versionCmd)
}

帮助信息

当然cobra这么优秀的框架肯定也有自动的帮助信息哈哈,我们只需要写好自己的功能代码即可。


最后我们的目录结构是:

爬虫功能(趁热打铁)

Goquery处理响应

Goquery爬虫必备包

下载

go get -u github.com/PuerkitoBio/goquery

使用

  • goquery.NewDocumentFromReader负责解析文本内容返回*goquery.Document对象
  • *goquery.Document对象根据你给的css选择器查找元素,这个查找的元素是将你这个css选择器在内容中所有元素都查找出来,所以不必担心只查找到一个而已。

随便爬一下百度的某个元素:
这里仅仅展示爬取一个元素,因为图片中教学的是复制完整的css选择器路径,更多css选择器自行去学习。

func testClimb() {
    fmt.Println("测试爬取百度热点功能")
    client := req.C()
    data, _ := client.R().Get("https://top.baidu.com/board?top=realtime")
    doc, _ := goquery.NewDocumentFromReader(data.Body)  //解析网页
    doc.Find("#sanRoot > main > div.hot-wrap_1nNog > div.theme-hot.category-item_1fzJW > div.list_1EDla > a:nth-child(7) > div.normal_1fQqB > div.content-wrap_1RisM > div > div").Each(func(i int, s *goquery.Selection) { //查找类名为c-single-text-ellipsis的元素
    fmt.Println(strings.TrimSpace(s.Text())) //打印标题
    })}

运行结果

编码处理

有一个单独处理某些编码的库,这个库有需要的自行学习句即可:

github.com/djimenez/iconv-go

这里仅仅展示一个比较通用的编码处理方式

go get -u golang.org/x/text

参考文章中作者已经写好了工具函数,拿来就用即可,我们写一些小工具来使用的话就不建议重复造轮子了,
使用方式:

utf8Body, err := DecodeHTMLBody(res.Body, "")

工具函数代码如下:

func detectContentCharset(body io.Reader) string {r := bufio.NewReader(body)if data, err := r.Peek(1024); err == nil {if _, name, _ := charset.DetermineEncoding(data, ""); len(name) != 0 {return name}}return "utf-8"
}func DecodeHTMLBody(body io.Reader, charset string) (io.Reader, error) {if charset == "" {charset = detectContentCharset(body)}e, err := htmlindex.Get(charset)if err != nil {return nil, err}if name, _ := htmlindex.Name(e); name != "utf-8" {body = e.NewDecoder().Reader(body)}return body, nil
}

参考文章的作者还找到一篇远古gbk编码的html网页来练习:
https://news.sina.com.cn/society/netsurvival/

charset.DetermineEncoding会根据 HTML 页面中的 meta 元信息猜测网页编码。

由于我的终端编码类型是gb2312,不转换就能直接解析了,我转为utf8反而还乱码了,所以这里就不演示了
运行结果:(结尾会给出所有源码)

收集百度热搜榜

参考文章:https://darjun.github.io/2020/10/11/godailylib/goquery/

这里一样的功能,读取top榜单,但是我们读取的是榜单,所以不能直接复制单个元素了,要会一点选择器操作
三个class标签直接用.符号来取至于子元素、后代元素用空格还是>我还是简单说一下吧(忍不住)
空格:后代元素,即你孩子的孩子也能够匹配到
>:子元素,仅仅代表你的子,即你生的下一代,不代表你下一代的下一代,所以只能取到下一层的元素。
我这里的结构其实用空格还是>都行,因为结构比较简单

运行结果


函数功能如下:

func baiduHotspot() {    // fmt.Println("爬取百度热点功能")    client := req.C()    data, _ := client.R().Get("https://top.baidu.com/board?top=realtime")    doc, _ := goquery.NewDocumentFromReader(data.Body)                                                          //解析网页    doc.Find(".content-pos_1fT0H .name_2Px2N .c-single-text-ellipsis").Each(func(i int, s *goquery.Selection) { //查找类名为c-single-text-ellipsis的元素        title := s.Text() //获取文本内容        res := "\t" + strconv.Itoa(i) + ":" + strings.TrimSpace(title)        fmt.Println(res) //打印标题    })}

爬虫功能所有源码

我把功能用到了cobra框架里
目录结构如下,记得创建文件

帮助命令


cmd/root.go文件

package cmdimport (    "bufio"    "fmt"    "io"    "strconv"    "strings"    "github.com/PuerkitoBio/goquery"    "github.com/imroc/req/v3"    "github.com/spf13/cobra"    "golang.org/x/net/html/charset"    "golang.org/x/text/encoding/htmlindex")var (    P     string //打印参数    PP    string //全局打印参数    Climb bool   //爬取百度热点功能    Testc bool   //测试爬虫功能)func testClimb() {    // fmt.Println("测试爬取百度热点功能")    client := req.C()    data, _ := client.R().Get("https://top.baidu.com/board?top=realtime")    doc, _ := goquery.NewDocumentFromReader(data.Body)                                                                                                                                                                      //解析网页    doc.Find("#sanRoot > main > div.hot-wrap_1nNog > div.theme-hot.category-item_1fzJW > div.list_1EDla > a:nth-child(7) > div.normal_1fQqB > div.content-wrap_1RisM > div > div").Each(func(i int, s *goquery.Selection) { //查找类名为c-single-text-ellipsis的元素        fmt.Println(strings.TrimSpace(s.Text())) //打印标题    })}func detectContentCharset(body io.Reader) string {    r := bufio.NewReader(body)    if data, err := r.Peek(1024); err == nil {        if _, name, _ := charset.DetermineEncoding(data, ""); len(name) != 0 {            return name        }    }    return "utf-8"}func DecodeHTMLBody(body io.Reader, charset string) (io.Reader, error) {    if charset == "" {        charset = detectContentCharset(body)    }    e, err := htmlindex.Get(charset)    if err != nil {        return nil, err    }    if name, _ := htmlindex.Name(e); name != "utf-8" {        body = e.NewDecoder().Reader(body)    }    return body, nil}func testClimb2() {    client := req.C()    data, _ := client.R().Get("https://news.sina.com.cn/society/netsurvival/")    // 将 data.Body 转换为 io.Reader    // decodedBody, _ := DecodeHTMLBody(bytes.NewReader(data.Bytes()), "") // 解码网页    doc, _ := goquery.NewDocumentFromReader(data.Body)               // 解析网页    doc.Find(".title14 li").Each(func(i int, s *goquery.Selection) { // 微博72小时网络生存测试        fmt.Printf("%d:%s\n", i, strings.TrimSpace(s.Text())) // 打印标题    })}func baiduHotspot() {    // fmt.Println("爬取百度热点功能")    client := req.C()    data, _ := client.R().Get("https://top.baidu.com/board?top=realtime")    doc, _ := goquery.NewDocumentFromReader(data.Body)                                                              //解析网页    doc.Find(".content-pos_1fT0H > .name_2Px2N > .c-single-text-ellipsis").Each(func(i int, s *goquery.Selection) { //查找类名为c-single-text-ellipsis的元素        title := s.Text() //获取文本内容        res := "\t" + strconv.Itoa(i) + ":" + strings.TrimSpace(title)        fmt.Println(res) //打印标题    })}var rootCmd = &cobra.Command{    Use:   "命令名字",    Short: "短描述",    Long:  `长描述`,    Run: func(cmd *cobra.Command, args []string) {        if P != "nil" {            fmt.Println(P)        }        if PP != "nil" {            fmt.Println(PP)        }        if Climb {            fmt.Println("爬取百度热点功能")            baiduHotspot()        }        if Testc {            fmt.Println("测试爬虫功能")            // testClimb()            testClimb2()        }    },}func Execute() error {    return rootCmd.Execute() //执行命令,这个是给main函数调用的}func init() {    rootCmd.Flags().StringVarP(&P, "print", "p", "nil", "打印")              //添加参数    rootCmd.PersistentFlags().StringVarP(&PP, "Print", "P", "nil", "全局打印") //添加全局参数    rootCmd.Flags().BoolVarP(&Climb, "climb", "c", false, "爬取百度热点功能")      //添加爬取百度热点功能参数    rootCmd.Flags().BoolVarP(&Testc, "testc", "t", false, "测试爬虫功能")        //添加测试爬虫功能}

cmd/version.go 文件

package cmdimport (    "fmt"    "github.com/spf13/cobra")var versionCmd = &cobra.Command{    Use:   "version",    Short: "显示版本",    Long:  `显示xxxx版本`,    Run: func(cmd *cobra.Command, args []string) {        fmt.Println("工具当前版本:v1.0.0")        if PP != "nil" {            fmt.Println(PP)        }    },}func init() {    //将versionCmd添加到rootCmd    //这样在执行命令时,就可以使用version这个子命令了    rootCmd.AddCommand(versionCmd)}

main.go 文件

package mainimport (    "go_cobra/cmd")func main() {    cmd.Execute()}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/906933.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

APP性能测试工具-GT

GT(随身调)是腾讯研发的一款可以用来做App性能测试的工具,可以对APP进行快速的性能测试,检测App的CPU、内存、流量、电量、帧率/流畅度等等、还能开启日志的查看、Crash日志查看、网络数据包的抓取、APP内部参数的调试、真机代码耗时统计等。 虽然现在该项目已经停止维护了…

一文速通Python并行计算:04 Python多线程编程-多线程同步(上)—基于条件变量、事件和屏障

本文介绍了Python多线程同步的三种机制:条件变量(Condition)、事件(Event)和屏障(Barrier),条件变量指的是线程等待特定条件满足后执行,适用于生产者-消费者模型;Event指的是线程通过事件标志进行同步,适用于线程间简单通信;Barrier指的是多个线程需同步到同一阶段…

docker desktop windows安装

我的机器windows 11 家庭版 下载docker desktop for windows 就直接安装了。安装后打开,遇到了界面转圈圈加载不出来问题,docker engine也是stopped. 病急乱投医,先是说要启用hyper-v,控制面板=》程序和功能里没有发现有hyper-v,一看是家庭版,网上倒是有一个脚本可以在家…

C语言打卡学习第6天(2025.3.25)(补发)

只做了一些有关循环分支函数求值的题,感觉循环函数其实差不多,只有一些细微差别,可能是做的题还不够多或者看运用场景吧

C语言打卡学习第5天(2025.3.24)(补发)

1、把char,getchar,putchar简单看了一下,求ascii值之类的 之类的简单看了一下 2、交换值那一题很奇怪,结果我输出的跟答案要求是一样的,交过去之后显示答案错误,白天的时候问一下

Vulnstack红日靶场通关(持续更新)

带你速通内网渗透相关知识点!!!Vulnstack通关 来源于《内网渗透实战攻略》实战部分 个人是写下自己的笔记 攻击链:探索发现阶段->入侵和感染阶段->攻击和利用阶段->探索感知阶段->传播阶段->持久化和恢复阶段 Windows权限级别前置知识:权限层级 账户类型 权…

Ubuntu 24.04安装MySQL,并且配置外网访问

安装启动更新软件包列表sudo apt update安装MySQL软件包sudo apt install mysql-server启动MySQL服务sudo systemctl start mysql重启命令:systemctl restart mysql配置外网访问 需要修改一个配置 vim /etc/mysql/mysql.conf.d/mysqld.cnf注释掉 这行 配置 bind-address …

2022CCPC Online Contest G - Name the Puppy

对正串和反串分别建立 Trie 树,定义 \(dp[i][j]\) 表示正串 Trie 树上编号为 \(i\) 的点匹配反串 Trie 树上编号为 \(j\) 的点所能拼出最长 anti-border 的长度。 如此,从根节点开始搜索,直到无法匹配为止都可以搜,搜到底后回到根节点继续匹配,可以证明,拼出来的 anti-bo…

互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(四):结合BotSharp智能体框架开发语音交互

前言 前段时间太忙了博客一直都没来得及更新,但是不代表我已经停止开发了,刚好最近把语音部分给调整了一下,所以就来分享一下具体的内容了。我想说一下,更新晚还是有好处的,社区已经有很多的小伙伴自己实现了一些语音对话功能的案例,比如小智也有.NET客户端了,还有就是一…

【AI News | 20250327】每日AI进展

AI Repos 1、playwright-mcp 使用Playwright提供浏览器自动化功能的MCP服务,核心是让LLM通过结构化的可访问性快照与网页交互,不需要依赖截图或视觉模型。可以用来自动填写网页表单、自动收集网页信息、自动进行网页测试等。支持两种模式:快照模式(默认):使用可访问性快照…

markdown常用命令行格式

Markdown 主要命令(语法)如下:标题 使用 # 号表示标题,# 的个数决定标题的级别:一级标题 二级标题 三级标题 四级标题 五级标题 六级标题段落 & 换行 直接输入文字形成段落,使用两个以上空格或 进行换行:这是一个段落。 这是同一段的下一行。 使用 <br> 也可…

微调可以获得什么

1.改变模型的行为: 使模型的响应更稳定; 使模型聚焦于某一领域; 发展期潜力,在某一方面更加出色,比如对话 2.获取新的知识: 学习预训练阶段没学过的知识; 纠正过时的错误和信息;