爬取豆瓣电影

未匹配的标注

爬取豆瓣电影

爬取网页简单工作流程:

URL (请求的url地址,明确爬什么)

送请求,获取响应数据 (将网站的所有内容全部爬取)

保存响应数据,提有用信息

处理数据(存储、使

双向爬取

首先我们来熟悉两个基础技术名词:横向爬取 和 纵向爬取。

横向爬取:

所谓横向爬取,是指在爬取的网站页面中,以“页”为单位,找寻该网站分页器规律。一页一页的爬取网站数据信息。大多数网站,采用分页管理模式。针对这类网站,首先要确立横向爬取方法。

纵向爬取:

纵向爬取,是指在一个页面内,按不同的“条目”为单位。找寻各条目之间的规律。一条一条的爬取一个网页中的数据信息。也就是同时爬取一个页面内不同类别数据。

爬取电影评分

类似的,我们再来试试豆瓣电影爬取。首先打“豆瓣”网站首页,然后单击菜单中“排行榜”。在页面右部偏下的位置有一个“豆瓣电影TOP250”,这里记录了全球影迷评出的世界级经典影片。当你剧荒的时候,参考下这个排行榜,选取任意一部来观赏下,都不会有浪费生命的感觉。

点击“全部”进入前250部电影排行榜页面。URL为:movie.douban.com/top250 同样,测试下翻页特性:

第二页:movie.douban.com/top250?start=25&a...=

第三页:movie.douban.com/top250?start=50&a...=

第四页:movie.douban.com/top250?start=75&a...=

得出横向爬取条件大致为:“下一页”= “前一页”+ 25。

寻找好的电影,主要参考依据为“分数”和“评分人数”两项。因此,这里我们将电影名、评分分数和该电影的参与评分的评分人数作为我们的爬取目标。

选择任意一个页面,探索纵向爬取条件。总结发现:


电影名:被包裹在 <img width="100" alt="电影名称" 之中

分数:被包裹在 <span class="rating_num" property="v:average">分数</span> 之中

评分人数:被包裹在 <span>评分人数 人评价</span> 之中

同样,可以使用正则表达式 (?s:(.*?))来依次获取 电影名、分数、评分人数三部分数据信息。而后封装文件,将这三部分信息按格式存入即可。

借助前面的经验,我们直接实现一个并发版的爬虫,爬取豆瓣电影评分信息。

示例代码:

package main
import (
   "fmt"
   "net/http"
   "os"
   "regexp"
   "strconv"
   "time"
)

//发起请求,获取网页内容
func HttpGet(url string) (result string, err error) {
   resp, err1 := http.Get(url)     //发送get请求
   if err1 != nil {
      err = err1
      return
   }
   defer resp.Body.Close()

   //读取网页内容
   buf := make([]byte, 4*1024)
   for {
      n, _ := resp.Body.Read(buf)
      if n == 0 {
         break
      }
      result += string(buf[:n])  //累加读取的内容
   }
   return
}

func SpiderPage(i int, page chan int) {
   // 明确爬取的url
   fmt.Println("正在抓取第" + strconv.Itoa(1) + "页......")
   url := "https://movie.douban.com/top250?start=" + strconv.Itoa((i-1)*25) + "&filter="

   time.Sleep(1 * time.Second)       // 为防止访问IP 被网站屏闭,降低爬取频率

   // 开始爬取页面内容,将结果保存至 result。
   result, err := HttpGet(url)
   if err != nil {
      fmt.Println("HttpGet err = ", err)
      return
   }

   // 解析、编译正则表达式。—— 评价人数
   ret1 := regexp.MustCompile(`<span>(?s:(\d*?))人评价</span>`)
   // 取关键信息 评价人数
   pepoleCount := ret1.FindAllStringSubmatch(result, -1)

   // 解析、编译正则表达式。—— 评分
   patternScore := `<span class="rating_num" property="v:average">(?s:(.*?))</span>`
   ret2 := regexp.MustCompile(patternScore)
   filmScore := ret2.FindAllStringSubmatch(result, -1)

   // 解析、编译正则表达式。—— 电影名称
   patternName := `<img width="100" alt="(?s:(.*?))"`
   ret3 := regexp.MustCompile(patternName)
   filmName := ret3.FindAllStringSubmatch(result, -1)

   // 把内容写入到文件
   Save2File(i, pepoleCount, filmScore, filmName)

   page <- i     //写完一个文件,写i 到channel
}

//把内容写入到文件
func Save2File(i int, pepoleCount, filmScore, filmName [][]string) {
   // 新建文件,每一页保存成一个文件
   f, err := os.Create("第 " + strconv.Itoa(i) + " 页.txt")
   if err != nil {
      fmt.Println("os.Create err = ", err)
      return
   }
   defer f.Close()

   // 写标题
   f.WriteString("电影名称" + "\t\t\t" +  "评分" + "\t\t" + "评价人数" + "\t" + "\n")

   // 写内容
   n := len(pepoleCount)
   for i := 0; i < n; i++ {
      f.WriteString(filmName[i][1] + "\t\t\t" + filmScore[i][1] + "\t\t" + pepoleCount[i][1] + "\t" + "\n")
   }
}

func DoWork(start, end int) {
   fmt.Printf("准备爬取第 %d 页到 %d 页的网址\n", start, end)
   page := make(chan int)
   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.Printf("请输入起始页( >= 1 ):")
   fmt.Scan(&start)
   fmt.Printf("请输入终止页( >= 起始页 ):")
   fmt.Scan(&end)

   DoWork(start, end) 
}

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~