在上一篇中,我们根据命令行的 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: 并发抓取网页内容的更多相关文章

  1. Golang高并发抓取HTML图片

    Golang高并发抓取HTML图片 使用准备 1.安装Golang 2.下载爬虫包 go get -v github.com/hunterhug/marmot/util go get -v githu ...

  2. paip.抓取网页内容--java php python

    paip.抓取网页内容--java php python.txt 作者Attilax  艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog ...

  3. 使用Jsoup函数包抓取网页内容

    之前写过一篇用Java抓取网页内容的文章,当时是用url.openStream()函数创建一个流,然后用BufferedReader把这个inputstream读取进来.抓取的结果是一整个字符串.如果 ...

  4. Asp.Net 之 抓取网页内容

    一.获取网页内容——html ASP.NET 中抓取网页内容是非常方便的,而其中更是解决了 ASP 中困扰我们的编码问题. 需要三个类:WebRequest.WebResponse.StreamRea ...

  5. ASP.NET抓取网页内容的实现方法

    这篇文章主要介绍了ASP.NET抓取网页内容的实现方法,涉及使用HttpWebRequest及WebResponse抓取网页内容的技巧,需要的朋友可以参考下 一.ASP.NET 使用HttpWebRe ...

  6. ASP.NET抓取网页内容

    原文:ASP.NET抓取网页内容 一.ASP.NET 使用HttpWebRequest抓取网页内容 这种方式抓取某些页面会失败 不过,有时候我们会发现,这个程序在抓取某些页面时,是获不到所需的内容的, ...

  7. c#抓取网页内容乱码的解决方案

    写过爬虫的同学都知道,这是个很常见的问题了,一般处理思路是: 使用HttpWebRequest发送请求,HttpWebResponse来接收,判断HttpWebResponse中”Content-Ty ...

  8. C# 抓取网页内容的方法

    1.抓取一般内容 需要三个类:WebRequest.WebResponse.StreamReader 所需命名空间:System.Net.System.IO 核心代码: view plaincopy ...

  9. python 分别用python2和python3伪装浏览器爬取网页内容

    python网页抓取功能非常强大,使用urllib或者urllib2可以很轻松的抓取网页内容.但是很多时候我们要注意,可能很多网站都设置了防采集功能,不是那么轻松就能抓取到想要的内容. 今天我来分享下 ...

随机推荐

  1. Flask 中的 request 之 先知道有这么个东西

    每个框架中都有处理请求的机制(request),但是每个框架的处理方式和机制是不同的 为了了解Flask的request中都有什么东西,首先我们要写一个前后端的交互 基于HTML + Flask 写一 ...

  2. haproxy 配置文件详解 之 WEB监控平台

    HAProxy 虽然实现了服务的故障转移,但是在主机或者服务出现故障的时候,并不能发出通知告知运维人员,这对于及时性要求很高的业务系统来说,是非常不便的,不过,HAProxy 似乎也考虑到了这一点,在 ...

  3. nginx+keepalived高可用及双主模式【h】

    高可用有2中方式. 1.Nginx+keepalived 主从配置 这种方案,使用一个vip地址,前端使用2台机器,一台做主,一台做备,但同时只有一台机器工作,另一台备份机器在主机器不出现故障的时候, ...

  4. Keras 中的 verbose 参数

    在 fit( ) 和 evaluate( ) 中 都有 verbose 这个参数,但都是表示日志显示的参数. 具体如下:  fit( ) 中 的 verbose 参数: verbose:日志显示ver ...

  5. Python 3.X 练习集100题 04

    输入某年某月某日,判断这一天是这一年的第几天? 方法1: import time test_time = input("请输入日期(年-月-日):") time_struct = ...

  6. Jmeter之Bean shell使用(二)(转载)

    转载地址:https://www.cnblogs.com/puresoul/p/4949889.html 上一篇Jmeter之Bean shell使用(一)简单介绍了下Jmeter中的Bean she ...

  7. JDBC链接数据库MySQL 8.0 Public Key Retrieval is not allowed 错误的解决方法

    现象 Mybatis和Spring框架整合过程中报 com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Publ ...

  8. [转帖]Centos7防火墙配置rich-rule实现IP端口限制访问

    Centos7防火墙配置rich-rule实现IP端口限制访问 2019-02-18 18:05:35 sunny05296 阅读数 2143  收藏 更多 分类专栏: Linux   版权声明:本文 ...

  9. 67 GC 垃圾回收机制

    GC是JVM中自带的一个线程 他会不定时来堆中回收那些不再使用的对象,以释放内存 可以通过System.gc()来主动建议jvm尽快调度gc来回收垃圾 不同的平台的jvm对GC的策略是有所不同的 我们 ...

  10. Win10 UEFI 系统安装教程

    1:首先我们需要先拿一个U盘,制作一个带UEFI PE.(网上的大白菜以及老毛桃都可以,我用的是从网上找的WIN10 PE.WIN10 PE的好处是集成了NVME驱动,可以认得到SM951 NVME版 ...