Go API代理开发示例
流程:调用 API → 解析 JSON → 处理错误 → 使用返回代理访问目标站。
提示
- 下述示例均采用 IP 白名单模式(无需用户名密码认证)。请将
apiUrl替换为您自己的 API 提取链接。 - 常见错误码:
400 参数错误、403 主机 IP 未在白名单、429 频率过快(参考@api-proxy.md)。 - 对标 Java 热门生态中的常见组件:
HttpClient/OkHttp→net/http/resty/fasthttp;Jsoup→goquery;Selenium→chromedp/rod;WebMagic/crawler4j→colly。
依赖安装
1go get github.com/go-resty/resty/v22go get github.com/valyala/fasthttp@latest3go get github.com/valyala/fasthttp/fasthttpproxy@latest4go get github.com/gocolly/colly/v25go get github.com/PuerkitoBio/goquery6go get github.com/chromedp/chromedp7go get github.com/go-rod/rod8go get github.com/imroc/req/v3
入门示例(net/http)
1package main23import (4"encoding/json"5"fmt"6"io/ioutil"7"net/http"8"net/url"9"time"10)1112type ProxyItem struct {13IP string `json:"ip"`14Port int `json:"port"`15}1617func fetchProxy(api string) (string, int, error) {18client := &http.Client{Timeout: 10 * time.Second}19resp, err := client.Get(api)20if err != nil {21return "", 0, err22}23defer resp.Body.Close()24if resp.StatusCode != 200 {25return "", 0, fmt.Errorf("API错误: %d", resp.StatusCode)26}27b, _ := ioutil.ReadAll(resp.Body)28var arr []ProxyItem29if err := json.Unmarshal(b, &arr); err != nil {30return "", 0, err31}32if len(arr) == 0 {33return "", 0, fmt.Errorf("API 返回为空")34}35return arr[0].IP, arr[0].Port, nil36}3738func main() {39apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"40ip, port, err := fetchProxy(apiUrl)41if err != nil {42panic(err)43}44proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%d", ip, port))45tr := &http.Transport{Proxy: http.ProxyURL(proxyURL)}46client := &http.Client{Transport: tr, Timeout: 10 * time.Second}47r, err := client.Get("https://httpbin.org/ip")48if err != nil {49panic(err)50}51defer r.Body.Close()52body, _ := ioutil.ReadAll(r.Body)53fmt.Println(string(body))54}
net/http(加强版,带错误映射)
1package main23import (4"context"5"encoding/json"6"errors"7"fmt"8"io"9"net/http"10"net/url"11"time"12)1314type ProxyItem struct {15IP string `json:"ip"`16Port int `json:"port"`17}1819func fetchFirstProxy(ctx context.Context, api string) (string, int, error) {20req, err := http.NewRequestWithContext(ctx, http.MethodGet, api, nil)21if err != nil {22return "", 0, err23}24req.Header.Set("User-Agent", "Go-ApiProxy-Demo/1.0")2526client := &http.Client{Timeout: 10 * time.Second}27resp, err := client.Do(req)28if err != nil {29return "", 0, fmt.Errorf("API 请求失败: %w", err)30}31defer resp.Body.Close()3233if resp.StatusCode != http.StatusOK {34switch resp.StatusCode {35case 400:36return "", 0, errors.New("API错误 400: 参数错误")37case 403:38return "", 0, errors.New("API错误 403: 主机IP不在白名单")39case 429:40return "", 0, errors.New("API错误 429: 提取频率过快")41default:42return "", 0, fmt.Errorf("API错误: %d", resp.StatusCode)43}44}4546body, err := io.ReadAll(resp.Body)47if err != nil {48return "", 0, err49}50var arr []ProxyItem51if err := json.Unmarshal(body, &arr); err != nil {52return "", 0, err53}54if len(arr) == 0 || arr[0].IP == "" || arr[0].Port == 0 {55return "", 0, errors.New("API 返回为空或缺少 ip/port")56}57return arr[0].IP, arr[0].Port, nil58}5960func visitTargetViaProxy(ctx context.Context, ip string, port int) error {61proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%d", ip, port))62tr := &http.Transport{Proxy: http.ProxyURL(proxyURL)}63client := &http.Client{Transport: tr, Timeout: 10 * time.Second}6465req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpbin.org/ip", nil)66req.Header.Set("User-Agent", "Mozilla/5.0")67resp, err := client.Do(req)68if err != nil {69return fmt.Errorf("访问目标站失败: %w", err)70}71defer resp.Body.Close()7273if resp.StatusCode != http.StatusOK {74switch resp.StatusCode {75case 403:76return errors.New("访问失败 403: 主机IP不在白名单或目标站拒绝")77case 408:78return errors.New("访问失败 408: 请求超时,检查带宽/目标站速度")79case 429:80return errors.New("访问失败 429: 访问频率过快,降低并发/增加间隔")81case 504:82return errors.New("访问失败 504: 目标站暂时不可达,稍后重试或补充 headers")83default:84return fmt.Errorf("访问失败: %d", resp.StatusCode)85}86}87b, _ := io.ReadAll(resp.Body)88fmt.Println(string(b))89return nil90}9192func main() {93ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)94defer cancel()9596apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"97ip, port, err := fetchFirstProxy(ctx, apiUrl)98if err != nil {99panic(err)100}101if err := visitTargetViaProxy(ctx, ip, port); err != nil {102panic(err)103}104}
Resty(HTTP 客户端)
1package main23import (4"encoding/json"5"errors"6"fmt"7"time"89"github.com/go-resty/resty/v2"10)1112type ProxyItem struct {13IP string `json:"ip"`14Port int `json:"port"`15}1617func main() {18apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"1920client := resty.New().SetTimeout(10 * time.Second)21resp, err := client.R().Get(apiUrl)22if err != nil {23panic(fmt.Errorf("API 请求失败: %w", err))24}25if resp.StatusCode() != 200 {26switch resp.StatusCode() {27case 400:28panic("API错误 400: 参数错误")29case 403:30panic("API错误 403: 主机IP不在白名单")31case 429:32panic("API错误 429: 提取频率过快")33default:34panic(fmt.Errorf("API错误: %d", resp.StatusCode()))35}36}3738var arr []ProxyItem39if err := json.Unmarshal(resp.Body(), &arr); err != nil || len(arr) == 0 {40panic(errors.New("API 返回为空或解析失败"))41}42ip, port := arr[0].IP, arr[0].Port4344// 设置代理访问目标站45proxied := resty.New().SetTimeout(10 * time.Second).SetProxy(fmt.Sprintf("http://%s:%d", ip, port))46r2, err := proxied.R().SetHeader("User-Agent", "Mozilla/5.0").Get("https://httpbin.org/ip")47if err != nil {48panic(fmt.Errorf("访问目标站失败: %w", err))49}50if r2.StatusCode() != 200 {51panic(fmt.Errorf("访问失败: %d", r2.StatusCode()))52}53fmt.Println(string(r2.Body()))54}
fasthttp + fasthttpproxy(高性能客户端)
1package main23import (4"encoding/json"5"errors"6"fmt"7"time"89"github.com/valyala/fasthttp"10"github.com/valyala/fasthttp/fasthttpproxy"11)1213type ProxyItem struct {14IP string `json:"ip"`15Port int `json:"port"`16}1718func main() {19apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"2021// 先用 fasthttp 获取代理22var arr []ProxyItem23{24req := fasthttp.AcquireRequest()25resp := fasthttp.AcquireResponse()26defer fasthttp.ReleaseRequest(req)27defer fasthttp.ReleaseResponse(resp)28req.SetRequestURI(apiUrl)29req.Header.SetMethod(fasthttp.MethodGet)3031if err := fasthttp.DoTimeout(req, resp, 10*time.Second); err != nil {32panic(fmt.Errorf("API 请求失败: %w", err))33}34if resp.StatusCode() != 200 {35panic(fmt.Errorf("API错误: %d", resp.StatusCode()))36}37if err := json.Unmarshal(resp.Body(), &arr); err != nil || len(arr) == 0 {38panic(errors.New("API 返回为空或解析失败"))39}40}4142ip, port := arr[0].IP, arr[0].Port4344// 使用代理访问目标站45client := &fasthttp.Client{46Dial: fasthttpproxy.FasthttpHTTPDialer(fmt.Sprintf("%s:%d", ip, port)),47}48req := fasthttp.AcquireRequest()49resp := fasthttp.AcquireResponse()50defer fasthttp.ReleaseRequest(req)51defer fasthttp.ReleaseResponse(resp)52req.SetRequestURI("https://httpbin.org/ip")53req.Header.SetMethod(fasthttp.MethodGet)54req.Header.Set("User-Agent", "Mozilla/5.0")55if err := client.DoTimeout(req, resp, 10*time.Second); err != nil {56panic(fmt.Errorf("访问目标站失败: %w", err))57}58if resp.StatusCode() != 200 {59panic(fmt.Errorf("访问失败: %d", resp.StatusCode()))60}61fmt.Println(string(resp.Body()))62}
Colly(对标 Java WebMagic/crawler4j)
1package main23import (4"encoding/json"5"fmt"6"log"7"time"89"github.com/gocolly/colly/v2"10"github.com/gocolly/colly/v2/proxy"11"github.com/imroc/req/v3"12)1314type ProxyItem struct {15IP string `json:"ip"`16Port int `json:"port"`17}1819func fetchFirstProxy(api string) (string, int, error) {20r := req.C().SetTimeout(10 * time.Second)21resp, err := r.R().Get(api)22if err != nil {23return "", 0, err24}25if resp.StatusCode != 200 {26return "", 0, fmt.Errorf("API错误: %d", resp.StatusCode)27}28var arr []ProxyItem29if err := json.Unmarshal(resp.Bytes(), &arr); err != nil || len(arr) == 0 {30return "", 0, fmt.Errorf("API 返回为空或解析失败")31}32return arr[0].IP, arr[0].Port, nil33}3435func main() {36apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"37ip, port, err := fetchFirstProxy(apiUrl)38if err != nil {39log.Fatal(err)40}4142c := colly.NewCollector(colly.UserAgent("Mozilla/5.0"))43rp, _ := proxy.RoundRobinProxySwitcher(fmt.Sprintf("http://%s:%d", ip, port))44c.SetProxyFunc(rp)4546c.OnResponse(func(r *colly.Response) { fmt.Println(string(r.Body)) })47c.OnError(func(r *colly.Response, e error) { log.Printf("访问失败: %d %v", r.StatusCode, e) })4849if err := c.Visit("https://httpbin.org/ip"); err != nil {50log.Fatal(err)51}52}
goquery(对标 Java Jsoup)
1package main23import (4"encoding/json"5"fmt"6"log"7"net/http"8"net/url"9"time"1011"github.com/PuerkitoBio/goquery"12)1314type ProxyItem struct {15IP string `json:"ip"`16Port int `json:"port"`17}1819func fetchProxy(api string) (string, int, error) {20client := &http.Client{Timeout: 10 * time.Second}21resp, err := client.Get(api)22if err != nil {23return "", 0, err24}25defer resp.Body.Close()26var arr []ProxyItem27if err := json.NewDecoder(resp.Body).Decode(&arr); err != nil || len(arr) == 0 {28return "", 0, fmt.Errorf("API 返回为空或解析失败")29}30return arr[0].IP, arr[0].Port, nil31}3233func main() {34apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"35ip, port, err := fetchProxy(apiUrl)36if err != nil { log.Fatal(err) }3738proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%d", ip, port))39tr := &http.Transport{Proxy: http.ProxyURL(proxyURL)}40client := &http.Client{Transport: tr, Timeout: 10 * time.Second}4142req, _ := http.NewRequest(http.MethodGet, "https://httpbin.org/ip", nil)43req.Header.Set("User-Agent", "Mozilla/5.0")44resp, err := client.Do(req)45if err != nil { log.Fatal(err) }46defer resp.Body.Close()4748doc, err := goquery.NewDocumentFromReader(resp.Body)49if err != nil { log.Fatal(err) }50fmt.Println(doc.Text())51}
chromedp(对标 Java Selenium)
1package main23import (4"context"5"encoding/json"6"fmt"7"log"8"time"910"github.com/chromedp/chromedp"11"net/http"12)1314type ProxyItem struct {15IP string `json:"ip"`16Port int `json:"port"`17}1819func fetchProxy(api string) (string, int, error) {20c := &http.Client{Timeout: 10 * time.Second}21r, err := c.Get(api)22if err != nil { return "", 0, err }23defer r.Body.Close()24var arr []ProxyItem25if err := json.NewDecoder(r.Body).Decode(&arr); err != nil || len(arr) == 0 {26return "", 0, fmt.Errorf("API 返回为空或解析失败")27}28return arr[0].IP, arr[0].Port, nil29}3031func main() {32apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"33ip, port, err := fetchProxy(apiUrl)34if err != nil { log.Fatal(err) }3536allocOpts := append(chromedp.DefaultExecAllocatorOptions[:],37chromedp.Flag("headless", true),38chromedp.Flag("proxy-server", fmt.Sprintf("http://%s:%d", ip, port)),39)40allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), allocOpts...)41defer cancel()42ctx, cancel2 := chromedp.NewContext(allocCtx)43defer cancel2()4445var html string46if err := chromedp.Run(ctx,47chromedp.Navigate("https://httpbin.org/ip"),48chromedp.OuterHTML("html", &html),49); err != nil {50log.Fatal(err)51}52fmt.Println(html)53}
rod(浏览器自动化)
1package main23import (4"encoding/json"5"fmt"6"log"7"time"89"github.com/go-rod/rod"10"github.com/go-rod/rod/lib/launcher"11"net/http"12)1314type ProxyItem struct {15IP string `json:"ip"`16Port int `json:"port"`17}1819func fetchProxy(api string) (string, int, error) {20c := &http.Client{Timeout: 10 * time.Second}21r, err := c.Get(api)22if err != nil { return "", 0, err }23defer r.Body.Close()24var arr []ProxyItem25if err := json.NewDecoder(r.Body).Decode(&arr); err != nil || len(arr) == 0 {26return "", 0, fmt.Errorf("API 返回为空或解析失败")27}28return arr[0].IP, arr[0].Port, nil29}3031func main() {32apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"33ip, port, err := fetchProxy(apiUrl)34if err != nil { log.Fatal(err) }3536url := launcher.New().Proxy(fmt.Sprintf("http://%s:%d", ip, port)).MustLaunch()37browser := rod.New().ControlURL(url).MustConnect()38defer browser.MustClose()3940page := browser.MustPage("https://httpbin.org/ip")41html := page.MustHTML()42fmt.Println(html)43}