go爬虫之爬取豆瓣电影
go爬取豆瓣电影#
好久没使用go语言做个项目了,上午闲来无事花了点时间使用golang来爬取豆瓣top电影,这里我没有用colly框架而是自己设计简单流程。mark一下
思路#
定义两个channel,一个channel存放web网页源内容,另一个存放提取后的有效内容。
多个goroutine并发爬取网页源内容放到存放web网页的channel里,再启动goroutine去存放web网页的channel里读取内容,读取到内容后启动goroutine去提取有效值存放到channel里,最后持久化写入本地文件(文件写操作并非线程安全所以这里我没有使用多goroutine)。
代码#
具体代码如下,如果想执行看效果需要go get github.com/PuerkitoBio/goquery
安装三方包或者直接点这里拉取代码设置GOPATH之后便可运行
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
"time"
"os"
"github.com/PuerkitoBio/goquery"
)
/*
通过多个goroutine并发执行爬取操作,channel存放要爬取url内容和爬取结果
这样只需要设计爬取函数和提取函数
*/
func get_web_content(url string, chan_web chan string) {
resp, err := http.Get(url)
if err != nil {
fmt.Println("http get error", err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("read error", err)
return
}
chan_web <- string(body)
}
func extract_valid_content(body string, chan_r chan []byte) {
dom, err := goquery.NewDocumentFromReader(strings.NewReader(body))
if err != nil {
fmt.Println(err)
}
dom.Find("ol.grid_view div.item").Each(func(i int, selection *goquery.Selection) {
// extract result
result := make(map[string]string)
name := selection.Find("div.info span.title").First().Text()
doctor_str := selection.Find("div.info div.bd p").First().Text()
r := regexp.MustCompile(`导演:(?s:(.*?))(主演|主|&|\.\.\.)`)
doctor := r.FindAllStringSubmatch(doctor_str, -1)[0][1]
rating_num := selection.Find("div.star span.rating_num").First().Text()
evaluation_str := selection.Find("div.star span").Last().Text()
r = regexp.MustCompile(`(?s:(.*?))人评价`)
evaluation := r.FindAllStringSubmatch(evaluation_str, -1)[0][1]
ranking := selection.Find("div.pic em").First().Text()
result["name"] = name
result["doctor"] = doctor
result["rating_num"] = rating_num
result["evaluation"] = evaluation
result["ranking"] = ranking
json_str, err := json.Marshal(result)
if err != nil {
fmt.Println(err)
return
}
chan_r <- json_str
})
}
func main() {
var (
OutputFile = "./film_crawl.txt"
)
base_url := "https://movie.douban.com/top250?start=%d&filter="
chan_web_content := make(chan string)
defer close(chan_web_content)
chan_r := make(chan []byte)
defer close(chan_r)
for i := 0; i < 10; i++ {
url := fmt.Sprintf(base_url, i*25)
go get_web_content(url, chan_web_content)
}
go func() {
for {
web_content, ok := <- chan_web_content
if !ok {
break
}
go extract_valid_content(web_content, chan_r)
}
}()
flag := false
to := time.NewTimer(time.Second * 5)
file, err := os.OpenFile(OutputFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Println("Failed to open the file", err.Error())
return
}
defer file.Close()
for {
if flag {
break
}
to.Reset(time.Second * 5)
select {
case res := <- chan_r:
fmt.Printf("%s\n", res)
file.Write(res)
file.WriteString("\n")
case <- to.C:
flag = true
break
}
}
fmt.Println("end")
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!