golang实现查询一个host的所有IP,分别返回2个数组,IPv4和IPv6,golang实现从request请求返回的response中提取网页的title

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)
	}
}

关键点说明:

  1. IP类型判断

    • 使用ip.To4()检查IPv4地址(返回4字节标准化格式)
    • 使用ip.To16()检查IPv6地址(返回16字节标准化格式)
    • 注意:IPv4地址也能通过To16()检测,因此需要先判断IPv4
  2. 结果处理

    • IPv4地址存储为4字节格式(通过To4()转换)
    • IPv6地址存储为16字节格式(通过To16()转换)
    • 无效IP会被自动过滤
  3. 错误处理

    • 如果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)
}

关键功能说明:

  1. 内容类型检查

    • 检查Content-Type头部确保响应是HTML
    • 避免尝试解析非HTML内容(如JSON/图片)
  2. HTML解析

    • 使用golang.org/x/net/html包解析HTML
    • 递归遍历DOM树查找<title>标签
    • 正确处理嵌套标签和文本节点
  3. 标题清理

    • 移除首尾空白
    • 替换换行符和制表符为空格
    • 压缩连续空格
  4. 错误处理

    • 处理网络请求错误
    • 处理非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)
}

处理特殊情况:

  1. 编码处理

    // 在读取响应体之前自动检测编码
    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)
    
  2. 截断长标题

    // 在返回前添加
    const maxTitleLength = 200
    if len(title) > maxTitleLength {
        title = title[:maxTitleLength] + "..."
    }
    
  3. 处理多个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)
        }
        // ...
    }
    

注意事项:

  1. 需要安装HTML解析库:go get golang.org/x/net/html
  2. 对于大型HTML文件,考虑使用流式解析
  3. 处理重定向时可能需要自定义HTTP客户端
  4. 考虑添加超时和取消上下文支持

此实现能正确处理:

  • 各种大小写的<TITLE>标签
  • 包含额外空格的标题文本
  • 特殊字符编码的标题
  • 嵌套在其他元素中的标题标签


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码讲故事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值