golang实现查询一个host的所有IP,分别返回2个数组,IPv4和IPv6,golang实现从request请求返回的response中提取网页的title。
在Go语言中,可以使用标准库的net.LookupIP
函数查询主机的所有IP地址,然后通过检查IP类型将结果分为IPv4和IPv6两个数组。以下是实现代码:
package main
import (
"fmt"
"net"
)
func LookupHostIPs(host string) (ipv4Addrs []net.IP, ipv6Addrs []net.IP, err error) {
// 查询主机所有IP地址
ips, err := net.LookupIP(host)
if err != nil {
return nil, nil, err
}
// 分离IPv4和IPv6地址
for _, ip := range ips {
// 标准化IP并检查类型
if ip4 := ip.To4(); ip4 != nil {
ipv4Addrs = append(ipv4Addrs, ip4)
} else if ip6 := ip.To16(); ip6 != nil {
ipv6Addrs = append(ipv6Addrs, ip6)
}
}
return ipv4Addrs, ipv6Addrs, nil
}
func main() {
host := "example.com"
ipv4, ipv6, err := LookupHostIPs(host)
if err != nil {
fmt.Printf("查询失败: %v\n", err)
return
}
fmt.Printf("%s 的IPv4地址:\n", host)
for _, ip := range ipv4 {
fmt.Println(ip)
}
fmt.Printf("\n%s 的IPv6地址:\n", host)
for _, ip := range ipv6 {
fmt.Println(ip)
}
}
关键点说明:
-
IP类型判断:
- 使用
ip.To4()
检查IPv4地址(返回4字节标准化格式) - 使用
ip.To16()
检查IPv6地址(返回16字节标准化格式) - 注意:IPv4地址也能通过
To16()
检测,因此需要先判断IPv4
- 使用
-
结果处理:
- IPv4地址存储为4字节格式(通过
To4()
转换) - IPv6地址存储为16字节格式(通过
To16()
转换) - 无效IP会被自动过滤
- IPv4地址存储为4字节格式(通过
-
错误处理:
- 如果DNS查询失败(如无效主机名或网络错误),返回错误信息
- 如果主机没有对应类型的IP,返回空数组
示例输出:
example.com 的IPv4地址:
93.184.216.34
example.com 的IPv6地址:
2606:2800:220:1:248:1893:25c8:1946
此实现能正确处理以下情况:
- 同时包含IPv4/IPv6的双栈主机
- 只有单一协议类型的主机
- 自动过滤无效IP地址
- 返回标准化的IP字节格式
golang实现从request请求返回的response中提取网页的title
在Go中提取网页标题,可以通过发送HTTP请求获取HTML内容,然后解析<title>
标签来实现。以下是完整的实现代码:
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"strings"
"golang.org/x/net/html"
)
// ExtractTitle 从HTTP响应中提取网页标题
func ExtractTitle(resp *http.Response) (string, error) {
// 确保响应体最终关闭
defer resp.Body.Close()
// 检查内容类型是否为HTML
contentType := resp.Header.Get("Content-Type")
if !strings.Contains(contentType, "text/html") {
return "", fmt.Errorf("content type is not HTML: %s", contentType)
}
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response body: %w", err)
}
// 解析HTML
doc, err := html.Parse(bytes.NewReader(body))
if err != nil {
return "", fmt.Errorf("failed to parse HTML: %w", err)
}
// 查找<title>标签
var title string
var findTitle func(*html.Node)
findTitle = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil {
title = n.FirstChild.Data
return
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
findTitle(c)
}
}
findTitle(doc)
// 清理标题文本
title = strings.TrimSpace(title)
title = strings.ReplaceAll(title, "\n", " ")
title = strings.ReplaceAll(title, "\t", " ")
// 处理空标题情况
if title == "" {
return "", fmt.Errorf("no title found in HTML")
}
return title, nil
}
func main() {
// 示例URL
url := "https://example.com"
// 发送HTTP GET请求
resp, err := http.Get(url)
if err != nil {
fmt.Printf("请求失败: %v\n", err)
return
}
// 提取标题
title, err := ExtractTitle(resp)
if err != nil {
fmt.Printf("提取标题失败: %v\n", err)
return
}
fmt.Printf("网页标题: %q\n", title)
}
关键功能说明:
-
内容类型检查:
- 检查
Content-Type
头部确保响应是HTML - 避免尝试解析非HTML内容(如JSON/图片)
- 检查
-
HTML解析:
- 使用
golang.org/x/net/html
包解析HTML - 递归遍历DOM树查找
<title>
标签 - 正确处理嵌套标签和文本节点
- 使用
-
标题清理:
- 移除首尾空白
- 替换换行符和制表符为空格
- 压缩连续空格
-
错误处理:
- 处理网络请求错误
- 处理非HTML内容
- 处理缺失标题的情况
使用示例:
func main() {
// 从HTTP请求获取响应对象
resp, err := http.Get("https://www.google.com")
if err != nil {
// 处理错误...
}
// 提取标题
title, err := ExtractTitle(resp)
if err != nil {
// 处理错误...
}
fmt.Println("提取到的标题:", title)
}
处理特殊情况:
-
编码处理:
// 在读取响应体之前自动检测编码 reader, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type")) if err != nil { return "", fmt.Errorf("failed to detect encoding: %w", err) } body, err := io.ReadAll(reader)
-
截断长标题:
// 在返回前添加 const maxTitleLength = 200 if len(title) > maxTitleLength { title = title[:maxTitleLength] + "..." }
-
处理多个title标签:
// 修改查找函数收集所有标题 var titles []string findTitle := func(n *html.Node) { if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil { titles = append(titles, n.FirstChild.Data) } // ... }
注意事项:
- 需要安装HTML解析库:
go get golang.org/x/net/html
- 对于大型HTML文件,考虑使用流式解析
- 处理重定向时可能需要自定义HTTP客户端
- 考虑添加超时和取消上下文支持
此实现能正确处理:
- 各种大小写的
<TITLE>
标签 - 包含额外空格的标题文本
- 特殊字符编码的标题
- 嵌套在其他元素中的标题标签