diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..c41f69f Binary files /dev/null and b/.DS_Store differ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100755 index 0000000..a7cdac7 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Sowhp.iml b/.idea/Sowhp.iml new file mode 100755 index 0000000..338a266 --- /dev/null +++ b/.idea/Sowhp.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100755 index 0000000..705d1fe --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..35f0bf3 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Sowhp diff --git a/Screenshot-of-website-homepage.txt b/Screenshot-of-website-homepage.txt new file mode 100755 index 0000000..e69de29 diff --git a/concert/directoriescreat.go b/concert/directoriescreat.go new file mode 100755 index 0000000..7563301 --- /dev/null +++ b/concert/directoriescreat.go @@ -0,0 +1,46 @@ +package concert + +import ( + "Sowhp/concert/logger" + "fmt" + "os" +) + +// MkdirResport 创建报告存放目录 +func MkdirResport() { + // 获取当前目录地址 + dir, err := os.Getwd() + if err != nil { + // panic(err) + logger.DebugError(err) + } + logger.Debug(fmt.Sprintf("当前路径:%s", dir)) + Dir_mk(dir + "/result/") + logger.Debug("当前目录下创建 /result/ 目录成功!") + +} + +// GetPath 获取当前路径位置 +func GetPath() string { + // 获取当前目录地址 + dir, err := os.Getwd() + if err != nil { + // panic(err) + logger.DebugError(err) + } + return dir +} + +// dir_mk 判断目录是否存在,若不存在则进行创建 +func Dir_mk(path string) { + // 判断目录是否存在 + if _, err := os.Stat(path); os.IsNotExist(err) { + err = os.Mkdir(path, 0777) + if err != nil { + // panic(err) + logger.DebugError(err) + } + logger.Debug(fmt.Sprintf("当前以创建好该目录:%s", path)) + return + } +} diff --git a/concert/logger/config.go b/concert/logger/config.go new file mode 100644 index 0000000..dfea6e3 --- /dev/null +++ b/concert/logger/config.go @@ -0,0 +1,8 @@ +package logger + +var ( + LogLevel int // log等级,默认设置3级 + NoColor bool // 是否开启log输出非颜色版设置 + OutputFileName string // 用于设置log输出名称设置 + NoSave bool // not save file // logsync.go 中设置不进行日志写入的设置, 注:在常规的logger中并没有设置该参数 +) diff --git a/concert/logger/level.go b/concert/logger/level.go new file mode 100755 index 0000000..c7b4c1c --- /dev/null +++ b/concert/logger/level.go @@ -0,0 +1,12 @@ +package logger + +type Level int + +const ( + LevelFatal Level = iota // 值 0 + LevelError // 值 1 + LevelInfo // 值 2 + LevelWarning // 值 3 + LevelDebug // 值 4 + LevelVerbose // 值 5 +) diff --git a/concert/logger/log.go b/concert/logger/log.go new file mode 100755 index 0000000..1055288 --- /dev/null +++ b/concert/logger/log.go @@ -0,0 +1,184 @@ +package logger + +import ( + "fmt" + "github.com/gookit/color" + "os" + "path" + "regexp" + "runtime" + "strings" + "time" +) + +var ( // 输出颜色设置 + Red = color.Red.Render + Cyan = color.Cyan.Render + Yellow = color.Yellow.Render + White = color.White.Render + Blue = color.Blue.Render + Purple = color.Style{color.Magenta, color.OpBold}.Render + LightRed = color.Style{color.Red, color.OpBold}.Render + LightGreen = color.Style{color.Green, color.OpBold}.Render + LightWhite = color.Style{color.White, color.OpBold}.Render + LightCyan = color.Style{color.Cyan, color.OpBold}.Render + LightYellow = color.Style{color.Yellow, color.OpBold}.Render + //LightBlue = color.Style{color.Blue, color.OpBold}.Render +) + +var ( + defaultLevel = LevelWarning // 输出等级 + noWrite int // 不进行写入log +) + +// SetLevel 设置输出等级 +func SetLevel(l Level) { + defaultLevel = l +} + +func getCallerInfo(skip int) (info string) { + _, file, lineNo, ok := runtime.Caller(skip) + if !ok { + info = "runtime.Caller() failed" + } + + // funcName := runtime.FuncForPC(pc).Name() + fileName := path.Base(file) // Base函数返回路径的最后一个元素 + // return fmt.Sprintf("FuncName:%s, file:%s, line:%d", funcName, fileName, lineNo) + return fmt.Sprintf("%s line:%d", fileName, lineNo) +} + +// log 日志输出效验判断 +func log(l Level, w int, detail string) { + switch LogLevel { // 判断配置文件中的日志输出等级,并设置到日志等级 + case 0: + SetLevel(0) + case 1: + SetLevel(1) + case 2: + SetLevel(2) + case 3: + SetLevel(3) + case 4: + SetLevel(4) + case 5: + SetLevel(5) + } + + if l > defaultLevel { // 判断输入等级是否大于设置等级,若大于则当前日志则不进行输出 + return + } + if NoColor { // 判断是否关闭颜色输出 + fmt.Println(clean(detail)) + return + } else { + fmt.Println(detail) + } + + if noWrite == 0 { + // 目前只写入 info 信息 和 Success 的信息 + strTrim := fmt.Sprintf("[%s] ", Cyan(getDate())) // 匹配log日志前面的日期 + detail := strings.TrimPrefix(detail, strTrim) // 去除日期信息 + writeLogFile(clean(detail), OutputFileName) // 写入到本地文件 + } + + if l == LevelFatal { + os.Exit(0) + } +} + +// Fatal 严重级别日志 log等级:0 +func Fatal(detail string) { + noWrite = 1 + log(LevelFatal, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("FATAL"), detail)) +} + +// Error 错误日志 log等级:1 +func Error(detail string) { + noWrite = 1 + log(LevelError, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("ERROR"), detail)) +} + +// Info 消息日志 log等级:2 +func Info(detail string) { + noWrite = 0 + log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("INFO"), detail)) +} + +// Warning 告警日志 log等级:3 +func Warning(detail string) { + noWrite = 1 + log(LevelWarning, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightYellow("WARNING"), detail)) +} + +// Debug 调试日志 log等级:4 +func Debug(detail string) { + noWrite = 1 + log(LevelDebug, noWrite, fmt.Sprintf("[%s] [%s] [%s] %s", Cyan(getDate()), LightWhite("DEBUG"), Yellow(getCallerInfo(2)), detail)) +} + +// Verbose 详细调试信息日志 log等级:5 +func Verbose(detail string) { + noWrite = 1 + log(LevelVerbose, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightCyan("VERBOSE"), detail)) +} + +// Success 成功信息日志 log等级:2 +func Success(detail string) { + noWrite = 0 + log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("+"), detail)) +} + +// Failed 失败信息日志 log等级:2 +func Failed(detail string) { + noWrite = 1 + log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightRed("-"), detail)) +} + +// Common 普通信息日志 log等级:2 +func Common(detail string) { + noWrite = 1 + log(LevelInfo, noWrite, fmt.Sprintf("[%s] [%s] %s", Cyan(getDate()), LightGreen("*"), detail)) +} + +func getTime() string { + return time.Now().Format("15:04:05") +} + +func getDate() string { + return time.Now().Format("2006.1.2") +} + +func DebugError(err error) bool { + /* Processing error display */ + if err != nil { + pc, _, line, _ := runtime.Caller(1) + Debug(fmt.Sprintf("%s%s%s", + White(runtime.FuncForPC(pc).Name()), + LightWhite(fmt.Sprintf(" line:%d ", line)), + White(err))) + return true + } + return false +} + +// Clean by https://github.com/acarl005/stripansi/blob/master/stripansi.go +func clean(str string) string { + const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" + var re = regexp.MustCompile(ansi) + return re.ReplaceAllString(str, "") +} + +func writeLogFile(result string, filename string) { + var text = []byte(result + "\n") + fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + fmt.Printf("Open %s error, %v\n", filename, err) + return + } + _, err = fl.Write(text) + fl.Close() + if err != nil { + fmt.Printf("Write %s error, %v\n", filename, err) + } +} diff --git a/concert/logger/logsync.go b/concert/logger/logsync.go new file mode 100755 index 0000000..f50b357 --- /dev/null +++ b/concert/logger/logsync.go @@ -0,0 +1,64 @@ +package logger + +import ( + "fmt" + "os" + "strings" + "sync" + "time" +) + +var Num int64 +var End int64 +var Results = make(chan *string) +var Start = true +var LogSucTime int64 +var LogErrTime int64 +var WaitTime int64 +var Silent bool +var LogWG sync.WaitGroup + +func init() { + go SaveLog() +} + +func LogSuccess(result string) { + LogWG.Add(1) + LogSucTime = time.Now().Unix() + Results <- &result +} + +func SaveLog() { + for result := range Results { + if Silent == false || strings.Contains(*result, "[+]") || strings.Contains(*result, "[*]") { + fmt.Println(*result) + } + if !NoSave { + WriteFile(*result, OutputFileName) + } + LogWG.Done() + } +} + +func WriteFile(result string, filename string) { + var text = []byte(result + "\n") + fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + fmt.Printf("Open %s error, %v\n", filename, err) + return + } + _, err = fl.Write(text) + fl.Close() + if err != nil { + fmt.Printf("Write %s error, %v\n", filename, err) + } +} + +func LogError(errinfo interface{}) { + if WaitTime == 0 { + fmt.Println(fmt.Sprintf(" %v/%v %v", End, Num, errinfo)) + } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime { + fmt.Println(fmt.Sprintf(" %v/%v %v", End, Num, errinfo)) + LogErrTime = time.Now().Unix() + } +} diff --git a/core/working.go b/core/working.go new file mode 100755 index 0000000..8a7ac1a --- /dev/null +++ b/core/working.go @@ -0,0 +1,88 @@ +package core + +import ( + dire "Sowhp/concert" + log "Sowhp/concert/logger" + "Sowhp/scripts" + "flag" + "fmt" + "os" +) + +// 当前版本信息 +var version = "1.0.0" + +// logo +var slogan = ` + + _____ _ + / ____| | | +| (___ _____ _| |__ _ __ + \___ \ / _ \ \ /\ / / '_ \| '_ \ + ____) | (_) \ V V /| | | | |_) | +|_____/ \___/ \_/\_/ |_| |_| .__/ + | | + |_| + + Sowhp version: ` + version + ` + +` + +var ( + txtfilepath string + resultmap map[string]map[string][]string + arraymap map[string][]string +) + +func Flag() { + flag.StringVar(&txtfilepath, "f", "", "URL文件路径地址,请参照格式输入, -f D://url.txt") + flag.IntVar(&log.LogLevel, "logl", 3, "设置日志输出等级,默认为3级,-logl 3") + print(slogan) + flag.Parse() +} + +func WorkIng() { + Flag() + if isCharacterEmpty(txtfilepath) { // 判断输入的URL路径是否为空,若为空,则停止运行。 + log.Debug(fmt.Sprintf("当前输入路径为:%s", txtfilepath)) + run(txtfilepath) + } else { + flag.Usage() + os.Exit(0) + } + +} + +func isCharacterEmpty(char string) bool { + return len(char) != 0 +} + +func run(path string) { + log.Common("正在运行 Sowhp 网站首页截图工具") + urls := scripts.FindTextUrl(path) // 获取TXT里url列表 + log.Debug(fmt.Sprint("以获取本地TXT文本里URL列表:", urls)) + resultname := fmt.Sprintf("result_%s", scripts.GetTimeStrin()) + log.Common("以获取本地URL列表信息数据,正在截图拍照中...") + arraymap = make(map[string][]string) // 初始化arraymap空间地址 + resultmap = make(map[string]map[string][]string) // 初始化resultmap空间地址 + // 格式参考样例 arraymap["Website URL Address"] = []string{"TItle Name", "status", "网站截图路径"} + dire.MkdirResport() // 创建默认结果目录 + dire.Dir_mk(fmt.Sprintf("./result/%s", resultname)) // 创建本次扫描结果目录文件夹 + dire.Dir_mk(fmt.Sprintf("./result/%s/%s", resultname, "data")) // 创建本次扫描图片存放目录 + for _, url := range urls { + urlresultlist := scripts.ChromeScreenshot(url, resultname) + + // 判断是否拍照成功,并根据拍照结果进行对应的结果填充 + if len(urlresultlist) == 0 { + log.Common(fmt.Sprintf("访问 %s 地址超时,无法进行首页截图拍照!", url)) + arraymap[url] = []string{"x_x!", "连接失败", "data/"} + } else { + log.Common(fmt.Sprintf("已完成对 %s 地址的首页截图拍照!", url)) + arraymap[urlresultlist[0]] = []string{urlresultlist[1], urlresultlist[2], urlresultlist[3]} + } + } + resultmap[resultname] = arraymap + log.Common(fmt.Sprintf("已完成所有URL地址的截图拍照,共拍摄 %d 条", len(arraymap))) + log.Common("正在生成最终结果报告文件...") + scripts.CreateHtml(resultmap) +} diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..b29697f --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module Sowhp + +go 1.20 + +require ( + github.com/chromedp/chromedp v0.9.1 + github.com/gookit/color v1.5.4 + github.com/klarkxy/gohtml v0.0.0-20220513090653-4beafb6047e9 +) + +require ( + github.com/chromedp/cdproto v0.0.0-20230620000757-8605e5981815 // indirect + github.com/chromedp/sysutil v1.0.0 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + golang.org/x/sys v0.10.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..a0c4137 --- /dev/null +++ b/go.sum @@ -0,0 +1,35 @@ +github.com/chromedp/cdproto v0.0.0-20230220211738-2b1ec77315c9/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/cdproto v0.0.0-20230620000757-8605e5981815 h1:o4k6aMs2G/cRe+DSAo3ui+Z8OKVQUcAOEM36BXSaX3M= +github.com/chromedp/cdproto v0.0.0-20230620000757-8605e5981815/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.1 h1:CC7cC5p1BeLiiS2gfNNPwp3OaUxtRMBjfiw3E3k6dFA= +github.com/chromedp/chromedp v0.9.1/go.mod h1:DUgZWRvYoEfgi66CgZ/9Yv+psgi+Sksy5DTScENWjaQ= +github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/klarkxy/gohtml v0.0.0-20220513090653-4beafb6047e9 h1:MMZHhkMQ+ODwwcH1cAB54YEnGp5A+Kfsx5SVO1r7UCo= +github.com/klarkxy/gohtml v0.0.0-20220513090653-4beafb6047e9/go.mod h1:Azf8dj+M8gsc0MkXDXzZp0a4ovsnJkNCKKTdko5SWUY= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go new file mode 100755 index 0000000..7dc353b --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "Sowhp/core" + +func main() { + core.WorkIng() +} diff --git a/scripts/Reporting.go b/scripts/Reporting.go new file mode 100755 index 0000000..9dfc85f --- /dev/null +++ b/scripts/Reporting.go @@ -0,0 +1,75 @@ +package scripts + +import ( + log "Sowhp/concert/logger" + "fmt" + "github.com/klarkxy/gohtml" + "os" +) + +var resultname string + +func CreateHtml(m map[string]map[string][]string) { + + for result_name := range m { + resultname = result_name // 获取html结果标题头 + } + + bootstrap := gohtml.NewHtml() + bootstrap.Html() + bootstrap.Head().Title().Text(resultname) + bootstrap.Meta().Charset("utf-8") + bootstrap1 := bootstrap.Body().Tag("div").Align("center").Id("content").Tag("table").Align("center").Border("1").Tag("tbody") + bootstrap1.Tr().Align("center").Td().Colspan("7").Text("url探测结果") + tr := bootstrap1.Tr().Align("center").Bgcolor("#0080FF").Attr("style", "color:white") + tr.Td().Text("URL详情") + tr.Td().Text("截图") + + filetxt, err := os.OpenFile(fmt.Sprintf("./result/%s/%s.txt", resultname, resultname), os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.FileMode(0666)) + if err != nil { + // log.Fatal(err) + log.DebugError(err) + + } + _, err = filetxt.WriteString(fmt.Sprintf("%s %s %s %s \r\n", "Website URL Address", "TItle Name", "status", "Website screenshot path")) + if err != nil { + // log.Fatal(err) + log.DebugError(err) + } + defer filetxt.Close() + + for urlippath := range m[resultname] { + titlename := m[resultname][urlippath][0] + statevalue := m[resultname][urlippath][1] + photopath := m[resultname][urlippath][2] + tr1 := bootstrap1.Tr().Align("center") + td := tr1.Td() + td.A().Href(urlippath).Target("_blank").Text(urlippath) + td.Text(fmt.Sprintf("%s %s", statevalue, titlename)) + tr1.Td().Img().Src(photopath).Attr("style", "width:800px;hight:200px") + _, err = filetxt.WriteString(fmt.Sprintf("%s %s %s %s", urlippath, titlename, statevalue, photopath)) + if err != nil { + // log.Fatal(err) + log.DebugError(err) + } + filetxt.WriteString("\r\n") + } + + resultHtml := fmt.Sprintf("./result/%s/%s.html", resultname, resultname) + // 生成最终结果HTML展示情况 + filehtml, err := os.OpenFile(resultHtml, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.FileMode(0666)) + if err != nil { + // log.Fatal(err) + log.DebugError(err) + } + // 关闭文件 + defer filehtml.Close() + + _, err = filehtml.WriteString(bootstrap.String()) + if err != nil { + // log.Fatal(err) + log.DebugError(err) + } + filehtml.WriteString("\r\n") + log.Common(fmt.Sprintf("以生成最终结果报告,报告存放路径为:%s", resultHtml)) +} diff --git a/scripts/conditionals.go b/scripts/conditionals.go new file mode 100755 index 0000000..b8b4287 --- /dev/null +++ b/scripts/conditionals.go @@ -0,0 +1,31 @@ +package scripts + +import "regexp" + +// IsIPAddress 判断字符串是否是IP地址的格式 +func IsIPAddress(str string) bool { + ipPattern := `^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$` + match, _ := regexp.MatchString(ipPattern, str) + return match +} + +// IsIPAddressWithPort 判断字符串是否是IP地址加端口号的格式 +func IsIPAddressWithPort(str string) bool { + ipPortPattern := `^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$` + match, _ := regexp.MatchString(ipPortPattern, str) + return match +} + +// IsDomainName 判断字符串是否是域名 +func IsDomainName(str string) bool { + domainPattern := `^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$` + match, _ := regexp.MatchString(domainPattern, str) + return match +} + +// IsDomainNameWithPort 判断字符串是否是域名加端口形式 +func IsDomainNameWithPort(str string) bool { + domainPortPattern := `^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}:\d+$` + match, _ := regexp.MatchString(domainPortPattern, str) + return match +} diff --git a/scripts/ipurlrelated.go b/scripts/ipurlrelated.go new file mode 100755 index 0000000..f33c84c --- /dev/null +++ b/scripts/ipurlrelated.go @@ -0,0 +1,189 @@ +package scripts + +import ( + log "Sowhp/concert/logger" + "bufio" + "context" + "fmt" + "github.com/chromedp/chromedp" + "github.com/chromedp/chromedp/device" + "net/http" + "os" + "regexp" + "strconv" + "strings" + "time" +) + +// extractDomainAndIP 提取URL中的顶级域名 +func extractDomainAndIP(url string) (domain string) { + // 匹配域名 + domainPattern := `^(?:https?://)?([\w.-]+)` + domainRegex := regexp.MustCompile(domainPattern) + domainMatch := domainRegex.FindStringSubmatch(url) + if len(domainMatch) > 1 { + domain = domainMatch[1] + } + return domain +} + +// FindTextUrl 获取本地text中url地址列表 +func FindTextUrl(filepath string) []string { + filePath := filepath // 替换为实际的txt文件路径 + + // 打开文件 + file, err := os.Open(filePath) + if err != nil { + // fmt.Println("无法打开文件:", err) + log.DebugError(err) + return []string{} + } + defer file.Close() + + urls := []string{} // 用于存储URL地址的列表 + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + // 提取URL地址 + urllist := extractURL(line) + for _, url := range urllist { + if url != "" { + urls = append(urls, url) + } + } + } + + if err := scanner.Err(); err != nil { + // fmt.Println("读取文件错误:", err) + log.DebugError(err) + return []string{} + } + + return urls +} + +// extractURL 提取IP端口地址,并生成url地址 +func extractURL(line string) []string { + + var urlTmpList []string + var http string = "http://" + var https string = "https://" + + // 判断当前输入的ip地址或者域名地址是否包含 http 或者 https + if strings.Contains(line, "http://") || strings.Contains(line, "https://") { + urllist := append(urlTmpList, line) + return urllist + } else if IsIPAddress(line) || IsDomainName(line) { + line1 := http + line + line2 := https + line + urllist := append(urlTmpList, line1, line2) + return urllist + } else if IsIPAddressWithPort(line) || IsDomainNameWithPort(line) { + line1 := http + line + line2 := https + line + urllist := append(urlTmpList, line1, line2) + return urllist + } + + return []string{} +} + +// visitURL 访问URL地址,在Tsaks中按顺利执行 +func visitURL(url string) chromedp.Tasks { + return chromedp.Tasks{ + chromedp.Navigate(url), + } +} + +// GetTimeStrin 获取当前时间字符串 +func GetTimeStrin() string { + currentTime := time.Now() + timeString := currentTime.Format("20060102150405") + return timeString +} + +// GetUrlStatusCode 获取网站状态码 +func GetUrlStatusCode(url string) string { + + // 创建一个自定义的 http.Client + client := &http.Client{ + Timeout: 5 * time.Second, // 设置超时时间为 5 秒 + } + + resp, err := client.Head(url) + if err != nil { + log.DebugError(err) + log.Debug(fmt.Sprintf("获取 %s 状态码超时,返回一个空的字符串", url)) + return " " + } + defer resp.Body.Close() + statuscode := strconv.Itoa(resp.StatusCode) + log.Debug(fmt.Sprintf("当前网站URL地址为:%s ,网站状态码为:%s", url, statuscode)) + return statuscode +} + +// ChromeScreenshot 访问url地址并对地址进行截图 +func ChromeScreenshot(URL string, resultname string) []string { + + // 创建一个上下文,并设置超时时间为 10 秒 + ct, cancel := context.WithTimeout(context.Background(), 13*time.Second) + defer cancel() + + // 创建一个新的上下文 + ctx, cancel := chromedp.NewContext(ct) + defer cancel() + + // 创建一个选项数组,配置浏览器行为 + opts := append(chromedp.DefaultExecAllocatorOptions[:], + chromedp.Flag("ignore-certificate-errors", true), // 忽略证书错误 + ) + // 创建一个自定义的执行器 + allocCtx, cancel := chromedp.NewExecAllocator(ctx, opts...) + defer cancel() + // 使用自定义执行器创建新的上下文 + ctx, cancel = chromedp.NewContext(allocCtx) + defer cancel() + + // 定义一个变量来保存状态码和标题 + var infoarray []string + + // var statusCode int + var pageTitle string + + // run + var b2 []byte + if err := chromedp.Run(ctx, + // reset + chromedp.Emulate(device.Reset), + + // 设置浏览器界面大小,并捕获/截取当前浏览器视口的屏幕截图 + chromedp.EmulateViewport(1920, 1080), + //chromedp.Navigate(URL), + visitURL(URL), + chromedp.CaptureScreenshot(&b2), + chromedp.WaitVisible(`body`, chromedp.ByQuery), + chromedp.Evaluate(`document.title`, &pageTitle), + ); err != nil { + log.DebugError(err) + log.Debug(fmt.Sprintf("访问 %s 超时,返回一个空的 []string{} 字典集", URL)) + // 在这里处理无法访问URL的情况,例如记录错误、重试等 + cancel() // 强制关闭 chromedp 上下文 + return []string{} + } + + urlname := extractDomainAndIP(URL) + photoname := fmt.Sprintf("data/%s-%s.png", urlname, GetTimeStrin()) + log.Debug(fmt.Sprintf("生成截图照片成功,截图名称为:%s", photoname)) + result := fmt.Sprintf("./result/%s/%s", resultname, photoname) + if err := os.WriteFile(result, b2, 0666); err != nil { + // log.Fatal(err) + log.DebugError(err) + } + log.Debug(fmt.Sprintf("本地存储截图照片成功,存储位置为:%s", result)) + + statuscode := GetUrlStatusCode(URL) + infoarray = append(infoarray, URL, pageTitle, statuscode, photoname) + cancel() // 强制关闭 chromedp 上下文 + return infoarray +} diff --git a/url.txt b/url.txt new file mode 100755 index 0000000..604b4c3 --- /dev/null +++ b/url.txt @@ -0,0 +1,2 @@ +https://github.com +https://sh1yan.top