golang 抓取网页并将其他编码(gbk,gb2312,big5等)中文文字转换成uft8编码的字符串处理函数
最近用golang采集网页中遇到了各种不能识别的的乱码字符串,他们大多编码是gbk、gb2312、big5、windows-1252 等编码。有时候,网页上并没有声明编码,却使用上面这种编码的网页也有,也有网页声明的编码和实际使用的编码不同的网页,导致网页编码转换工作带来诸多不便,更多的是根据提示的编码转换出来依然还是乱码的问题,着实让人头疼。于是乎,为了得到一个通用可行的中文字符串编码转换方法,本人通过网络上上百万个网站测试,采集数据回来进行编码转换,终于总结出来了一套绝大部分都能顺利将网页中文字符串编码都转换成utf-8编码的方法。
golang项目直接引用
安装依赖包
go get github.com/fesiong/goproject/convert
使用说明
对外公开有3个函数,Request函数支持请求网络页面,并自动检测页面内容的编码,转换成utf-8,ToUtf8函数支持传入的字符串会自动检测编码,并转换成utf-8,Convert函数需要传入原始编码和输出编码,如果原始编码传入出错,则转换出来的文本会乱码
请求网络页面,并自动检测页面内容的编码,转换成utf-8
link := "http://www.youth.cn/"
resp, err := Request(link)
if err != nil {
t.Error(err.Error())
}
传入的字符串会自动检测编码,并转换成utf-8
content := "中国青年网"
content = ToUtf8(content)
传入原始编码和输出编码
content := "中国青年网"
content = Convert(content, "utf-8", "utf-8")
源码地址
核心的编码转换判断函数
package convert
import (
"github.com/axgle/mahonia"
"github.com/parnurzeal/gorequest"
"golang.org/x/net/html/charset"
"net/http"
"regexp"
"strings"
"time"
)
type RequestData struct {
Header http.Header
Request *http.Request
Body string
Status string
StatusCode int
}
/**
* 请求网络页面,并自动检测页面内容的编码,转换成utf-8
*/
func Request(urlPath string) (*RequestData, error) {
resp, body, errs := gorequest.New().Timeout(90 * time.Second).Get(urlPath).End()
if len(errs) > 0 {
//如果是https,则尝试退回http请求
if strings.HasPrefix(urlPath, "https") {
urlPath = strings.Replace(urlPath, "https://", "http://", 1)
return Request(urlPath)
}
return nil, errs[0]
}
defer resp.Body.Close()
contentType := strings.ToLower(resp.Header.Get("Content-Type"))
body = toUtf8(body, contentType)
requestData := RequestData{
Header: resp.Header,
Request: resp.Request,
Body: body,
Status: resp.Status,
StatusCode: resp.StatusCode,
}
return &requestData, nil
}
/**
* 对外公开的编码转换接口,传入的字符串会自动检测编码,并转换成utf-8
*/
func ToUtf8(content string) string {
return toUtf8(content, "")
}
/**
* 内部编码判断和转换,会自动判断传入的字符串编码,并将它转换成utf-8
*/
func toUtf8(content string, contentType string) string {
var htmlEncode string
if strings.Contains(contentType, "gbk") || strings.Contains(contentType, "gb2312") || strings.Contains(contentType, "gb18030") || strings.Contains(contentType, "windows-1252") {
htmlEncode = "gb18030"
} else if strings.Contains(contentType, "big5") {
htmlEncode = "big5"
} else if strings.Contains(contentType, "utf-8") {
htmlEncode = "utf-8"
}
if htmlEncode == "" {
//先尝试读取charset
reg := regexp.MustCompile(`(?is)<meta[^>]*charsets*=["']?s*([A-Za-z0-9-]+)`)
match := reg.FindStringSubmatch(content)
if len(match) > 1 {
contentType = strings.ToLower(match[1])
if strings.Contains(contentType, "gbk") || strings.Contains(contentType, "gb2312") || strings.Contains(contentType, "gb18030") || strings.Contains(contentType, "windows-1252") {
htmlEncode = "gb18030"
} else if strings.Contains(contentType, "big5") {
htmlEncode = "big5"
} else if strings.Contains(contentType, "utf-8") {
htmlEncode = "utf-8"
}
}
if htmlEncode == "" {
reg = regexp.MustCompile(`(?is)<title[^>]*>(.*?)</title>`)
match = reg.FindStringSubmatch(content)
if len(match) > 1 {
aa := match[1]
_, contentType, _ = charset.DetermineEncoding([]byte(aa), "")
htmlEncode = strings.ToLower(htmlEncode)
if strings.Contains(contentType, "gbk") || strings.Contains(contentType, "gb2312") || strings.Contains(contentType, "gb18030") || strings.Contains(contentType, "windows-1252") {
htmlEncode = "gb18030"
} else if strings.Contains(contentType, "big5") {
htmlEncode = "big5"
} else if strings.Contains(contentType, "utf-8") {
htmlEncode = "utf-8"
}
}
}
}
if htmlEncode != "" && htmlEncode != "utf-8" {
content = Convert(content, htmlEncode, "utf-8")
}
return content
}
/**
* 编码转换
* 需要传入原始编码和输出编码,如果原始编码传入出错,则转换出来的文本会乱码
*/
func Convert(src string, srcCode string, tagCode string) string {
srcCoder := mahonia.NewDecoder(srcCode)
srcResult := srcCoder.ConvertString(src)
tagCoder := mahonia.NewDecoder(tagCode)
_, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
result := string(cdata)
return result
}
10 声望
1 粉丝
推荐阅读
iris.Django模板引擎的语法格式和语法标签使用方法,include标签传递变量方法
在网络上,关于Django模板的标签和语法教程少之又少,并且很多都是不全面的,要么就是抄来抄去的无用文章,要么就是简单寥寥几个标签,很多时候,都不能满足使用。比如include引入模板后,如何传递变量、如何在页...
开发小程序的之朴赞 1阅读 3.2k
Golang 中 []byte 与 string 转换
string 类型和 []byte 类型是我们编程时最常使用到的数据结构。本文将探讨两者之间的转换方式,通过分析它们之间的内在联系来拨开迷雾。
机器铃砍菜刀赞 22阅读 55.3k评论 1
年度最佳【golang】map详解
这篇文章主要讲 map 的赋值、删除、查询、扩容的具体执行过程,仍然是从底层的角度展开。结合源码,看完本文一定会彻底明白 map 底层原理。
去去1002赞 14阅读 11.1k评论 2
年度最佳【golang】GMP调度详解
Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底的. 这篇文章将通过分析...
去去1002赞 13阅读 11.3k评论 4
【已结束】SegmentFault 思否技术征文丨浅谈 Go 语言框架
亲爱的开发者们:我们的 11 月技术征文如期而来,这次主题围绕 「 Go 」 语言,欢迎大家来参与分享~征文时间11 月 4 日 - 11 月 27 日 23:5911 月 28 日 18:00 前发布中奖名单参与条件新老思否作者均可参加征文...
SegmentFault思否赞 11阅读 4.7k评论 11
【Go微服务】开发gRPC总共分三步
之前我也有写过RPC相关的文章:《 Go RPC入门指南:RPC的使用边界在哪里?如何实现跨语言调用?》,详细介绍了RPC是什么,使用边界在哪里?并且用Go和php举例,实现了跨语言调用。不了解RPC的同学建议先读这篇文...
王中阳Go赞 8阅读 3.7k评论 6
【golang】sync.WaitGroup详解
上一期中,我们介绍了 sync.Once 如何保障 exactly once 语义,本期文章我们介绍 package sync 下的另一个工具类:sync.WaitGroup。
去去1002赞 13阅读 30.2k评论 2
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。