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.mzph.cn/news/778606.shtml

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

相关文章

【JavaWeb】Day24.Web入门——HTTP协议(一)

HTTP协议——概述 1.介绍 HTTP&#xff1a;Hyper Text Transfer Protocol(超文本传输协议)&#xff0c;规定了浏览器与服务器之间数据传输的规则。 http是互联网上应用最为广泛的一种网络协议http协议要求&#xff1a;浏览器在向服务器发送请求数据时&#xff0c;或是服务器在…

AI写作的力量:让你的文章焕发生机

在当今数字化时代&#xff0c;内容创作变得越来越重要。无论是博客文章、广告文案还是社交媒体更新&#xff0c;优质的文本都能吸引读者的注意力并传达信息。然而&#xff0c;创作过程并不总是轻松的&#xff0c;尤其是当我们面临灵感枯竭或时间紧迫的挑战时。这就是AI写作的力…

网站为什么要选择使用安全加速SCDN?

安全加速SCDN&#xff08;安全内容交付网络&#xff09;是一种网络加速服务&#xff0c;旨在提高网站和应用程序的性能和安全性。它使用专门的技术和基础设施来加速内容传输并保护网站免受网络攻击。 安全加速SCDN可以通过内容缓存、快速传输和动态路由技术来加速网站和应用程…

安卓调试桥ADB

Logcat 命令行工具 | Android Studio | Android Developers 什么是ADB ADB 全称为 Android Debug Bridge &#xff0c;是 Android SDK &#xff08;安卓的开发工具&#xff09;中的一个工具&#xff0c;起到调试桥的作用&#xff0c;是一个 客户端 - 服务器端程序 。其中 …

【Linux】开始掌握进程控制吧

送给大家一句话&#xff1a; 我并不期待人生可以一直过得很顺利&#xff0c;但我希望碰到人生难关的时候&#xff0c;自己可以是它的对手。—— 加缪 开始学习进程控制 1 前言2 进程创建2.1 fork函数初识2.2 fork函数返回值2.3 写时拷贝2.4 fork常规用法2.5 fork调用失败的原因…

3月23日笔记

广播域与泛洪范围是相同的 广播&#xff1a;在同一个泛洪范围内&#xff0c;强迫交换机泛洪&#xff08;主动&#xff09; 泛洪&#xff08;被动&#xff09; ARP的工作原理&#xff1a;ARP先通过广播发送请求包&#xff0c;所有收到该广播包的设备都会将其中的源IP和源MAC相…

Oracle存数字精度问题number、binary_double、binary_float类型

--表1 score是number(10,5)类型 create table TEST1 (score number(10,5) ); --表2 score是binary_double类型 create table TEST2 (score binary_double ); --表3 score是binary_float类型 create table TEST3 (score binary_float );实验一&#xff1a;分别往三张表插入 小数…

OpenCV 形态学处理函数

四、形态学处理&#xff08;膨胀&#xff0c;腐蚀&#xff0c;开闭运算&#xff09;_getstructuringelement()函数作用-CSDN博客 数字图像处理(c opencv)&#xff1a;形态学图像处理-morphologyEx函数实现腐蚀膨胀、开闭运算、击中-击不中变换、形态学梯度、顶帽黑帽变换 - 知乎…

JAVA面试八股文之集合

JAVA集合相关 集合&#xff1f;说一说Java提供的常见集合&#xff1f;hashmap的key可以为null嘛&#xff1f;hashMap线程是否安全, 如果不安全, 如何解决&#xff1f;HashSet和TreeSet&#xff1f;ArrayList底层是如何实现的&#xff1f;ArrayList listnew ArrayList(10)中的li…

Day24:私信列表、私信详情、发送私信

测试用户&#xff1a;用户名aaa 密码aaa 查询当前用户的会话列表&#xff1b;每个会话只显示一条最新的私信&#xff1b;支持分页显示。 首先看下表结构&#xff1a; conversation_id: 用from_id和to_id拼接&#xff0c;小的放前面去&#xff08;因为两个人的对话应该在一个会…

Siemens S7-1500TCPU 运动机构系统功能简介

目录 引言&#xff1a; 1.0 术语定义 2.0 基本知识 2.1 运动系统工艺对象 2.2 坐标系与标架 3.0 运动机构系统类型 3.1 直角坐标型 3.2 轮腿型 3.3 平面关节型 3.4 关节型 3.5 并联型 3.6 圆柱坐标型 3.7 三轴型 4.0 运动系统的运动 4.1 运动类型 4.1.1 线性运动…

新手体验OceanBase社区版V4.2:离线部署单节点集群

本文源自OceanBase用户的分享 先简单总结如下&#xff1a; 1.本文适合初学者体验OceanBase社区版 v4.2.2 2.仅需准备一台配置为2C/8G的Linux虚拟机 3.通过离线方式安装&#xff0c;以便更直观地了解安装过程 一、Linux系统准备 在宿主机(即你的windows PC电脑)上安装vbox软…

【JavaEE初阶系列】——多线程案例四——线程池

目录 &#x1f6a9;什么是线程池 &#x1f388;从池子中取效率大于新创建线程效率(why) &#x1f6a9;标准库中的线程池 &#x1f388;为什么调用方法而不是直接创建对象 &#x1f388;工厂类里的方法 &#x1f4dd;newCachedThreadPool() &#x1f4dd;newFixedThread…

【微服务】Nacos(配置中心)

文章目录 1.AP和CP1.基本介绍2.说明 2.Nacos配置中心实例1.架构图2.在Nacos Server加入配置1.配置列表&#xff0c;加号2.加入配置3.点击发布&#xff0c;然后返回4.还可以编辑 3. 创建 Nacos 配置客户端模块获取配置中心信息1.创建子模块 e-commerce-nacos-config-client50002…

Matlab之求直角坐标系下两直线的交点坐标

目的&#xff1a;在直角坐标系下&#xff0c;求两个直线的交点坐标 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;直线A上的点坐标&#xff1b; AngleA&#xff1a;直线A的倾斜角&#xff0c;单位度&#xff1b; PointB&#xff1a;直线B上的点坐标&#xf…

LeetCode - 股票平滑下跌阶段的数目(分组循环)

2110. 股票平滑下跌阶段的数目 当数组中的数字满足这个prices[i] 1 prices[i - 1]条件之后&#xff0c;就是平滑下降的阶段&#xff0c;也就是将数组中连续的数字进行一个分组。每次计算一个分组即可。 class Solution { public:long long getDescentPeriods(vector<int&…

如何写好一篇文档?

&#x1f304; 前言 什么是好的文档&#xff1f;在我看来&#xff0c;不减分地表达清楚作者的意图&#xff0c;即是一个不错的文档&#xff0c; 从作者角度上讲&#xff0c;能够让读者快速、清晰理解作者要表达的内容。 从读者角度上讲&#xff0c;读者能够快速、清晰地了解到…

分布式部署LNMP+WordPress

需要四台虚拟机&#xff0c;实际上&#xff0c;我们只需要操作三台 一个数据库&#xff0c;一个nginx&#xff0c;一个php&#xff0c;还需要准备一个软件包wordpress-4.7.3-zh_C 首先配置nginx的服务环境 [rootnginx ~]# vi /usr/local/nginx/conf/nginx.conf 修改文件中的loc…

蓝桥杯23年第十四届省赛真题-三国游戏|贪心,sort函数排序

题目链接&#xff1a; 1.三国游戏 - 蓝桥云课 (lanqiao.cn) 蓝桥杯2023年第十四届省赛真题-三国游戏 - C语言网 (dotcpp.com) 虽然这道题不难&#xff0c;很容易想到&#xff0c;但是这个视频的思路理得很清楚&#xff1a; [蓝桥杯]真题讲解&#xff1a;三国游戏&#xff0…

2. Java基本语法

文章目录 2. Java基本语法2.1 关键字保留字2.1.1 关键字2.1.2 保留字2.1.3 标识符2.1.4 Java中的名称命名规范 2.2 变量2.2.1 分类2.2.2 整型变量2.2.3 浮点型2.2.4 字符型 char2.2.5 Unicode编码2.2.6 UTF-82.2.7 boolean类型 2.3 基本数据类型转换2.3.1 自动类型转换2.2.2 强…