Go言語でYahoo!ショッピングの商品検索を行う。

# やりたいこと

Go言語の勉強の一環として、Yahoo!ショッピングの商品検索結果を取得するプログラミングを作成する。

# 環境

* Windows 11 Pro
* Go バージョン 1.22.4

# 事前準備

Yahoo!デベロッパーネットワークにアプリケーションを登録し、Client IDを取得する。

https://developer.yahoo.co.jp

![スクリーンショット 2024-07-12 144237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624329/66f1666d-294b-bcda-8e6b-fd1a61f09870.png)

# コード

```main.go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

type Response struct {
	TotalResultsAvailable int     `json:"totalResultsAvailable"`
	TotalResultsReturned  int     `json:"totalResultsReturned"`
	FirstResultPosition   int     `json:"firstResultPosition"`
	Request               Request `json:"request"`
	Hits                  []Hit   `json:"hits"`
}

type Request struct {
	Query string `json:"query"`
}

type Hit struct {
	Index                 int             `json:"index"`
	Name                  string          `json:"name"`
	Description           string          `json:"description"`
	HeadLine              string          `json:"headLine"`
	URL                   string          `json:"url"`
	InStock               bool            `json:"inStock"`
	Code                  string          `json:"code"`
	Condition             string          `json:"condition"`
	ImageID               string          `json:"imageId"`
	Image                 Image           `json:"image"`
	Review                Review          `json:"review"`
	AffiliateRate         float64         `json:"affiliateRate"`
	Price                 int             `json:"price"`
	PremiumPrice          int             `json:"premiumPrice"`
	PremiumPriceStatus    bool            `json:"premiumPriceStatus"`
	PremiumDiscountType   interface{}     `json:"premiumDiscountType"`
	PremiumDiscountRate   interface{}     `json:"premiumDiscountRate"`
	PriceLabel            PriceLabel      `json:"priceLabel"`
	Point                 Point           `json:"point"`
	Shipping              Shipping        `json:"shipping"`
	GenreCategory         GenreCategory   `json:"genreCategory"`
	ParentGenreCategories []GenreCategory `json:"parentGenreCategories"`
	Brand                 Brand           `json:"brand"`
	ParentBrands          []Brand         `json:"parentBrands"`
	JanCode               string          `json:"janCode"`
	ReleaseDate           interface{}     `json:"releaseDate"`
	Seller                Seller          `json:"seller"`
	Delivery              Delivery        `json:"delivery"`
	Payment               string          `json:"payment"`
}

type Image struct {
	Small  string `json:"small"`
	Medium string `json:"medium"`
}

type Review struct {
	Rate  float64 `json:"rate"`
	Count int     `json:"count"`
	URL   string  `json:"url"`
}

type PriceLabel struct {
	Taxable         interface{} `json:"taxable"`
	DefaultPrice    int         `json:"defaultPrice"`
	DiscountedPrice interface{} `json:"discountedPrice"`
	FixedPrice      interface{} `json:"fixedPrice"`
	PremiumPrice    interface{} `json:"premiumPrice"`
	PeriodStart     interface{} `json:"periodStart"`
	PeriodEnd       interface{} `json:"periodEnd"`
}

type Point struct {
	Amount        int `json:"amount"`
	Times         int `json:"times"`
	PremiumAmount int `json:"premiumAmount"`
	PremiumTimes  int `json:"premiumTimes"`
}

type Shipping struct {
	Code int    `json:"code"`
	Name string `json:"name"`
}

type GenreCategory struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Depth int    `json:"depth"`
}

type Brand struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

type Seller struct {
	SellerID     string `json:"sellerId"`
	Name         string `json:"name"`
	URL          string `json:"url"`
	IsBestSeller bool   `json:"isBestSeller"`
	Review       Review `json:"review"`
	ImageID      string `json:"imageId"`
}

type Delivery struct {
	Area     string      `json:"area"`
	DeadLine interface{} `json:"deadLine"`
	Day      interface{} `json:"day"`
}

func main() {
    // Httpクライアント作成
	client := http.Client{
		Timeout: 30 * time.Second,
	}

    // URLクエリ作成
	url := "https://shopping.yahooapis.jp/ShoppingWebService/V3/itemSearch?appid=[クライアントID]&query=[検索ワード]"
	req, err := http.NewRequestWithContext(
		context.Background(),
		http.MethodGet,
		url,
		nil)
	if err != nil {
		panic(err)
	}

    // Httpリクエスト送信して結果を受け取る
	req.Header.Add("X-My-Client", "Learning Go")
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		panic(fmt.Sprintf("unexpected status: got %v", res.Status))
	}
	fmt.Println(res.Header.Get("Content-Type"))

    // レスポンスボディの文字列(json)を取得
	buffer := make([]byte, 1024)
	var jsonString string
	for {
		c, err := res.Body.Read(buffer)
		if err != nil {
			if err == io.EOF {
				jsonString += string(buffer[:c])
				break
			}
			print(err)
		}
		jsonString += string(buffer[:c])
	}
	fmt.Println(jsonString)

    // json文字列を構造体に変換
	var response Response
	err1 := json.Unmarshal([]byte(jsonString), &response)
	if err1 != nil {
		fmt.Println("Error decoding JSON:", err1)
		return
	}

	fmt.Printf("Parsed Response: %+v\n", response)
}
```

# 実行結果(jsonを整形したもの)

```

  "totalResultsAvailable": 19112,
  "totalResultsReturned": 20,
  "firstResultsPosition": 1,
  "request": {
    "query": "なにか"
  },
  "hits": [
    {
      "index": 1,
      "name": "[ネコポス送料無料]変形フレームパーツ 5個入り 枠 フレーム 土台 ネイルパーツ メタルパーツ マニキュア セルフネイル  ジェルネイル",
      "description": "【特徴】<br>  不揃いな形がおしゃれな<br>  『変形フレームパーツ』 が新登場！<br>  <br>  そのまま使っても、<br>  中になにか埋め込んで使ってもOK ◎<br>  <br>  流行りのドゥルー ジーネイルも<br>  ワンランク上の仕上がりに ！<br>  <br>  背面カーブ加工が施されているので<br>  しっかり爪にフィットします ♪<br><br>  <br>  <br>  【サイズ】<br>  約10mm×6mm<br>  （最大サイズ）<br>  <br>  5個入り<br><br>  <br>  <br>  【カラー】<br>  ゴールド<br><br>  <br>  <br>  【ネコポス】<br>  可　[ネコポス送料無料]<br>  <br>  <br>  ■ネコポスをご利用の場合のご注意■<br>  <br>  ・ネコポスは手渡しではなくポストへ直接投函となります。<br>  その為、代金引換や日時指定は出来ません。<br>  追跡サービス有り。<br>  ・配達時の欠損・盗難・不達・延着などの<br>  保証がございません。詳しくは配送についてを<br>  ご確認の上ご利用くださいますよう<br>  お願い申し上げます。",
      "headLine": "そのまま使っても中に埋め込んでも ◎",
      "url": "https://store.shopping.yahoo.co.jp/deconail-seisakujo/loave-metalparts-440.html",
      "inStock": true,
      "code": "deconail-seisakujo_loave-metalparts-440",
      "condition": "new",
      "imageId": "deconail-seisakujo_loave-metalparts-440",
      "image": {
        "small": "https://item-shopping.c.yimg.jp/i/c/deconail-seisakujo_loave-metalparts-440",
        "medium": "https://item-shopping.c.yimg.jp/i/g/deconail-seisakujo_loave-metalparts-440"
      },
      "review": {
        "count": 32,
        "url": "https://shopping.yahoo.co.jp/review/item/list?store_id=deconail-seisakujo&page_key=loave-metalparts-440",
        "rate": 4.59
      },
      "affiliateRate": 7,
      "price": 143,
      "premiumPrice": 143,
      "premiumPriceStatus": false,
      "premiumDiscountRate": null,
      "premiumDiscountType": null,
      "priceLabel": {
        "taxable": true,
        "defaultPrice": 143,
        "discountedPrice": null,
        "fixedPrice": null,
        "premiumPrice": null,
        "periodStart": 1720746000,
        "periodEnd": null
      },
      "point": {
        "amount": 0,
        "times": 0,
        "bonusAmount": 1,
        "bonusTimes": 1,
        "premiumAmount": 0,
        "premiumTimes": 0,
        "premiumBonusAmount": 1,
        "premiumBonusTimes": 1
      },
      "shipping": {
        "code": 2,
        "name": "送料無料"
      },
      "genreCategory": {
        "id": 1826,
        "name": "ラインストーン、パーツ",
        "depth": 4
      },
      "parentGenreCategories": [
        {
          "id": 2501,
          "depth": 1,
          "name": "コスメ、美容、ヘアケア"
        },
        {
          "id": 1809,
          "depth": 2,
          "name": "ネイル用品"
        },
        {
          "id": 1821,
          "depth": 3,
          "name": "ネイルアート"
        }
      ],
      "brand": {
        "id": 38074,
        "name": "ブランド登録なし"
      },
      "parentBrands": [
        {
          "id": 1,
          "name": "ブランド"
        }
      ],
      "janCode": "4589582646050",
      "payment": "128 4096 8192 4 16 16384 2 256 512 8 1024 1 64",
      "releaseDate": null,
      "seller": {
        "sellerId": "deconail-seisakujo",
        "name": "ネイルタウン NAILTOWN",
        "url": "https://store.shopping.yahoo.co.jp/deconail-seisakujo/",
        "isBestSeller": false,
        "review": {
          "rate": 4.74,
          "count": 12490
        },
        "imageId": "deconail-seisakujo_1"
      },
      "delivery": {
        "deadLine": 10,
        "day": 1,
        "area": "13"
      }
    },
    {
      "index": 2,
      "name": "【限定販売】【あみあみ限定特典】KDcolle『蜘蛛ですが、なにか？』 原作版 「私」アラクネ/白織 1/7 完成品フィギュア[KADOKAWA]《発売済・在庫品》",
      "description": "※流通の都合により「中国」への販売はできません。 ※こちらの商品はあみあみ、他一部店舗限定での取り扱いとなります。 ※あみあみ限定特典が付きます。  <br>◆お一人様 2 点まで（本店支店合計）<br>※同一住所より複数件のご注文をいただいた場合、ご注文を取り消しさせていただくことがございます。<br>◆仕様：塗装済み完成品 【スケール】1/7 【サイズ】 全高約240mm 【素材】PVC<br>◆ブランド：KADOKAWA <br>◆原作：蜘蛛ですが、なにか？ <br>◆キャラ：私(蜘蛛子/白/白織) <br>◆作家：あきもふ<br>◆検索ワード：nw210703 _00458 _00459 _09713 CTG001 b0003871 t025695 c0042967 ami_bonus",
      "headLine": "",
      "url": "https://store.shopping.yahoo.co.jp/amiami/figure-128049.html",
      "inStock": true,
      "code": "amiami_figure-128049",
      "condition": "new",
      "imageId": "amiami_figure-128049",
      "image": {
        "small": "https://item-shopping.c.yimg.jp/i/c/amiami_figure-128049",
        "medium": "https://item-shopping.c.yimg.jp/i/g/amiami_figure-128049"
      },
      "review": {
        "count": 7,
        "url": "https://shopping.yahoo.co.jp/review/item/list?store_id=amiami&page_key=figure-128049",
        "rate": 3.57
      },
      "affiliateRate": 1,
      "price": 31350,
      "premiumPrice": 31350,
      "premiumPriceStatus": false,
      "premiumDiscountRate": null,
      "premiumDiscountType": null,
      "priceLabel": {
        "taxable": true,
        "defaultPrice": 31350,
        "discountedPrice": null,
        "fixedPrice": null,
        "premiumPrice": null,
        "periodStart": 1639710000,
        "periodEnd": null
      },
      "point": {
        "amount": 0,
        "times": 0,
        "bonusAmount": 285,
        "bonusTimes": 1,
        "premiumAmount": 0,
        "premiumTimes": 0,
        "premiumBonusAmount": 285,
        "premiumBonusTimes": 1
      },
      "shipping": {
        "code": 1,
        "name": "設定なし"
      },
      "genreCategory": {
        "id": 2137,
        "name": "その他コミック、アニメフィギュア",
        "depth": 4
      },
      "parentGenreCategories": [
        {
          "id": 2511,
          "depth": 1,
          "name": "ゲーム、おもちゃ"
        },
        {
          "id": 15160,
          "depth": 2,
          "name": "フィギュア"
        },
        {
          "id": 17085,
          "depth": 3,
          "name": "コミック、アニメーションフィギュア"
        }
      ],
      "brand": {
        "id": 59518,
        "name": "KADOKAWA"
      },
      "parentBrands": [
        {
          "id": 1,
          "name": "ブランド"
        }
      ],
      "janCode": "4935228288724",
      "payment": "16 16384 4096 1 2 4",
      "releaseDate": null,
      "seller": {
        "sellerId": "amiami",
        "name": "あみあみ Yahoo!店",
        "url": "https://store.shopping.yahoo.co.jp/amiami/",
        "isBestSeller": false,
        "review": {
          "rate": 4.6,
          "count": 129393
        },
        "imageId": "amiami_1"
      },
      "delivery": {
        "deadLine": null,
        "day": null,
        "area": ""
      }
    },
    :
```