昨天我们一起爬取珍爱网首页,拿到了城市列表页面,接下来在返回体城市列表中提取城市和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,来进一步分析城市的男女性个人信息。



本公众号免费提供csdn下载服务,海量IT学习资源,如果你准备入IT坑,励志成为优秀的程序猿,那么这些资源很适合你,包括但不限于java、go、python、springcloud、elk、嵌入式 、大数据、面试资料、前端 等资源。同时我们组建了一个技术交流群,里面有很多大佬,会不定时分享技术文章,如果你想来一起学习提高,可以公众号后台回复【2】,免费邀请加技术交流群互相学习提高,会不定期分享编程IT相关资源。


扫码关注,精彩内容第一时间推给你

最新文章

  1. 《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作
  2. Silverlight动态生成控件实例
  3. 《CDN技术详解》 - CDN知多少?
  4. Redis Sentinel高可用架构
  5. IOS第十天(1:QQ好友列表 ,自定义的headview,代理 ,通知 ,black的使用)
  6. vue服务端渲染
  7. Atitit.jsou&#160;html转换纯文本&#160;java&#160;c#&#160;php
  8. Android主题换肤实现
  9. C# 把字符串类型日期转换为日期类型
  10. LINUX内核源代码情景分析
  11. 一篇哥们自己的写的IBM电话面试感想-@大国
  12. 集美大学网络1413第八次作业(团队四)-- 第一次项目冲刺(Alpha版本)成绩
  13. 全面了解Android热修复技术
  14. 安装Sublime Text 3插件的方法:
  15. 用JAVA写一个冒泡排序
  16. redis入门(05)redis的key命令
  17. LinuxMint18使用单独分区作为Home挂载点
  18. MPP-使用说明
  19. python框架面试题联系
  20. python 判断一个对象的变量类型

热门文章

  1. Python高效编程技巧实战 实战编程+面试典型问题 中高阶程序员过渡
  2. C++ 深入浅出工厂模式(初识篇)
  3. SpringBoot系列——Security + Layui实现一套权限管理后台模板
  4. 泛型接口、JAVA API、包装类
  5. 7、创建图及图的遍历(java实现)
  6. MOOC web前端开发笔记(一)
  7. 12 (OC)* AFNetworking
  8. JS基础-全方面掌握继承
  9. 【linux】【sonarqube】安装sonarqube7.9
  10. 【linux】linux固定ip