用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,来进一步分析城市的男女性个人信息。

原文发布于微信公众号 - 我的小碗汤(mysmallsoup)

原文发表时间:2018-06-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

如何将第三方控件嵌入ToolStrip控件,并提供Design-Time支持

最近研究了一下如何将第三方控件嵌入到ToolStrip控件中,并能提供Design-Time下的支持. 下面将详细讲解如何把系统的MonthCalendar控件...

2138
来自专栏BestSDK

“Excel格式”最风骚玩法,炫技加薪就靠它了

话不多说直接上干货! 001 自定义格式概述 01 调出单元格格式对话框 选中需要设置格式的单元格,按「CTRL+1」快捷键打开「设置单元格格式」对话框。 在对...

3693
来自专栏python学习路

五、XML与xpath--------------爬取美女图片 先用一个小实例开头吧(爬取贴吧每个帖子的图片)XML 和 HTML 的区别XML文档示例

除了正则表达式处理HTML文档,我们还可以用XPath,先将 HTML文件 转换成 XML文档,然后用 XPath 查找 HTML 节点或元素。 ----  先...

3754
来自专栏腾讯社交用户体验设计

面向未来的 CSS Variable

892
来自专栏我和未来有约会

用后台代码创建Storyboard

string storyboardName = "MyStoryBoard"; string myXamlElement = "MyXamlElement"...

2129
来自专栏葡萄城控件技术团队

Mobile First! Wijmo 5 之 架构

本文就开发者关心的话题之一架构,展开叙述。 ? Wijmo 5是一组JavaScript控件,但是不要与Widgets混淆。在此前开发Wijmo的时候,我们能够...

24210
来自专栏影子

jQuery中的常用内容总结(一)

3399
来自专栏Coco的专栏

引人瞩目的 CSS 变量(CSS Variable)

1723
来自专栏前端说吧

JS-DOM 综合练习-动态添加删除班级成绩表

3838
来自专栏向治洪

java的双缓冲技术

Java的强大特性让其在游戏编程和多媒体动画处理方面也毫不逊色。在Java游戏编程和动画编程中最常见的就是对于屏幕闪烁的处理。本文从J2SE的一个再现了屏幕闪...

3928

扫码关注云+社区

领取腾讯云代金券