0
1

More than 3 years have passed since last update.

Golang sync.WaitGroupとsemaphoreを使って10並列で画像ダウンロード

Last updated at Posted at 2020-05-24

sync.WaitGroupとsemaphoreを使って10並列で画像ダウンロードするサンプルスクリプトです。
並列実行数が10を超過しないように、semaphoreを使っています。便利です
sync.semaphore の実装を読んでみると面白いですよ

golang.org/x/sync/semaphoreを使ってゴルーチンの同時実行数を制御する を参考にしつつ実装してみました

main.go
package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "os"
    "sync"

    "golang.org/x/sync/semaphore"
)

var wg *sync.WaitGroup
var sm = semaphore.NewWeighted(10) // 10並列で取得
var maxPage = 50                   // 最大ページ取得数

// main 実行方法: GO111MODULE=off go run main.go
func main() {
    // 検索元: https://www.jisc.go.jp/app/jis/general/GnrJISSearch.html
    target := map[string]string{
        "X0001": "kAIAALDEpH2u0O895seN",
        "X0002": "UQMAALzrEixtvmtcZ4zt",
        "X0003": "VAMAACJ6MCttlyDSqCTz",
    }

    for name, pageUrl := range target {
        if pageUrl == "" {
            continue
        }
        download(name, pageUrl, maxPage)
    }
}

func download(name string, pageUrl string, maxPage int) {
    wg = &sync.WaitGroup{}
    makeDir(fmt.Sprintf("page/%s", name))
    for i := 1; i <= maxPage; i++ {
        wg.Add(1)
        go getImage(name, pageUrl, uint(i))
    }
    wg.Wait()
}

func makeDir(path string) {
    os.Mkdir(path, 0777)
}

func getImage(name string, pageUrl string, page uint) {
    defer wg.Done()
    // 並列実行数を制御
    if err := sm.Acquire(context.Background(), 1); err != nil { // 指定した同時実行数制限smから1つ実行権限を取得。上限に達していて取得できない場合は、取得でき次第、実行を開始
        fmt.Println(fmt.Sprintf("sm.Acquire(ctx, 1) error. err: %+v", err))
        return
    }
    defer sm.Release(1) // 最後にsm実行権限枠を1つ空ける

    sb := map[string]string{
        "X0001": "pdfb2",
        "X0002": "pdfa7",
        "X0003": "pdfb5",
    }
    serverName, _ := sb[name]

    // https://www.jisc.go.jp/pdfb5/PDFView/GetImage/VAMAACJ6MCttlyDSqCTz?pageNo=1&width=1200&seq=0
    urlBase := "https://www.jisc.go.jp/%s/PDFView/GetImage/%s?pageNo=%d&width=1200&seq=0"
    url := fmt.Sprintf(urlBase, serverName, pageUrl, page)
    fmt.Println(fmt.Sprintf("[+]start PagetNo.%d url:%s", page, url))
    // 画像データ取得と取得結果をHTTP Statusで振り分け
    response, err := http.Get(url)
    if err != nil {
        return
    }
    if response.StatusCode >= 300 {
        return // HTTP Statusが300以上で取得失敗
    }
    defer response.Body.Close()

    // 画像ファイル保存
    filePath := fmt.Sprintf("./page/%s/%d.png", name, page)
    file, err := os.Create(filePath)
    if err != nil {
        return
    }
    defer file.Close()

    io.Copy(file, response.Body)
}

実行結果
スクリーンショット 2020-05-24 21.38.32.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1