首页 > 其他分享 >Go语言---并发版网页段子筛选爬虫

Go语言---并发版网页段子筛选爬虫

时间:2024-07-26 19:55:47浏览次数:16  
标签:content string err url fmt 爬虫 --- 爬取 Go

爬虫四个主要步骤:

  • 明确目标(要知道你准备在哪个范围或者网站去搜索
  • 爬(将所有的网站的内容全部爬下来)
  • 取(去掉对我们没用处的数据)
  • 处理数据(按照我们想要的方式存储和使用)
在此之前,我们实现的简单版以及并发版的爬虫都没有对我们所需的信息进行过滤,这样得到的信息大多是我们不需要的,接下来我们就以捧腹网的笑话信息进行爬虫,并筛选出来我们需要的笑话信息。

捧腹网

在这里插入图片描述

总结页面信息

经过查看页面的源码:
在这里插入图片描述
我们可以总结出来以下的信息:

  • 网页规律:
    https://www.pengfu.net/xiaohuashouye/
    https://www.pengfu.net/xiaohuashouye/index_2.html
    https://www.pengfu.net/xiaohuashouye/index_3.html

  • 主页面规律:

<h3 class="blogtitle"><a href="       开头
"      结尾
<h3 class="blogtitle"><a href="      一个段子的ur1连接"      结尾
  • 一个系列段子的规律:
<h1 class="con_tilte">幽默笑话(100个幽默笑话简短)</h1>          //标题
<div class="con_text">       段子开头
<p class="share"> 段子结尾

代码实现

主线程

func main() {
	var start, end int
	fmt.Printf("请输入起始页(>=1):")
	fmt.Scan(&start)
	fmt.Printf("请输入终止页(>=起始页):")
	fmt.Scan(&end)

	DoWork(start, end)
}

  • 提示输入需要爬取的起始页和终止页
  • 开启爬虫函数DoWork(start, end)

DoWork(start, end)

func DoWork(start, end int) {
	fmt.Printf("准备爬取%d到%d的网址\n", start, end)

	page := make(chan int)
	//定义一个函数,爬主页面
	for i := start; i <= end; i++ {
		go SqiderPage(i, page)
	}

	for i := start; i <= end; i++ {
		fmt.Printf("第%d个页面爬取完成\n", <-page)
	}

}
  • 开启子线程对每一页进行爬取SqiderPage(i, page)这里使用了管道,用来阻塞等待爬取成功,否则会因为DoWork(start, end)的退出,导致子线程退出,爬取失败。

SqiderPage(i, page)

func SqiderPage(i int, page chan<- int) {
	//明确爬取的url
	var url string
	if i == 1 {
		url = "https://www.pengfu.net/xiaohuashouye/index.html"
	} else {
		url = "https://www.pengfu.net/xiaohuashouye/index_" + strconv.Itoa(i) + ".html"
	}
	fmt.Printf("准备爬取第%d个网址:%s\n", i, url)

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

	//fmt.Println("r=", result)
	//取内容 <h3 class="blogtitle"><a href="一个段子的ur1连接"结尾
	//解析表达式
	re := regexp.MustCompile(`<h3 class="blogtitle"><a href="(?s:(.*?))"`)
	if re == nil {
		fmt.Println("regexp.MustCompile.err=", err)
		return
	}
	//取关键信息从result中过滤,-1代表取所有内容
	joyUrls := re.FindAllStringSubmatch(result, -1)

	fileTitle := make([]string, 0)
	fileContent := make([]string, 0)

	//取网址
	for _, data := range joyUrls {
		//fmt.Println("Urls=", data[1])
		//开始爬取每一个系列笑话
		title, content, err := SpiderOneJoy(data[1])
		if err != nil {
			fmt.Println("SpiderOneJoy.err=", err)
			continue
		}
		//fmt.Printf("title=#%v#", title)
		//fmt.Printf("content=#%v#", content)
		fileTitle = append(fileTitle, title)
		fileContent = append(fileContent, content)
	}

	//把内容写入到文件
	StoreJoyToFile(i, fileTitle, fileContent)

	page <- i //写
}
  • 获取每一页的URL,根据规律可以写为以下URL:
if i == 1 {
		url = "https://www.pengfu.net/xiaohuashouye/index.html"
	} else {
		url = "https://www.pengfu.net/xiaohuashouye/index_" + strconv.Itoa(i) + ".html"
	}
  • 利用函数 HttpGet(url),获取页面信息,开始爬取页面内容
  • 利用正则表达式,获取每一系列笑话的URL
  • 开始爬取每一个系列笑话SpiderOneJoy(data[1])
  • 得到内容和标题后利用StoreJoyToFile(i, fileTitle, fileContent),写入到文件中

HttpGet(url)

func HttpGet(url string) (result string, err error) {
	resp, err1 := http.Get(url)
	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
}
  • 利用http.Get(url以及resp.Body.Read(buf)获取网页内容。

SpiderOneJoy(url string)

func SpiderOneJoy(url string) (title string, content string, err error) {
	result, err1 := HttpGet(url)
	if err1 != nil {
		err = err1
		return
	}

	//取关键信息<h1 class="con_tilte">幽默笑话(100个幽默笑话简短)</h1>
	re1 := regexp.MustCompile(`<h1 class="con_tilte">(?s:(.*?))</h1>`)
	if re1 == nil {
		err = fmt.Errorf("%s", "regexp.MustCompile.err")
		return
	}

	//取标题
	tmpTitle := re1.FindAllStringSubmatch(result, -1)
	for _, data := range tmpTitle {
		title = data[1]
		//	title = strings.Replace(title, "\n", "", -1)
		//	title = strings.Replace(content, " ", "", -1)
		break
	}

	//取内容 1表示只过滤第一个
	//<div class="con_text">        段子开头
	//</a>(<b id="diggnum"><script src= 段子结尾
	re2 := regexp.MustCompile(`<div class="con_text">(?s:(.*?))<p class="share">`)
	if re2 == nil {
		err = fmt.Errorf("%s", "regexp.MustCompile.err2")
		return
	}

	tmpContent := re2.FindAllStringSubmatch(result, -1)
	for _, data := range tmpContent {
		content = data[1]
		content = strings.Replace(content, "\n", "", -1)
		content = strings.Replace(content, "<p>", "", -1)
		content = strings.Replace(content, "</p>", "", -1)
		content = strings.Replace(content, " ", "", -1)
		break
	}

	return
}
  • 根据页面源码的题目以及内容特征,利用正则表达式获取题目以及内容;
  • 通过函数strings.Replace可以过滤到我们不需要的一些空格等信息;

StoreJoyToFile(i int, fileTitle []string, fileContent []string)

func StoreJoyToFile(i int, fileTitle []string, fileContent []string) {
	f, err := os.Create(strconv.Itoa(i) + ".txt")
	if err != nil {
		fmt.Println("os.Create.err=", err)
		return
	}
	defer f.Close()
	//写内容
	n := len(fileTitle)
	for i := 0; i < n; i++ {
		f.WriteString(fileTitle[i] + "\n")
		f.WriteString(fileContent[i] + "\n")
		f.WriteString("\n=============================================================\n")
	}
}
  • 以第几页为标题,创建一个文件,将内容写入。

结果展示

在这里插入图片描述
在这里插入图片描述

网页信息对比

在这里插入图片描述
在这里插入图片描述

标签:content,string,err,url,fmt,爬虫,---,爬取,Go
From: https://blog.csdn.net/m0_73537205/article/details/140161648

相关文章

  • DB2-Db2StreamingChangeEventSource
    提示:Db2StreamingChangeEventSource类主要用于从IBMDb2数据库中读取变更数据捕获(CDC,ChangeDataCapture)信息。CDC是一种技术,允许系统跟踪数据库表中数据的更改,这些更改可以是插入、更新或删除操作。在大数据和实时数据处理场景中,CDC可以用来同步数据到其他系统,比......
  • 【github】使用KeepassXC 解决github Enable two-factor authentication (2FA) 第二因
    下载https://github.com/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi代理地址https://dgithub.xyz/keepassxreboot/keepassxc/releases/download/2.7.9/KeePassXC-2.7.9-Win64.msi由于该软件不允许截图,以下操作参考官网创建数据库 Kee......
  • 【深海王国】小学生都能玩的单片机!番外1:Arduino家族Uno-Mega-Nano-Pro Mini-ATtiny85
    Hi٩(๑^o^๑)۶,各位深海王国的同志们,早上下午晚上凌晨好呀~辛勤工作的你今天也辛苦啦(o゜▽゜)o☆今天大都督继续为大家带来单片机的番外系列——小学生都能玩的单片机!番外1带你快速学习认识Arduino家族:Uno、Mega、Nano、ProMini、ATtiny85,了解它们的使用场景与优......
  • Windows11重置后出现Windows.old文件夹无法删除,报错C:\Windows.old\WINDOWS\System
    问题:Windows11重置后出现Windows.old文件夹无法删除,报错C:\Windows.old\WINDOWS\System32\WDI-目录不是空的。网上的各种方法均不奏效:https://baike.baidu.com/item/windows.old/2431751?fr=ge_ala最终的解决方法:发现在WDI文件夹下是空的,于是在这个文件夹下面建立了......
  • 深搜---油田问题
    题目描述GeoSurvComp地质调查公司负责探测地下石油储藏。GeoSurvComp现在在一块矩形区域探测石油,并把这个大区域分成了很多小块。他们通过专业设备,来分析每个小块中是否蕴藏石油。如果这些蕴藏石油的小方格相邻,那么他们被认为是同一油藏的一部分。在这块矩形区域,可能有很多油......
  • 【待做】【AI+安全】数据集:图像分类数据集-1000
    图像分类数据集-1000Sort_1000pics数据集包含了1000张图片,总共分为10大类,分别是人(第0类)、沙滩(第1类)、建筑(第2类)、大卡车(第3类)、恐龙(第4类)、大象(第5类)、花朵(第6类)、马(第7类)、山峰(第8类)和食品(第9类),每类100张。内容类型:图像样本使用范围:图像分类、恶意家族分类推荐理由:个人感......
  • java静态代码检测-spotbugs
    以前使用的findbugs宣布在2016年后已经不做维护了,取而代之的是spotbugs.要想使用spotbugs,需要在代码仓库中做一些配置1.在maven项目的pom.xml文件中,加入依赖包: officalwebsiteurlreference: https://spotbugs.readthedocs.io/en/latest/maven.html<plugin><groupId......
  • 网络规划设计师-日常学习5-STP汇总
    目录STP定义BPDU交换时间HelloTime:ForwardDelay:MaxAge:STP接口状态STP几个重要要素根交换机(RootSwitch)主路径(RootPath)端口角色收敛时间(ConvergenceTime)STP选举过程示例网络拓扑规划如下:分析过程选举根交换机选举根端口1、比较路径开销(到根交换机A)2......
  • P10218-魔法手杖
    题面\(O(nk^2)\)我们考虑如果确定了\(ans\),如何判断是否合法?考虑从高到低逐位确定\(x\)。设\(ans\)和\(x\)的第\(i\)位为\(ans_i,x_i\)。分类讨论一波:如果\(ans_i\)为:0:无论\(x_i\)取什么,总有一边在异或\(x\)后第\(i\)位为1。\(x_i=0\),那么右子树一定......
  • uniapp(安卓苹果app端) - 微信小程序分享提示报错“由于不支持的分享类型无法分享到微
    问题说明在uni-app(手机app端)安卓Android、苹果ios系统中,解决分享微信小程序时,页面提示:由于不支持的分享类型无法分享到微信|由于应用和小程序未绑定在同意微信开放平台账号,无法分享到微信,uniApp开发App分享微信时出现无法分享且报错提示、分享不可用、微信appid、配置......