TL;DR
GoでHTTPリクエストを送信送るnet/httpライブラリのhttp.Client
はデフォルトだと自動リダイレクトする仕様ですが、http.Client.CheckRedirect
をオーバーライドすればリダイレクトする前の結果を返すことができます。
外部要因でリダイレクトがかかる際にシステムでエラーが起きる
Amazonの商品の分類によって処理を変えるようなシステムを実装していました。
Amazonの商品は「靴」などの分類を「BrowseNodes」という単位で管理しており、こちらはAPIのドキュメントにも書かれています。
メンズスニーカーの場合、「https://www.amazon.co.jp/b?node=2221112051」というURLの「2221112051」がメンズスニーカーのBrowseNodeのIDに相当します。
このBrowseNodeのIDをもとにしたシステムをGoにて作成していたところ、このIDがときどき変更されて、リダイレクトされていることがわかりました。事前にシステムにIDを登録しておくと、そのIDで上記のAPIを利用した際にエラーになってしまいます。
このリダイレクトをシステムで検知することができないかを考えてみたので、今回はその方法を紹介したいと思います。
リダイレクト時にリダイレクト先を表示させるコード
こちらが実際に実装したコードです。
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
targetUrl := os.Args[1]
c := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := c.Get(targetUrl)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode == 301 {
fmt.Println(resp.Header["Location"][0])
}
}
$ go run main.go "https://www.amazon.co.jp/b?node=15326491"
/b?node=2231280051
http.Client.CheckRedirectをオーバーライドしてリダイレクト前のレスポンスを返す
HTTPのリクエストを送信するnet/httpライブラリのhttp.Client
はデフォルトだと自動リダイレクトする仕様ですが、http.Client.CheckRedirect
をオーバーライドして、エラーを返すようにすればClientのGetメソッドは、bodyが閉る前のレスポンスとCheckRedirectの結果を返してくれます。
今回のケースではErrUseLastResponse
を返していて、これが返されると次のリクエストの前の最新の応答を返してくれます。
http.GET
の結果がリダイレクト前のものになる
これによってhttp.GET
で返される結果が、リダイレクトされている場合にはリダイレクトされる前の結果が返るようになったので、ステータスコードによる分岐を追加することで、リダイレクト先のパスも取得することができるようになりました。
留意点
今回のコードに関しては、検討した結果として実際のプロダクションコード等では使っておりません。
スクレイピング等に使う用途を想定しておりませんので、ご注意ください。
参考