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)
}