質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

4564閲覧

golang で webスクレイピングのスクリプトのエラー、と解決方法について

ShouYama

総合スコア36

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

1クリップ

投稿2016/08/24 01:19

#やりたいこと
http://recipe.rakuten.co.jp/recipe/1150010609/
このサイトのレシピデータをgo言語でスクレイピングし、その後JSONで返すapIを作りたです
#JSON

json

1{ 2name:"料理名", 3image:"料理写真", 4explanation:"料理の説明" 5material: { name:["各材料の名前をもつstringの配列"], 6 quantity:["各材料の分量をstringの配列]}, 7process:[{image:"料理工程の写真(ここわからない)", 8 quantity:"作業内容"},.....] 9}

#現在のコード

go

1package main 2 3import ( 4 "encoding/json" 5 "io/ioutil" 6 "os" 7 8 "github.com/PuerkitoBio/goquery" 9) 10 11type process struct { 12 Image string `json:"image"` 13 Operation string `json:"operation"` 14} 15 16type material struct { 17 Name []string `json:"name"` 18 Quantity []string `json:"quantity"` 19} 20 21//メインのjson 22type mesi struct { 23 Name string `json:"name"` 24 Image string `json:"image"` 25 MemberNum string `json:"membernum"` 26 Explanation string `json:"n"` 27 Material material `json:"material"` 28 Process []process `json:"process"` 29} 30 31//料理名(タイトル)を返す。 32func title(url string) string { 33 var i string 34 doc, _ := goquery.NewDocument(url) 35 doc.Find("h1").Each(func(_ int, s *goquery.Selection) { 36 i = s.Text() 37 }) 38 return i 39} 40 41//何人前かを返す関数 42func people(url string) string { 43 var i string 44 doc, _ := goquery.NewDocument(url) 45 doc.Find("div > div > div > div > div > h3 > span > span").Each(func(_ int, s *goquery.Selection) { 46 i = s.Text() 47 }) 48 return i 49} 50 51//材料の量をstringの配列で返す 52func materialQuantity(url string) []string { 53 var quantity []string 54 doc, _ := goquery.NewDocument(url) 55 doc.Find("div > div > div > div > ul > li > p.amount").Each(func(_ int, s *goquery.Selection) { 56 quantity = append(quantity, s.Text()) 57 }) 58 return quantity 59} 60 61//手順をstringの配列で返す 62func procedure(url string) []string { 63 var procedure []string 64 doc, _ := goquery.NewDocument(url) 65 doc.Find("li#step_box_li.stepBox > p.stepMemo").Each(func(_ int, s *goquery.Selection) { 66 procedure = append(procedure, s.Text()) 67 }) 68 return procedure 69} 70 71//完成時の写真のurlをstringで返す。 72func image(url string) string { 73 var imgURL string 74 doc, _ := goquery.NewDocument(url) 75 doc.Find("div > div > span > img").Each(func(_ int, s *goquery.Selection) { 76 url, _ := s.Attr("src") 77 // fmt.Println(url) 78 imgURL = url 79 }) 80 return imgURL 81} 82 83//調理時のコツについてをstringで返す。 84func kotu(url string) string { 85 var kotu string 86 doc, _ := goquery.NewDocument(url) 87 doc.Find("div.howtoPointBox.last > p").Each(func(_ int, s *goquery.Selection) { 88 kotu = s.Text() 89 }) 90 return kotu 91} 92 93//材料の名前をstringの配列で返す 94func mat(url string) []string { 95 var materials []string 96 doc, _ := goquery.NewDocument(url) 97 doc.Find("div > div > div > div > ul > li > a#material_link.name").Each(func(_ int, s *goquery.Selection) { 98 materials = append(materials, s.Text()) 99 }) 100 return materials 101} 102 103//説明文を返す。 104func exp(url string) string { 105 var i string 106 doc, _ := goquery.NewDocument(url) 107 doc.Find(" div >div > div > div > p.summary").Each(func(_ int, s *goquery.Selection) { 108 i = s.Text() 109 }) 110 return i 111} 112 113//各pointを1.もう一度作りたい 2.簡単だった 3.節約できた 114// func point(url string) []int64 { 115// var points []string 116// doc, _ := goquery.NewDocument(url) 117// doc.Find("dl > dd > div > p.num ").Each(func(_ int, s *goquery.Selection) { 118// points = append(points, s.Text()) 119// }) 120// return points 121// } 122 123func main() { 124 var recipe mesi 125 var mat material 126 var proc [30]process 127 s := 0 128 url := "http://recipe.rakuten.co.jp/recipe/1150010609/" 129 130 recipe.Name = title(url) 131 recipe.Image = image(url) 132 recipe.MemberNum = people(url) 133 resipe.Explanation = exp(url) 134 135 mat.Quantity = materialQuantity(url) 136 mat.Name = mat(url) 137 resipe.Material = mat 138 for i := range procedure(url) { 139 proc[s].Operation = append(proc.Operation, i) 140 s++ 141 } 142 recipe.Process = proc 143 bytes, _ := json.Marshal(recipe) 144 ioutil.WriteFile("./test.json", bytes, os.ModePerm) 145}

#エラー

# command-line-arguments ./getCooking.go:133: undefined: resipe in resipe.Explanation ./getCooking.go:133: cannot assign to resipe.Explanation ./getCooking.go:136: cannot call non-function mat (type material) ./getCooking.go:137: undefined: resipe in resipe.Material ./getCooking.go:137: cannot assign to resipe.Material ./getCooking.go:139: proc.Operation undefined (type [30]process has no field or method Operation) ./getCooking.go:142: cannot use proc (type [30]process) as type []process in assignment

#知りたいこと

  • エラーの解消方法を教えていただきたい
  • 作り方の途中で出てくる写真がない時があって、その場合どうやってそこをスキップして、作業工程の番号と同じjsonの配列の中に入れられるのかを教えていただきたい

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

エラーの解消方法を教えていただきたい

undefined: ...となっているのは、変数が定義されていないことを示します。
定義しているはずの変数がundefined: ...となるのは、名前が間違っていることが考えられます。

cannot call non-function mat (type material)の箇所は、
matmaterial型の変数だから関数として使えない、と言っています。
matという名前を使おうとした場合、Goは変数の方だと思っているわけです。

cannot use proc (type [30]process) as type []process in assignmentは、
固定長配列とスライスは互換性が無いので代入できないためです。

procedureの結果をループするところは、色々おかしいですね。
proc.Operationはスライスでないのでappendできません。もしappendするとしてもprocの方でしょう。
下記のようにすれば結果のstring配列をそれぞれのOperationにセットできます。
変数sは不要です。

lang

1for i, v := range procedure(url) { 2 proc[i].Operation = v 3}

作り方の途中で出てくる写真がない時があって、その場合どうやってそこをスキップして、作業工程の番号と同じjsonの配列の中に入れられるのかを教えていただきたい

こちらは、結果としてどうしたいのかを決めていただく必要があります。

投稿2016/08/24 03:37

argius

総合スコア9388

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ShouYama

2016/08/24 04:14

anic: runtime error: index out of range goroutine 1 [running]: panic(0x3a52e0, 0xc82000a100) /usr/local/Cellar/go/1.6.2/libexec/src/runtime/panic.go:481 +0x3e6 main.main() /Users/yamamshou/Bot/line_sc/getCooking.go:134 +0x493 exit status 2 こうなってしましました。
ShouYama

2016/08/24 04:15

package main import ( "encoding/json" "io/ioutil" "os" "github.com/PuerkitoBio/goquery" ) type process struct { Image string `json:"image"` Operation string `json:"operation"` } type material struct { Name []string `json:"name"` Quantity []string `json:"quantity"` } //メインのjson type mesi struct { Name string `json:"name"` Image string `json:"image"` MemberNum string `json:"membernum"` Explanation string `json:"explanation"` Material material `json:"material"` Process []process `json:"process"` } //料理名(タイトル)を返す。 func title(url string) string { var i string doc, _ := goquery.NewDocument(url) doc.Find("h1").Each(func(_ int, s *goquery.Selection) { i = s.Text() }) return i } //何人前かを返す関数 func people(url string) string { var i string doc, _ := goquery.NewDocument(url) doc.Find("div > div > div > div > div > h3 > span > span").Each(func(_ int, s *goquery.Selection) { i = s.Text() }) return i } //材料の量をstringの配列で返す func materialQuantity(url string) []string { var quantity []string doc, _ := goquery.NewDocument(url) doc.Find("div > div > div > div > ul > li > p.amount").Each(func(_ int, s *goquery.Selection) { quantity = append(quantity, s.Text()) }) return quantity } //手順をstringの配列で返す func procedure(url string) []string { var procedure []string doc, _ := goquery.NewDocument(url) doc.Find("li#step_box_li.stepBox > p.stepMemo").Each(func(_ int, s *goquery.Selection) { procedure = append(procedure, s.Text()) }) return procedure } //完成時の写真のurlをstringで返す。 func image(url string) string { var imgURL string doc, _ := goquery.NewDocument(url) doc.Find("div > div > span > img").Each(func(_ int, s *goquery.Selection) { url, _ := s.Attr("src") // fmt.Println(url) imgURL = url }) return imgURL } //調理時のコツについてをstringで返す。 func kotu(url string) string { var kotu string doc, _ := goquery.NewDocument(url) doc.Find("div.howtoPointBox.last > p").Each(func(_ int, s *goquery.Selection) { kotu = s.Text() }) return kotu } //材料の名前をstringの配列で返す func mat(url string) []string { var materials []string doc, _ := goquery.NewDocument(url) doc.Find("div > div > div > div > ul > li > a#material_link.name").Each(func(_ int, s *goquery.Selection) { materials = append(materials, s.Text()) }) return materials } //説明文を返す。 func exp(url string) string { var i string doc, _ := goquery.NewDocument(url) doc.Find(" div >div > div > div > p.summary").Each(func(_ int, s *goquery.Selection) { i = s.Text() }) return i } func main() { var recipe mesi var mats material var proc []process url := "http://recipe.rakuten.co.jp/recipe/1150010609/" recipe.Name = title(url) recipe.Image = image(url) recipe.MemberNum = people(url) recipe.Explanation = exp(url) mats.Quantity = materialQuantity(url) mats.Name = mat(url) recipe.Material.Name = mats.Name recipe.Material.Quantity = mats.Quantity for i, v := range procedure(url) { proc[i].Operation = v } recipe.Process = make([]process, len(proc)) copy(recipe.Process, proc) bytes, _ := json.Marshal(recipe) ioutil.WriteFile("./test.json", bytes, os.ModePerm) }
ShouYama

2016/08/24 04:15

上コードです
ShouYama

2016/08/24 04:24

例えば、7つの作業工程があった場合、で2,5,7の工程に写真があったとしたら {image:["","url2","","","url5","","url7"]} みたいにしたいのですが、この時の空白の部分を詰めてしまって、うまく開けられないのでどうしたらいいのかな、と困っています。
argius

2016/08/24 04:30

私が書いたのはprocを[30]processに合わせた場合の例でした。 スライスにした場合、こうします。 proc = make([]process, 0) for _, v := range procedure(url) { p := process{Operation: v} proc = append(proc, p) } 配列とスライスの違い、スライスの使い方について理解が不足していらっしゃるようですので、その辺を学習なさって下さい。
ShouYama

2016/08/24 04:40

ありがとうございます! エラーは解消しました! スクレイピングについてなのですがどうですか?
argius

2016/08/24 04:52

> スクレイピングについてなのですがどうですか? {image:["","url2","","","url5","","url7"]}みたいにしたいところですよね? 今考え中です。 質問と関係ないですが、 このコードだとgoquery.NewDocument(url)を何度も呼び出して無駄があるように見えます。 (何度もページにアクセスしてしまう) doc, _ := goquery.NewDocument(url)をmainに書いて docを使いまわした方が良いと思います。
argius

2016/08/24 04:57

そもそもですが、 いまは processの配列でimageとoperationをセットにしているので、 "process":[{"image":"","operation":"step1"},{"image":"","operation":"step2"},...] みたいになってますよね。 だったら {image:["","url2","","","url5","","url7"]} みたいにならなくないですか?
ShouYama

2016/08/24 05:26

間違いました、写真のないところは""にしたいんです。
argius

2016/08/24 05:38

> 間違いました、写真のないところは""にしたいんです。 えーと、すみません、 {image:["","url2","","","url5","","url7"]} は既に、写真のないところは""になっているように見えますが、 どこが間違いですか? それに、処理の方はprocessの配列でimageとoperationをセットにしているのに、 "process":[{"image":"","operation":"step1"},{"image":"","operation":"step2"},...] ではなくて {image:["","url2","","","url5","","url7"]} にしようとしているのはなぜですか? もし{image:["","url2","","","url5","","url7"]}を 個別に持とうとしているのでしたら、 type mesi structのどこに入れるのかを示してください。
ShouYama

2016/08/24 06:16

えーっとですね、まずもって、goでスクレイピングをする時に、この楽天のレシピサイトは 作り方のところに写真がない工程とある工程があるので、画像のすべてのurlはとってこれるんですけどこれがどの、工程の写真かを判定するほうほうがわからなくて教えて欲しいというのが、質問で 上の{image:["","url2","","","url5","","url7"]}っていうのは、image:っていうのが間違えで、イメージとして、スクレイピングする関数が["","url2","","","url5","","url7"]こんな感じで返してきてそれを、他の要素と同様にmainで{image:"料理工程の写真(ここわからない)"くっつけたいっていうイメージをもって発言していました。
argius

2016/08/24 06:38

> スクレイピングする関数が["","url2","","","url5","","url7"]こんな感じで返してきて ああ、なるほど、関数の結果のことを言っていたんですね。 それなら、processの配列を返す関数を作れば良いんじゃないでしょうか。 li.stepBox単位で取り出して、 その下にあるdiv.stepPhoto > span > imgをprocess.Imageに、 p.stepMemoをprocess.Operationにセットする。 これをli.stepBoxの要素数分繰り返せば、 processの配列が完成します。 この関数を作れば、 recipe.Processに直接その結果を代入できて、 先ほどのmain側のスライス生成は不要になります。 その代わり、そのスライス生成の部分は関数側に書きます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問