爬取豆瓣电影信息:
双向爬取:
横向:以页为单位。
纵向:以一个页面内的条目为单位。
横向:
https://movie.douban.com/top250?start=0&filter= 1
https://movie.douban.com/top250?start=25&filter= 2
https://movie.douban.com/top250?start=50&filter= 3
https://movie.douban.com/top250?start=75&filter= 4
纵向:
电影名称: <img width="100" alt="电影名称" ——> `<img width="100" alt="(.*?)"`
分数:<span class="rating_num" property="v:average">分数</span> ——> `<span class="rating_num" property="v:average">(.*?)</span>`
评分人数:<span> 评分人数 人评价</span> ——> `<span>(.*?)人评价</span>`
---- 实现流程:
1. 获取用户输入 起始、终止页、启动 toWork 函数 循环 调用 SpiderPageDB(url) 爬取每一个页面
2. SpiderPageDB 中, 获取 豆瓣电影 横向爬取url 信息。封装 HttpGet 函数,爬取一个页面所有数据 存入 result 返回
3. 找寻、探索豆瓣网页 纵向爬取规律。找出“电影名”、“分数”、“评分人数”网页数据特征。
4. 分别 对这三部分数据使用 go 正则函数: 1) 解析、编译正则表达式 2) 提取信息 ——> string[1]: 代表没有 匹配参考项内容。
5. 将提取到的数据,按自定义格式写入文件。使用 网页编号命名文件。
6. 实现并发。
1) go SpiderPageDB(url) 。
2) 创建 channel 防止主 go 程退出
3) SpiderPageDB 函数末尾,写入 channel
4) 主 go 程 for 读取 channel 。
package main
import (
"fmt"
"strconv"
"net/http"
"io"
"regexp"
"os"
)
// 爬取指定url 的页面,返回 result
func HttpGetDB(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 {
break
}
if err2 != nil && err2 != io.EOF {
err = err2
return
}
result += string(buf[:n])
}
return
}
func Save2file(idx int, filmName, filmScore, peopleNum [][]string) {
path := "C:/itcast/"+ "第 " + strconv.Itoa(idx) + " 页.txt"
f, err := os.Create(path)
if err != nil {
fmt.Println("os.Create err:", err)
return
}
defer f.Close()
n := len(filmName) // 得到 条目数。 应该是 25
// 先打印 抬头 电影名称 评分 评分人数
f.WriteString("电影名称" + "\t\t\t" + "评分" + "\t\t" + "评分人数" + "\n")
for i:=0; i<n; i++ {
f.WriteString(filmName[i][1] + "\t\t\t" + filmScore[i][1] + "\t\t" + peopleNum[i][1] + "\n")
}
}
// 爬取一个豆瓣页面数据信息
func SpiderPageDB(idx int, page chan int) {
// 获取 url
url := "https://movie.douban.com/top250?start="+ strconv.Itoa((idx-1)*25) + "&filter="
// 封装 HttpGet2 爬取 url 对应页面
result, err := HttpGetDB(url)
if err != nil {
fmt.Println("HttpGet2 err:", err)
return
}
//fmt.Println("result=", result)
// 解析、编译正则表达式 —— 电影名称:
ret1:= regexp.MustCompile(`<img width="100" alt="(?s:(.*?))"`)
// 提取需要信息
filmName := ret1.FindAllStringSubmatch(result, -1)
// 解析、编译正则表达式 —— 分数:
pattern := `<span class="rating_num" property="v:average">(?s:(.*?))</span>`
ret2 := regexp.MustCompile(pattern)
// 提取需要信息
filmScore := ret2.FindAllStringSubmatch(result, -1)
// 解析、编译正则表达式 —— 评分人数:
ret3 := regexp.MustCompile(`<span>(?s:(\d*?))人评价</span>`)
//ret3 := regexp.MustCompile(`<span>(.*?)人评价</span>`)
// 提取需要信息
peopleNum := ret3.FindAllStringSubmatch(result, -1)
// 将提取的有用信息,封装到文件中。
Save2file(idx, filmName, filmScore, peopleNum)
// 与主go程配合 完成同步
page <- idx
}
func toWork(start, end int) {
fmt.Printf("正在爬取 %d 到 %d 页...\n", start, end)
page := make(chan int) //防止主go 程提前结束
for i:=start; i<=end; i++ {
go SpiderPageDB(i, page)
}
for i:=start; i<=end; i++ {
fmt.Printf("第 %d 页爬取完毕\n", <-page)
}
}
func main() {
// 指定爬取起始、终止页
var start, end int
fmt.Print("请输入爬取的起始页(>=1):")
fmt.Scan(&start)
fmt.Print("请输入爬取的终止页(>=start):")
fmt.Scan(&end)
toWork(start, end)
}