双向爬取:

  • 横向:以页为单位。
  • 纵向:以一个页面内的条目为单位

横向

横向的规律是itype在变化

https://v.qq.com/channel/cartoon?channel=cartoon&iarea=1&itype=2&listpage=1&sort=18
https://v.qq.com/channel/cartoon?channel=cartoon&iarea=1&itype=5&listpage=1&sort=18

纵向

用正则表达式对下面进行分割

更新到第几集: `<div class="figure_caption" >全90集</div>`
动漫名称:target="_blank" title="天行九歌"
动漫介绍:<div class="figure_desc" title="超高颜值的权谋史诗">
 `<div class="figure_caption" >(?s:(.*?))</div>`
 `target="_blank" title="(?s:(.*?))"`
 `<div class="figure_desc" title="(?s:(.*?))">`
 

实现流程

  1. 获取用户输入起始和终止、启动working函数循环调用SpiderPage(url)爬取每一个页面
  2. SpiderPage中,获取动漫信息 横向爬取url信息,封装HttpGet函数,爬取每一个页面所有数据 存入 result 返回
  3. 找寻、探索豆瓣网页 纵向爬取规律。找出 ”更新到第几集“ ”动漫名称“ ”动漫介绍“
  4. 分别对这三个数据使用go正则函数
  5. 将提取的数据,按自定义格式写入文件。使用 网页编号命名文件
  6. 实现并发。
    1) go SpiderPage(url)
    2) 创建channel防止主go程退出
    3) SpiderPage函数末尾,写入channel
    4) 主 go 程 for 读取 channel

代码

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "regexp"
    "strconv"
)
//爬取指定url的页面 返回result
func HttpGet(url string)(result string,err error){
    resp,err1 :=http.Get(url)
    if err1 !=nil{
        err = err1    //将封装函数内部的错误,传出给调用者
        return
    }
    defer resp.Body.Close()

    //循环读取网页数据传出给调用者
    buf := make([]byte,4096)
    for {
        n,err2 :=resp.Body.Read(buf)
        if n==0{
            fmt.Println("读取完成")
            break
        }
        if err2!=nil && err2 != io.EOF{
            err = err2
            return
        }
        //累加每一次循环读到的buf数据,存入result一次性返回
        result +=string(buf[:n])
    }
    return
}

func Save2File(idx int,sum,name,introduce [][]string){
    Path :="D:/Go code/src/Gopa/07豆瓣纵向多线程/"+"第"+strconv.Itoa(idx)+"页.txt"
    f,err :=os.Create(Path)
    if err !=nil{
        fmt.Println("os.Create err:",err)
        return
    }
    defer f.Close()

    n := len(sum)    //得到条目数
    //打印一个开头
    f.WriteString("更新几集"+"\t\t\t"+"电影名称"+"\t\t\t"+"电影介绍"+"\n")
    for i :=0;i<n;i++{
        f.WriteString(sum[i][1]+"\t\t\t"+name[i+1][1]+"\t\t\t"+introduce[i][1]+"\n")
    }

}

//爬取一个页面信息
func SpiderPage(idx int, page chan int){
    //获取url地址
    url :="https://v.qq.com/channel/cartoon?channel=cartoon&iarea=1&itype="+strconv.Itoa((idx-1)*1)+"&listpage=1&sort=18"
    fmt.Println(url)
    //封装HttpGet爬取页面
    result,err :=HttpGet(url)
    if err!=nil{
        fmt.Println("HttpGet2 err:",err)
        return
    }
    //fmt.Println("result=",result)
    //解析、编译正则表达式--动漫集数
    ret1 :=regexp.MustCompile(`<div class="figure_caption" >(?s:(.*?))</div>`)
    //提取需要的信息
    sum :=ret1.FindAllStringSubmatch(result,-1)

    //解析、编译正则表达式--动漫名称
    ret2 :=regexp.MustCompile(`target="_blank" title="(?s:(.*?))"`)
    //提取需要的信息
    name :=ret2.FindAllStringSubmatch(result,-1)

    //解析、编译正则表达式--动漫介绍
    ret3 :=regexp.MustCompile(`<div class="figure_desc" title="(?s:(.*?))">`)
    //提取需要的信息
    introduce :=ret3.FindAllStringSubmatch(result,-1)

    //读取有用信息传递到参数
    Save2File(idx,sum,name,introduce)

    //与主go程配合
    page <- idx
}

func working(start,end int){
    fmt.Printf("正在爬取 %d 到 %d \n",start,end)

    page := make(chan int)    //防止主go程提前结束

    for i:=start;i<=end;i++{
        go SpiderPage(i,page)
    }

    for i:=start;i<=end;i++{
        fmt.Printf("第 %d 页爬取完毕\n",<-page)
    }

}

func main() {
    //指定爬取起始、终止页
    var start,end int
    fmt.Println("请输入爬取的起始页(>=1):")
    fmt.Scan(&start)
    fmt.Println("请输入爬取的终止页(>=start):")
    fmt.Scan(&end)

    working(start,end)
}