Golang: 并发抓取网页内容
在上一篇中,我们根据命令行的 URL 参数输入,抓取对应的网页内容并保存到本地磁盘,今天来记录一下如何利用并发,来抓取多个站点的网页内容。
首先,我们在上一次代码的基础上稍作改造,使它能够获取多个站点的内容。下面代码中,我们首先定义好三个 URL,然后逐个发送网络请求,获取数据并保存,最后统计消耗的总时间:
// fetch.go
package main
import (
"os"
"fmt"
"time"
"regexp"
"net/http"
"io/ioutil"
)
// 创建正则常量
var RE = regexp.MustCompile("\\w+\\.\\w+$")
func main() {
urls := []string {
"http://www.qq.com",
"http://www.163.com",
"http://www.sina.com",
}
// 开始时间
start := time.Now()
for _, url := range urls {
start := time.Now()
// 发送网络请求
res, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
// 读取资源数据
body, err := ioutil.ReadAll(res.Body)
// 关闭资源
res.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
fileName := getFileName(url)
// 写入文件
ioutil.WriteFile(fileName, body, 0644)
// 消耗的时间
elapsed := time.Since(start).Seconds()
fmt.Printf("%.2fs %s\n", elapsed, fileName)
}
// 消耗的时间
elapsed := time.Since(start).Seconds()
fmt.Printf("%.2fs elapsed\n", elapsed)
}
// 获取文件名
func getFileName(url string) string {
// 从URL中匹配域名后面部分
return RE.FindString(url) + ".txt"
}
在上面代码中,我们使用正则表达式来从 URL 中匹配域名后面部分,作为最终的文件名。关于正则表达式,后续会做总结。
下面来看看程序运行后的控制台信息:
$ ./fetch
0.12s qq.com.txt
0.20s 163.com.txt
0.27s sina.com.txt
0.59s elapsed
从打印信息中可以看出,最后消耗的总时间等于三次执行的总和。这种方式效率低下,并且不能充分利用计算机资源,下面我们就对程序进行改造,使其能够并发地执行三个抓取操作:
// fetch.go
package main
import (
"os"
"fmt"
"time"
"regexp"
"net/http"
"io/ioutil"
)
// 创建正则
var RE = regexp.MustCompile("\\w+\\.\\w+$")
func main() {
urls := []string {
"http://www.qq.com",
"http://www.163.com",
"http://www.sina.com",
}
// 创建channel
ch := make(chan string)
// 开始时间
start := time.Now()
for _, url := range urls {
// 开启一个goroutine
go fetch(url, ch)
}
for range urls {
// 打印channel中的信息
fmt.Println(<-ch)
}
// 总消耗的时间
elapsed := time.Since(start).Seconds()
fmt.Printf("%.2fs elapsed\n", elapsed)
}
// 根据URL获取资源内容
func fetch(url string, ch chan<- string) {
start := time.Now()
// 发送网络请求
res, err := http.Get(url)
if err != nil {
// 输出异常信息
ch <- fmt.Sprint(err)
os.Exit(1)
}
// 读取资源数据
body, err := ioutil.ReadAll(res.Body)
// 关闭资源
res.Body.Close()
if err != nil {
// 输出异常信息
ch <- fmt.Sprintf("while reading %s: %v", url, err)
os.Exit(1)
}
// 写入文件
ioutil.WriteFile(getFileName(url), body, 0644)
// 消耗的时间
elapsed := time.Since(start).Seconds()
// 输出单个URL消耗的时间
ch <- fmt.Sprintf("%.2fs %s", elapsed, url)
}
// 获取文件名
func getFileName(url string) string {
// 从URL中匹配域名部分
return RE.FindString(url) + ".txt"
}
上面代码中,我们先创建一个 channel,然后对每个抓取操作开启一个 goroutine,待抓取程序完成后,通过 channel 发送消息告知主线程,主线程再做相应的处理操作。关于这部分的原理细节,后续再做总结。
我们运行上面的程序,执行结果如下:
$ ./fetch
0.10s http://www.qq.com
0.19s http://www.163.com
0.29s http://www.sina.com
0.29s elapsed
从结果中可以看出,最后消耗的总时间与耗时最长的那个操作等同,可见并发在性能方面带来的提升是非常可观的。
Golang: 并发抓取网页内容的更多相关文章
- Golang高并发抓取HTML图片
Golang高并发抓取HTML图片 使用准备 1.安装Golang 2.下载爬虫包 go get -v github.com/hunterhug/marmot/util go get -v githu ...
- paip.抓取网页内容--java php python
paip.抓取网页内容--java php python.txt 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog ...
- 使用Jsoup函数包抓取网页内容
之前写过一篇用Java抓取网页内容的文章,当时是用url.openStream()函数创建一个流,然后用BufferedReader把这个inputstream读取进来.抓取的结果是一整个字符串.如果 ...
- Asp.Net 之 抓取网页内容
一.获取网页内容——html ASP.NET 中抓取网页内容是非常方便的,而其中更是解决了 ASP 中困扰我们的编码问题. 需要三个类:WebRequest.WebResponse.StreamRea ...
- ASP.NET抓取网页内容的实现方法
这篇文章主要介绍了ASP.NET抓取网页内容的实现方法,涉及使用HttpWebRequest及WebResponse抓取网页内容的技巧,需要的朋友可以参考下 一.ASP.NET 使用HttpWebRe ...
- ASP.NET抓取网页内容
原文:ASP.NET抓取网页内容 一.ASP.NET 使用HttpWebRequest抓取网页内容 这种方式抓取某些页面会失败 不过,有时候我们会发现,这个程序在抓取某些页面时,是获不到所需的内容的, ...
- c#抓取网页内容乱码的解决方案
写过爬虫的同学都知道,这是个很常见的问题了,一般处理思路是: 使用HttpWebRequest发送请求,HttpWebResponse来接收,判断HttpWebResponse中”Content-Ty ...
- C# 抓取网页内容的方法
1.抓取一般内容 需要三个类:WebRequest.WebResponse.StreamReader 所需命名空间:System.Net.System.IO 核心代码: view plaincopy ...
- python 分别用python2和python3伪装浏览器爬取网页内容
python网页抓取功能非常强大,使用urllib或者urllib2可以很轻松的抓取网页内容.但是很多时候我们要注意,可能很多网站都设置了防采集功能,不是那么轻松就能抓取到想要的内容. 今天我来分享下 ...
随机推荐
- 网络协议 11 - Socket 编程(下)
之前我们基本了解了网络通信里的大部分协议,一直都是在“听”的过程.很多人都会觉得,好像看懂了,但关了页面回忆起来,好像又什么都没懂.这次咱们就“真枪实弹”的码起来,再用一个“神器”-网络分析系统详细跟 ...
- 【CSP-S膜你考】那23个路口
那23个路口 题面 故事的起源不加赘述,那23个路口. 单刀直入,我直接说题的意思. 蚊子和疯子在做一件事,就是他们要在茫茫的大街上找一个出发点,然后从出发点开始,经过上下左右23次拐弯,到达一个他们 ...
- 【luoguP2252】 取石子游戏
题目链接 定义\(f[i][j]\)表示\(a=i,b=j\)时是必胜态还是必败态,博弈DP可以解决\(a,b \leq 100\) 的情况 然后就可以找规律了,发现\(f[i][j]=0\)的情况很 ...
- Guava 源码分析之Cache的实现原理
Guava 源码分析之Cache的实现原理 前言 Google 出的 Guava 是 Java 核心增强的库,应用非常广泛. 我平时用的也挺频繁,这次就借助日常使用的 Cache 组件来看看 Goog ...
- kube-prometheus部署
一.从git拉取相应yaml文件 git clone https://github.com/coreos/kube-prometheus.git 二.修改grafana及prometheus的serv ...
- Appium Grid并发测试
背景 Selenium玩的比较6的同学比较清楚:在Selenium中三大组件中有包含了Selenium Grid,而其作用就是分布式执行测试用例.主要的应用场景在于: 缩短测试执行时间,提高自动化测试 ...
- 关于Linux TCP "SACK PANIC" 远程拒绝服务漏洞的修复
Linux 内核被曝存在TCP "SACK PANIC" 远程拒绝服务漏洞(漏洞编号:CVE-2019-11477,CVE-2019-11478,CVE-2019-11479),攻 ...
- Git命令之tag差异比较
比较两个tag的区别有以下几种方式: 例如两个tag分别为v1.0. v2.0 1. git log v2.0 ^v1.0 查看v2.0上有,而v1.0上没有的部分: 2. git log v1.0. ...
- 遍历 HashSet 的方法
遍历 HashSet 的方法 import java.util.HashSet; import java.util.Iterator; import java.util.Set; public cla ...
- Scala 数组操作之Array、ArrayBuffer以及遍历数组
ArrayBuffer 在Scala中,如果需要类似于Java中的ArrayList这种长度可变的集合类,则可以使用ArrayBuffer. // 如果不想每次都使用全限定名,则可以预先导入Array ...