GoビギナーがPDFのクローラー作った

##はじめに
2017年4月22日〜23日，千葉県某所にてGoエンジニア合宿に参加したので，
そこで作ってみた物と，Goの感想などを記事にしようと思います．

参加したイベント詳細
[Go合宿2017](https://go-beginners.connpass.com/event/47481/)
![a5356bf154130f483221a0d4e01fadf9.png](https://qiita-image-store.s3.amazonaws.com/0/137949/cfe94639-1f82-b29b-4177-7bb863730347.png)

Gopherかわえぇ...



###自分のスペック
- Go歴(ほぼ)0日
- A Tour of Goリタイア勢
- 最近はSwiftでアプリ作ってる
- サーバーサイドはほとんど触ったことない

#Go＆クローラーの話
おそらく一番有名なチュートリアルの[A Tour of Go](https://go-tour-jp.appspot.com)でフィボナッチ数列の練習問題までやったら謎の自信が出て来た()

Goの，goroutineとchannelを使って非同期でバババッと気持ちいいことしたくなったので，
クローラーを作ることに

クローラー概要・使い方
main関数内のurlにダウンロードしたいPDFがあるページのURLを入れる．
実行．
PDFがデフォのいい感じの名前になってダウンロードされる．
ディレクトリを指定してダウンロードするやり方がわからなかったので，残念なことにソースコードと同じディレクトリに保存される(本当に残念)



以下ソースコード

```go
package main

import (
	//goのスクレイピングモジュール
	"github.com/PuerkitoBio/goquery"
	"net/url"
	"net/http"
	"strings"
	"io"
	"os"
	"sync"
	"log"
)

func GetPdf_goroutine(url_string string) {
	//
	wait := new(sync.WaitGroup)
	doc, _ := goquery.NewDocument(url_string)
	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
		path , _ := s.Attr("href")
		if (strings.Index(path, ".pdf") != -1 ) {
				result, _ := url.Parse(url_string)
			//この辺が冗長な気がする
			i := strings.LastIndex(path, "/")
			file_name := path[i+1:len(path)]
			go func(path string) {
				wait.Add(1)
				//この辺も冗長な気がする
				response, err := http.Get(result.Scheme + "://" + result.Host + path)
				if err != nil {
					panic(err)
				}
				defer response.Body.Close()
				file, _ := os.Create(string(file_name))
				defer file.Close()
				io.Copy(file, response.Body)
				wait.Done()
			}(path)
			wait.Wait()
		}
	})
}

func GetPdf(url_string string) {
	doc, _ := goquery.NewDocument(url_string)
	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
		path , _ := s.Attr("href")
		if (strings.Index(path, ".pdf") != -1 ) {
			result, _ := url.Parse(url_string)
			//この辺も冗長な気がする
			i := strings.LastIndex(path, "/")
			file_name := path[i+1:len(path)]
			//この辺も冗長な気がする
			response, err := http.Get(result.Scheme + "://" + result.Host + path)
			if err != nil {
				panic(err)
			}
			defer response.Body.Close()
			file, _ := os.Create(string(file_name))
			defer file.Close()
			io.Copy(file, response.Body)
		}
	})
}


func main() {
	log.Print("Start",)
　　　　　　　　//例として大学の入試ページの入試要項を保存する．大学のサーバーごめんなさい．	
　　　　　　　　url := "http://www.iwate-pu.ac.jp/examination/gakubu.html"
	GetPdf_goroutine(url)
	log.Print("Finish",)
}


```
###goroutine有無の比較
![スクリーンショット 2017-04-23 20.35.30.png](https://qiita-image-store.s3.amazonaws.com/0/137949/0457fdf1-276c-dce7-5656-0bac1efbe3da.png)

syncの使い方が怪しいのですが，これくらい差が出ました．データを引っ張って来たりする時はgoを付ける(単純)
goroutineを使うことで半分の時間，倍の速度でダウンロードできた．これすごくない？ 

Node.jsで作るクローラーより，ソースコードが見やすい，非同期処理がめっちゃ簡単という印象を受けた．
逆にめんどくせーなと思ったのは，未使用のパッケージをインポートしてると怒られるところ．ポチポチコメントアウトしながらコード書いてるので，いちいち指摘されるのは少々面倒

#圡善旅館の話
神
[圡善旅館](http://www.dozenryokan.com/)

研究室の合宿はここでしよう（岩手並感）

#まとめ感想

ただただ勉強しただけなのに，賞までいただけたので，本当に実りある合宿になった．
初めてGoを勉強したが，Cから入った自分にはとても既視感のある言語でとっつきやすい．
早い，単純，みんなで開発しやすそう

今後もGoでなんかやることが増えそう


**みんな圡善旅館に行こう‼︎**


#参考にした記事
- 第5章　並行プログラミング―ゴルーチンとチャネルを使いこなす
http://gihyo.jp/dev/feature/01/go_4beginners/0005
- goroutine 使い方まとめ
http://qiita.com/fukumone/items/8330e8c67c660f1e8d4c
- sync.WaitGroupの正しい使い方
http://qiita.com/ruiu/items/dba58f7b03a9a2ffad65
- golangでweb画像収集クローラーを作る
http://qiita.com/7yan00/items/1d814d9ed3fbaa4dc6fb