Golang: 分布式爬虫项目

基于Golang搭建一个抓取某相亲网站内容的爬虫。

源码地址:https://github.com/chao2015/go-crawler

源码分析:

1. 获取网页信息

Fetcher模块,通过一个url来获取该网页的全部内容,返回[]byte格式的文本信息。

// 抓取网页信息并转为urf-8编码
func Fetch(url string) ([]byte, error) {
    //resp, err := http.Get(url)

    // 直接用http.Get(url)进行获取信息会报错:Error: status code 403
    client := &http.Client{}
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        log.Fatalln(err)
    }
    // 查看自己浏览器中的User-Agent信息(检查元素->Network->User-Agent)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36")

    resp, err := client.Do(req)
    if err != nil {
        log.Fatalln(err)
    }

    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        fmt.Println("Error: status code", resp.StatusCode)
        return nil, fmt.Errorf("wrong status code: %d", resp.StatusCode)
    }

    //all, err := ioutil.ReadAll(resp.Body)  // gbk中文乱码
    // 网页编码转为utf-8的方法:
    // 法1. gbk解码为utf-8
    //utf8Reader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
    //all, err := ioutil.ReadAll(utf8Reader)

    bodyReader := bufio.NewReader(resp.Body)
    // 法2. 自动识别网页html编码,并转换为utf-8
    e := determineEncoding(bodyReader)
    utf8Reader := transform.NewReader(bodyReader, e.NewDecoder())
    return ioutil.ReadAll(utf8Reader)
}

// 识别网页编码
func determineEncoding(r io.Reader) encoding.Encoding {
    // Peek 返回缓存的一个切片,该切片引用缓存中前 n 字节数据,
    // 该操作不会将数据读出,只是引用,引用的数据在下一次读取操作之前是有效的
    // 如果引用的数据长度小于 n,则返回一个错误信息;如果 n 大于缓存的总大小,则返回 ErrBufferFull
    // 通过 Peek 的返回值,可以修改缓存中的数据,但是不能修改底层 io.Reader 中的数据
    bytes, err := bufio.NewReader(r).Peek(1024)
    if err != nil {
        panic(err)
    }
    e, _, _ := charset.DetermineEncoding(bytes, "")
    return e
}

2. 爬虫的执行引擎

执行引擎Engine模块维护一个任务队列,整个项目的流程如下:

  1. Engine模块从Seed中获得request请求(url+调用的Parser)。
  2. 依次处理传来的request请求,将request.url传递给Fecher模块。
    待Fecher模块根据url返回网页的文本内容后,Engine模块将文本内容传递给解析器Parser模块。
  3. 通过Parser模块得到想要的结构化数据items,并且得到包含下一级的url和对应Parser的requests,返回给Engine模块。

这里写图片描述

3. 选取内容

三种方法:

  1. css选择器
  2. xpath
  3. 正则表达式

这里采用正则表达式来获得网页上的结构化数据,例如:

const text = `
My email is chao@gmail.com
My email is chao1@gmail.com@163.com
My email is chao2@gmail.com.cn
`

func main() {
    // . 匹配任意字符
    // .+ 匹配1个或多个字符或者字母
    // .* 匹配0个或多个字符或者字母
    //re := regexp.MustCompile(`[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z0-9.]+`)
    //match := re.FindString(text)
    //match := re.FindAllString(text, -1)
    //fmt.Println(match)

    re := regexp.MustCompile(`([a-zA-Z0-9]+)@([a-zA-Z0-9]+)(\.[a-zA-Z0-9.]+)`)
    match := re.FindAllStringSubmatch(text, -1)
    for _, m := range match {
        fmt.Println(m)
    }
}

Output:

[chao@gmail.com chao gmail .com]
[chao1@gmail.com chao1 gmail .com]
[chao2@gmail.com.cn chao2 gmail .com.cn]

本项目中的具体应用见解析器Parser模块。

4. 解析器模块

本项目Parser模块分三级解析器:

  1. 城市列表解析器(得到各个城市名称和城市首页url)
  2. 城市解析器(得到各个城市首页上的用户名称和用户详情页url)
  3. 用户解析器(得到用户详情页的各种结构化数据)

5. 单机版爬虫效果

$ go run main.go 
// 城市列表:
2018/08/15 07:25:48 Fetching http://www.zhenai.com/zhenghun
2018/08/15 07:25:50 Got item City 阿坝
2018/08/15 07:25:50 Got item City 阿克苏
2018/08/15 07:25:50 Got item City 阿拉善盟
2018/08/15 07:25:50 Got item City 阿勒泰
2018/08/15 07:25:50 Got item City 阿里
2018/08/15 07:25:50 Got item City 安徽
2018/08/15 07:25:50 Got item City 安康
2018/08/15 07:25:50 Got item City 安庆
... ...

// 城市首页用户名:
2018/08/15 07:25:50 Fetching http://www.zhenai.com/zhenghun/aba
2018/08/15 07:25:50 Got item User 小顺儿
2018/08/15 07:25:50 Got item User 风中的蒲公英
2018/08/15 07:25:50 Got item User 路漫漫
... ...

// 用户详情:
2018/08/15 07:25:55 Fetching http://album.zhenai.com/u/1995815593
2018/08/15 07:25:55 Got item {小顺儿 女 29 169 52 3001-5000元 未婚 大学本科 会计 四川阿坝 魔羯座 和家人同住 未购车}
2018/08/15 07:25:55 Fetching http://album.zhenai.com/u/1314495053
2018/08/15 07:25:55 Got item {风中的蒲公英 女 41 158 48 3001-5000元 离异 中专 公务员 四川阿坝 处女座 已购房 未购车}
2018/08/15 07:25:55 Fetching http://album.zhenai.com/u/1626200317
2018/08/15 07:25:55 Got item {路漫漫 女 32 158 0 3000元以下 离异 大专 中学教师 四川阿坝 狮子座 和家人同住 未购车}
... ...
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值