Go语言爬虫实战(线程池)

Go语言爬虫实战

目标

  • 利用go语言爬取指定网站的图片。
  • 实现爬取网站任意页面所有所需的图片。
  • 实现使用go语言线程池开启多个线程爬取图片内容。
  • 最后实现创建多个文件夹存储图片。

爬取网站图片

步骤

  • 对指定URL发去GET请求,获取对应的响应。

    • resp, err := http.Get(url)
  • 通过返回的响应获取网站的Html文本内容

    • BodyData, err := io.ReadAll(resp.Body)
  • 通过观察Html文本中图片的地址,并写出对应的正则表达式,匹配所有符合的图片信息。

    • 细节:通过浏览器的开发者模式,可以更快找到图片的地址

    • reImg := `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
      
  • 保存正则表达式的匹配结果,并对其发起GET请求获取图片资源信息。

    • //创建正则表达式的对象
      compile, err := regexp.Compile(reImg)
      //根据网站得Html内容匹配符合条件的结果,-1的意思是匹配所有结果。正数则表示匹配对应数字的结果
      allResult := compile.FindAllString(string(BodyData), -1)
      //获取图片资源
      for i, resultUrl := range allResult {...}
      
  • 保存图片到指定的文件夹

    • //获取图片信息
      data, err := io.ReadAll(get.Body)
      //创建指定文件夹
      mkdirPath := "./img/" + "img_" + strconv.Itoa(num) + "/"
      os.MkdirAll(mkdirPath, os.ModePerm)
      //创建文件保存图片信息
      file, err := os.OpenFile(mkdirPath+"cutImg_"+name, os.O_CREATE|os.O_RDWR, os.ModePerm)
      //将图片信息写入文件
      _, err = file.Write(data)
      

实现爬取网站任意页面

思路

  • 可以通过对网站的观察我们可以发现网站各个页面之间微小的变化,然后将需要爬取的网页存储在一个切片当中,之后重复第一步即可。

  • 例如:https://desk.3gbizhi.com/deskFJ/该网站的网页信息,通过点击翻页可以发现一些规律

    • https://desk.3gbizhi.com/deskFJ/index_1.html 第一页
    • https://desk.3gbizhi.com/deskFJ/index_2.html 第二页
    • https://desk.3gbizhi.com/deskFJ/index_3.html 第三页
    • 所以这里我们只需改变后面的数字即可获取对应页数的网页信息,并开始爬取图片信息。
  • 代码

    var start int
    var end int
    fmt.Printf("请输入爬取的开始页数:")
    fmt.Scanf("%d\n", &start)
    fmt.Printf("请输入爬取的结束页数:")
    fmt.Scanf("%d\n", &end)
    reImg := `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
    for i := 0; i < end-start+1; i++ {...urls[i].Url = "https://desk.3gbizhi.com/deskFJ/index_" + strconv.Itoa(urls[i].Id) + ".html"...
    }
    

线程池开启多个线程

  • 可以查看往期文章
    • https://editor.csdn.net/md/?articleId=137082930

创建多个文件夹存储图片

  • 创建文件夹

    • os.MkdirAll(mkdirPath, os.ModePerm)
  • 在存储图片的时候,获取图片的后缀以及获取图片原名称来命名图片

    • //截取名字和后缀
      index := strings.LastIndex(resultUrl, "/")
      name := resultUrl[index+1:]

项目结构图片

在这里插入图片描述


项目代码

//pool.go
package workerimport ("log""math""sync"
)// Args 参数结构体
type Args struct {Url   stringReImg stringId    int
}// Task 定义任务函数类型
type Task func(num int, url string, reImg string) interface{}//type Task func() interface{}type Pool struct {worker  inttasks   *Queueevents  chan struct{}results chan interface{}wg      sync.WaitGroup
}// NewPool 创建pool
func NewPool(worker int) *Pool {return &Pool{worker:  worker,tasks:   NewQueue(-1),events:  make(chan struct{}, math.MaxInt),results: make(chan interface{}, worker*2),wg:      sync.WaitGroup{},}
}// AddTasks 任务添加
func (p *Pool) AddTasks(task Task) {err := p.tasks.Push(task)if err != nil {log.Println(err)return}p.events <- struct{}{}
}// Start 启动工作池
func (p *Pool) Start(urls []Args) chan interface{} {var index = -1var IndexLock sync.Mutexfor i := 0; i < p.worker; i++ {p.wg.Add(1)go func() {for range p.events {task, err := p.tasks.Pop()if err != nil {log.Println(err)continue}IndexLock.Lock()index++IndexLock.Unlock()if task, ok := task.(Task); ok {p.results <- task(urls[index].Id, urls[index].Url, urls[index].ReImg)}}p.wg.Done()}()}return p.results
}// Wait 关闭池子
func (p *Pool) Wait() {close(p.events)p.wg.Wait()close(p.results)
}//queue.go
package workerimport ("fmt""sync"
)type Queue struct {elements []interface{}lock     sync.Mutexlimit    int
}// NewQueue 创建队列
func NewQueue(limit int) *Queue {return &Queue{elements: make([]interface{}, 0, 1024),lock:     sync.Mutex{},limit:    limit,}
}// Push 入队
func (q *Queue) Push(task interface{}) error {if q.limit != -1 && len(q.elements) >= q.limit {return fmt.Errorf("队列已满,请等待")}q.lock.Lock()defer q.lock.Unlock()q.elements = append(q.elements, task)return nil
}// Pop 出队
func (q *Queue) Pop() (interface{}, error) {if len(q.elements) == 0 {return nil, fmt.Errorf("队列以空,等带任务入队")}task := q.elements[0]q.elements = q.elements[1:]return task, nil
}//main.go
package mainimport ("fmt""io""log""net/http""os""regexp""src/worker""strconv""strings""time"
)// 爬虫流程
// 1.对网站发送Get请求
// 2.读取网站信息
// 3.提取图片路径
// 4.下载图片,保存起来func main() {var start intvar end intfmt.Printf("请输入爬取的开始页数:")fmt.Scanf("%d\n", &start)fmt.Printf("请输入爬取的结束页数:")fmt.Scanf("%d\n", &end)pool := worker.NewPool(5)reImg := `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`urls := make([]worker.Args, end-start+1)for i := 0; i < end-start+1; i++ {urls[i].ReImg = reImgurls[i].Id = start + iurls[i].Url = "https://desk.3gbizhi.com/deskFJ/index_" + strconv.Itoa(urls[i].Id) + ".html"pool.AddTasks(GetUrlImage)}pool.Start(urls)pool.Wait()fmt.Printf("爬取结束!")
}func GetUrlImage(num int, url string, reImg string) interface{} {resp, err := http.Get(url)HandleError(err)//读取BodyData, err := io.ReadAll(resp.Body)HandleError(err)//解析数据compile, err := regexp.Compile(reImg)HandleError(err)allResult := compile.FindAllString(string(BodyData), -1)if allResult == nil {fmt.Printf("%d号线程找不到对应数据\n", num)return nil}fmt.Printf("%d号线程已经获取到%d个图片路径,准备下载\n", num, len(allResult))for i, resultUrl := range allResult {//截取名字index := strings.LastIndex(resultUrl, "/")name := resultUrl[index+1:]get, err := http.Get(resultUrl)HandleError(err)//下载图片now := time.Now()data, err := io.ReadAll(get.Body)HandleError(err)fmt.Println("图片耗时:", time.Now().Sub(now))mkdirPath := "./img/" + "img_" + strconv.Itoa(num) + "/"os.MkdirAll(mkdirPath, os.ModePerm)file, err := os.OpenFile(mkdirPath+"cutImg_"+name, os.O_CREATE|os.O_RDWR, os.ModePerm)HandleError(err)_, err = file.Write(data)HandleError(err)fmt.Printf("成功下载图片%d\n", i)}return nil
}// HandleError 错误
func HandleError(err error) {if err != nil {log.Println(err)return}
}

(mkdirPath, os.ModePerm)
file, err := os.OpenFile(mkdirPath+“cutImg_”+name, os.O_CREATE|os.O_RDWR, os.ModePerm)
HandleError(err)
_, err = file.Write(data)
HandleError(err)
fmt.Printf(“成功下载图片%d\n”, i)
}
return nil
}

// HandleError 错误
func HandleError(err error) {
if err != nil {
log.Println(err)
return
}
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/578220.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小迪学习笔记(内网安全)(常见概念和信息收集)

小迪学习笔记&#xff08;内网安全&#xff09;&#xff08;一&#xff09; 内网分布图内网基本概念工作组和域环境的优缺点内网常用命令域的分类单域父域和子域域数和域森林 Linux域渗透问题内网安全流程小迪演示环境信息收集mimikatzLazagne(all)凭据信息政集操作演示探针主机…

Modbus转Profinet网关快速解决PLC插槽数量不够用的烦恼

通过Modbus转Profinet&#xff08;XD-MDPN100&#xff09;网关的应用&#xff0c;不仅可以实现Modbus设备与Profinet网络的平滑对接&#xff0c;还能有效解决PLC插槽限制和Modbus指令轮询等问题&#xff0c;Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;在解决PLC插…

CSS(三)---【盒子模型、边框、外边距合并】

零.前言 本篇主要介绍CSS中最重要的一种概念模型&#xff1a;“盒子模型”。 关于CSS的更多内容&#xff0c;可以查看作者之前的文章&#xff1a; CSS(一)---【CSS简介、导入方式、八种选择器、优先级】-CSDN博客 CSS(二)---【常见属性、复合属性使用】-CSDN博客 一.盒子模…

鸿蒙OS开发实例:【消息传递】

介绍 在HarmonyOS中&#xff0c;参考官方指导&#xff0c;其实你会发现在‘指南’和‘API参考’两个文档中&#xff0c;对消息传递使用的技术不是一对一的关系&#xff0c;那么今天这篇文章带你全面了解HarmonyOS 中的消息传递 概况 参照官方指导&#xff0c;我总结了两部分…

链表合集(easy难度)

合并两个有序链表 双指针法 由于list1和list2都是递增的&#xff0c;可以想到用双指针法。假如当前list1这个指针指向的节点被收入完成&#xff0c;那就list1&#xff1b;如果是list2被收入&#xff0c;那就list2。 具体是list1和节点被收入还是list2的节点被收入&#xff…

Netty核心原理剖析与RPC实践21-25

Netty核心原理剖析与RPC实践21-25 21 技巧篇&#xff1a;延迟任务处理神器之时间轮 HahedWheelTimer Netty 中有很多场景依赖定时任务实现&#xff0c;比较典型的有客户端连接的超时控制、通信双方连接的心跳检测等场景。在学习 Netty Reactor 线程模型时&#xff0c;我们知道…

使用filezilla连接Ubuntu22.04虚拟机

获取电脑IP和虚拟机IP ① 在windows下ctrlR再输入cmd&#xff0c;打开指令窗口&#xff0c;输入 ipconfig 虚拟机连接电脑用的是NAT模式&#xff0c;故看VMnet8的IP地址 ② 查看虚拟机IP地址 终端输入 ifconfig 如果没安装&#xff0c;按提示安装net-tools sudo apt install …

HarmonyOS 应用开发之创建PageAbility

开发者需要重写app.js/app.ets中的生命周期回调函数&#xff0c;开发者通过DevEco Studio开发平台创建PageAbility时&#xff0c;DevEco Studio会在app.js/app.ets中默认生成onCreate()和onDestroy()方法&#xff0c;其他方法需要开发者自行实现。接口说明参见前述章节&#xf…

5.11 Vue配置Element UI框架

Vue配置Element UI框架 目录一、 概要二、 开发前准备1. 搭建Vue框架 三、 安装 Element UI1. 引入 Element UI 依赖2. 在 mian.js 中引入 Element UI 和相关样式&#xff1a;3. 按需引入(非必须, 可忽略)4. 简单构建一个主页面 目录 一、 概要 Element UI 是一个基于 Vue.js …

窥探未来:Web3如何颠覆传统互联网

随着科技的迅速发展&#xff0c;Web3正逐渐成为人们关注的焦点。与传统的Web2相比&#xff0c;Web3代表了一种全新的互联网模式&#xff0c;其潜力和影响力引发了人们对未来的期待和探索。本文将深入探讨Web3如何颠覆传统互联网的各个方面&#xff0c;并展望其可能带来的未来变…

卷积神经网络(CNN)基础知识整理

卷积神经网络&#xff08;CNN&#xff09;基础知识整理 0写在前面 这两天陆续看了一些关于卷积神经网络的视频和博文&#xff0c;把我觉得比较有用的知识和内容梳理一下&#xff0c;理顺逻辑&#xff0c;自己也可加深理解&#xff0c;写在这里&#xff0c;日后想看&#xff0…

3D人体姿态估计项目 | 从2D视频中通过检测人体关键点来估计3D人体姿态实现

项目应用场景 人体姿态估计是关于图像或视频中人体关节的 2D 或 3D 定位。一般来说&#xff0c;这个过程可以分为两个部分&#xff1a;(1) 2D 视频中的 2D 关键点检测&#xff1b;(2) 根据 2D 关键点进行 3D 位姿估计。这个项目使用 Detectron2 从任意的 2D 视频中检测 2D 关节…