植物のミカタの任意のページから複数の画像を取得してみる。

func main() {

url := "https://saitodev.co/article/%E6%AF%94%E5%8F%A1%E5%B1%B1%E3%81%AE%E5%B1%B1%E9%A0%82%E4%BB%98%E8%BF%91%E3%81%AB%E3%81%82%E3%81%A3%E3%81%9F%E5%A4%A7%E3%81%8D%E3%81%AA%E5%B2%A9"
response, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
imagePaths := make([]string, 0)
r := regexp.MustCompile("<img.*src=\"(.*?)\".*?>")
reader := bufio.NewReader(response.Body)
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
res := r.FindStringSubmatch(line)
if len(res) > 1 && strings.Index(res[1], "/site/files") >= 0 {
imagePaths = append(imagePaths, res[1])
}
}
fmt.Println(imagePaths)
}

まず、トップページのアドレスをコピーしurl変数に代入する。response, err := http.Get(url)でデータを取得する。imagePaths := make([]string, 0)で0個の値をもつimagePathsスライスを作成する。r := regexp.MustCompile("<img.*src=\"(.*?)\".*?>")の正規表現でsrc=の属性の値を探す。reader := bufio.NewReader(response.Body)でresponse.Bodyを全部読み込むという意味になる。line, err := reader.ReadString('\n')で改行コードを発見するまで読みこんだものがlineになる。

if err == io.EOF でエラーにEoFが返ってきたら、繰り返しを終わる(break)。res := r.FindStringSubmatch(line)でlineの中で、rの条件のものがあるかないかを検索する。

if len(res) > 1で検索したものがあれば、かつ(&&)、strings.Index(res[1], "/site/files") >= 0でres[1]に/site/filesの文字列があれば、append(imagePaths, res[1])でimagePathsにres[1]を追加するという意味になる。strings.Indexは、文字列操作の機能をまとめたパッケージの関数で、指定した文字列が含まれれば、返し点を返す、含まれなければ-1を返す。

そうすると、画像のパスがとれる。ここで、fmt.Println(imagePaths)すると下記のように表示される。

※ioutil.ReadAllと bufio.NewReaderの違いは、区切って読みこむときは、bufio.NewReader、全部読むこむ時は、ioutil.ReadAllを使う。パスとは、データがある場所を意味する。

path

次にファイルを作成し、画像のパスを書き込む作業を行う。

length := len(imagePaths)
if length > 0 {
for i := 0; i < length; i++ {
imagePath := imagePaths[i]
n := strings.LastIndex(imagePath, "/")
p := imagePath[n+1:]

url = "https://saitodev.co" + imagePath
resp, err := http.Get(url)
if err == nil {
file, err := os.Create(p)
if err != nil {
log.Fatal(err)
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
file, err := os.Create(p)
if err != nil {
log.Fatal(err)
}
file.Write(body)
file.Close()
}
resp.Body.Close()
}
}

length := len(imagePaths)で、imagePathsのスライスの個数をlengthとする。今、lengthの中に入っているスライスを一つづつ読み込む作業が必要になる。まず、if length > 0で画像のパスがあるならば、for i := 0; i < length; i++ {でlengthの中に入っている数だけ、繰り返す。n := strings.LastIndex(imagePath, "/")で、imagePathの中で最後に/がある部分が最初の文字から何番目にあるかを示すnを作成する。n+1番目からが、画像の名前になるので、p := imagePath[n+1:]で画像のファイル名を表すpを作成する。

url = "https://saitodev.co" + imagePathで画像のurlが作成する。resp, err := http.Get(url)で画像のデータをrespで取得する。ちなみにrespには、下記のようなデータが入っている。

body

body, err := ioutil.ReadAll(resp.Body)で取得したデータをバイト型の値に変えたbodyを作成する。file, err := os.Create(p)でpという名前のファイルを作成する。file.Write(body)でファイルにbodyを書き込む。すると、下記のように画像ファイルが複数作成される。

※Getでデータを取得する際、if err == nilで、errがなければ、処理を続けるにしている。

picture