用go语言爬取珍爱网 | 第二回
昨天我们一起爬取珍爱网首页,拿到了城市列表页面,接下来在返回体城市列表中提取城市和url,即下图中的a标签里的href的值和innerText值。
提取a标签,可以通过CSS选择器来选择,如下:
$(’#cityList>dd>a’);就可以获取到470个a标签:
这里只提供一个思路,go语言标准库里没有CSS解析库,通过第三方库可以实现。具体可以参考文章:
https://my.oschina.net/2xixi/blog/488811
http://liyangliang.me/posts/2016/03/zhihu-go-insight-parsing-html-with-goquery/
这两篇文章都是用goquery解析 HTML,用到了库:
https://github.com/PuerkitoBio/goquery
也可以用xpath去解析html,可以参考:
https://github.com/antchfx/xquery
xpath和goquery相比还是比较麻烦的,通过以下这张图可以看出来goquery要活跃的多:
我们这里不用xpath,也不用goquery提取,用更加通用的正则表达式来提取。
从上图可以看出,返回体中的a标签里都是这种形式,XXX表示城市拼音,XX表示城市中文,其他的都一样。
<a href="http://www.zhenai.com/zhenghun/XXX"
class="">XX</a>
所以可以写出以下的正则表达式来匹配:
compile := regexp.MustCompile(`<a href="http://www.zhenai.com/zhenghun/[0-9a-z] "[^>]*>[^<] </a>`)
正则表达式说明:
1、href的值都类似http://www.zhenai.com/zhenghun/XX
2、XX可以是数字和小写字母,所以[0-9a-z], 表示至少有一个
3、[^>]*表示匹配不是>的其他字符任意次
4、[^<] 表示匹配不是<的其他字符至少一次
然后利用分组获取url和城市,代码如下:
func printAllCityInfo(body []byte){
//href的值都类似http://www.zhenai.com/zhenghun/XX
//XX可以是数字和小写字母,所以[0-9a-z], 表示至少有一个
//[^>]*表示匹配不是>的其他字符任意次
//[^<] 表示匹配不是<的其他字符至少一次
compile := regexp.MustCompile(`<a href="(http://www.zhenai.com/zhenghun/[0-9a-z] )"[^>]*>([^<] )</a>`)
submatch := compile.FindAllSubmatch(body, -1)
for _, matches := range submatch {
//打印
fmt.Printf("City:%s URL:%s\n", matches[2], matches[1])
}
//可以看到匹配个数为470个
fmt.Printf("Matches count: %d\n", len(submatch))
}
那么提取URL和City的完整代码如下:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"golang.org/x/text/transform"
//"golang.org/x/text/encoding/simplifiedchinese"
"io"
"golang.org/x/text/encoding"
"bufio"
"golang.org/x/net/html/charset"
"regexp"
)
func main() {
//返送请求获取返回结果
resp, err := http.Get("http://www.zhenai.com/zhenghun")
if err != nil {
panic(fmt.Errorf("Error: http Get, err is %v\n", err))
}
//关闭response body
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Println("Error: statuscode is ", resp.StatusCode)
return
}
//utf8Reader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
utf8Reader := transform.NewReader(resp.Body, determinEncoding(resp.Body).NewDecoder())
body, err := ioutil.ReadAll(utf8Reader)
if err != nil {
fmt.Println("Error read body, error is ", err)
}
printAllCityInfo(body)
//打印返回值
//fmt.Println("body is ", string(body))
}
func determinEncoding(r io.Reader) encoding.Encoding {
//这里的r读取完得保证resp.Body还可读
body, err := bufio.NewReader(r).Peek(1024)
if err != nil {
fmt.Println("Error: peek 1024 byte of body err is ", err)
}
//这里简化,不取是否确认
e, _, _ := charset.DetermineEncoding(body, "")
return e
}
func printAllCityInfo(body []byte){
//href的值都类似http://www.zhenai.com/zhenghun/XX
//XX可以是数字和小写字母,所以[0-9a-z], 表示至少有一个
//[^>]*表示匹配不是>的其他字符任意次
//[^<] 表示匹配不是<的其他字符至少一次
compile := regexp.MustCompile(`<a href="(http://www.zhenai.com/zhenghun/[0-9a-z] )"[^>]*>([^<] )</a>`)
/*matches := compile.FindAll(body, -1)
//matches是二维数组[][]byte
for _, m := range matches {
fmt.Printf("%s\n", m)
}
*/
submatch := compile.FindAllSubmatch(body, -1)
//submatch是三维数组[][][]byte
/* for _, matches := range submatch {
//[][]byte
for _, m := range matches {
fmt.Printf("%s ", m)
}
fmt.Println()
}*/
for _, matches := range submatch {
//打印
fmt.Printf("City:%s URL:%s\n", matches[2], matches[1])
}
//可以看到匹配个数为470个
fmt.Printf("Matches count: %d\n", len(submatch))
//打印abc
//fmt.Printf("%s\n", []byte{97,98,99})
}
运行后,可以看到输出了URL和City:
今天我们完成了URL和城市的提取,明天我们将利用URL,来进一步分析城市的男女性个人信息。
作者简洁
作者:小碗汤,一位热爱、认真写作的小伙,目前维护原创公众号:『我的小碗汤』,专注于写golang、docker、kubernetes等知识等提升硬实力的文章,期待你的关注。 转载说明:务必注明来源(注明:来源于公众号:我的小碗汤, 作者:小碗汤)
相关文章
- 一个Go语言接口和多操作系统实现的简单例子
- [Go] 开发 go web 项目,踩到的一些“坑”
- [Go] 如何正确地 抛出 错误 和 异常(error/panic/recover)?
- Go 语言体系下的微服务框架选型:Dubbo-go
- Go语言的类型转换和类型断言
- 【Go语言】【6】GO语言的数组
- 【Go语言】【5】GO语言变量
- 【Go语言】【4】GO语言类型和为类型增加方法
- 【Go语言】【3】GO语言常量
- 【Go语言】【18】GO语言的select
- 【Go语言】【11】GO语言的包和函数
- 我的Go+语言初体验——【一、go+环境WIN10_100%成功安装(附-视频)】
- 【Go资料】go语言学习资料书籍
- Go语言精进自学系列 | 了解切片实现原理并高效使用
- Go语言自学系列 | golang结构体的初始化
- Go语言自学系列 | go语言中的流程控制
- Go语言自学系列 | go语言遍历map
- Go语言自学系列 | golang流程控制关键字goto
- Go语言自学系列 | go语言中的流程控制
- 387集Go语言核心编程培训视频教材整理 | 数组和切片(二)
- go语言坑之for range
- Go语言开发Prometheus Exporter示例
- Go语言中的条件变量
- go语言|数据结构:二叉树(3)拷贝、镜像和对称
- Go语言入门-掌握go语言的变量和数据类型
- Mac上搭建Go语言环境及第一个hello.go程序