流程:调用 API → 解析 JSON → 处理错误 → 使用返回代理访问目标站。
[!NOTE]提示
- 下述示例均采用 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。
依赖安装
go get github.com/go-resty/resty/v2
go get github.com/valyala/fasthttp@latest
go get github.com/valyala/fasthttp/fasthttpproxy@latest
go get github.com/gocolly/colly/v2
go get github.com/PuerkitoBio/goquery
go get github.com/chromedp/chromedp
go get github.com/go-rod/rod
go get github.com/imroc/req/v3
入门示例(net/http)
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func fetchProxy(api string) (string, int, error) {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get(api)
if err != nil {
return "", 0, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return "", 0, fmt.Errorf("API错误: %d", resp.StatusCode)
}
b, _ := ioutil.ReadAll(resp.Body)
var arr []ProxyItem
if err := json.Unmarshal(b, &arr); err != nil {
return "", 0, err
}
if len(arr) == 0 {
return "", 0, fmt.Errorf("API 返回为空")
}
return arr[0].IP, arr[0].Port, nil
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
ip, port, err := fetchProxy(apiUrl)
if err != nil {
panic(err)
}
proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%d", ip, port))
tr := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
client := &http.Client{Transport: tr, Timeout: 10 * time.Second}
r, err := client.Get("https://httpbin.org/ip")
if err != nil {
panic(err)
}
defer r.Body.Close()
body, _ := ioutil.ReadAll(r.Body)
fmt.Println(string(body))
}
net/http(加强版,带错误映射)
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func fetchFirstProxy(ctx context.Context, api string) (string, int, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, api, nil)
if err != nil {
return "", 0, err
}
req.Header.Set("User-Agent", "Go-ApiProxy-Demo/1.0")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", 0, fmt.Errorf("API 请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case 400:
return "", 0, errors.New("API错误 400: 参数错误")
case 403:
return "", 0, errors.New("API错误 403: 主机IP不在白名单")
case 429:
return "", 0, errors.New("API错误 429: 提取频率过快")
default:
return "", 0, fmt.Errorf("API错误: %d", resp.StatusCode)
}
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", 0, err
}
var arr []ProxyItem
if err := json.Unmarshal(body, &arr); err != nil {
return "", 0, err
}
if len(arr) == 0 || arr[0].IP == "" || arr[0].Port == 0 {
return "", 0, errors.New("API 返回为空或缺少 ip/port")
}
return arr[0].IP, arr[0].Port, nil
}
func visitTargetViaProxy(ctx context.Context, ip string, port int) error {
proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%d", ip, port))
tr := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
client := &http.Client{Transport: tr, Timeout: 10 * time.Second}
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpbin.org/ip", nil)
req.Header.Set("User-Agent", "Mozilla/5.0")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("访问目标站失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case 403:
return errors.New("访问失败 403: 主机IP不在白名单或目标站拒绝")
case 408:
return errors.New("访问失败 408: 请求超时,检查带宽/目标站速度")
case 429:
return errors.New("访问失败 429: 访问频率过快,降低并发/增加间隔")
case 504:
return errors.New("访问失败 504: 目标站暂时不可达,稍后重试或补充 headers")
default:
return fmt.Errorf("访问失败: %d", resp.StatusCode)
}
}
b, _ := io.ReadAll(resp.Body)
fmt.Println(string(b))
return nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
ip, port, err := fetchFirstProxy(ctx, apiUrl)
if err != nil {
panic(err)
}
if err := visitTargetViaProxy(ctx, ip, port); err != nil {
panic(err)
}
}
Resty(HTTP 客户端)
package main
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/go-resty/resty/v2"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
client := resty.New().SetTimeout(10 * time.Second)
resp, err := client.R().Get(apiUrl)
if err != nil {
panic(fmt.Errorf("API 请求失败: %w", err))
}
if resp.StatusCode() != 200 {
switch resp.StatusCode() {
case 400:
panic("API错误 400: 参数错误")
case 403:
panic("API错误 403: 主机IP不在白名单")
case 429:
panic("API错误 429: 提取频率过快")
default:
panic(fmt.Errorf("API错误: %d", resp.StatusCode()))
}
}
var arr []ProxyItem
if err := json.Unmarshal(resp.Body(), &arr); err != nil || len(arr) == 0 {
panic(errors.New("API 返回为空或解析失败"))
}
ip, port := arr[0].IP, arr[0].Port
// 设置代理访问目标站
proxied := resty.New().SetTimeout(10 * time.Second).SetProxy(fmt.Sprintf("http://%s:%d", ip, port))
r2, err := proxied.R().SetHeader("User-Agent", "Mozilla/5.0").Get("https://httpbin.org/ip")
if err != nil {
panic(fmt.Errorf("访问目标站失败: %w", err))
}
if r2.StatusCode() != 200 {
panic(fmt.Errorf("访问失败: %d", r2.StatusCode()))
}
fmt.Println(string(r2.Body()))
}
fasthttp + fasthttpproxy(高性能客户端)
package main
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpproxy"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
// 先用 fasthttp 获取代理
var arr []ProxyItem
{
req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(resp)
req.SetRequestURI(apiUrl)
req.Header.SetMethod(fasthttp.MethodGet)
if err := fasthttp.DoTimeout(req, resp, 10*time.Second); err != nil {
panic(fmt.Errorf("API 请求失败: %w", err))
}
if resp.StatusCode() != 200 {
panic(fmt.Errorf("API错误: %d", resp.StatusCode()))
}
if err := json.Unmarshal(resp.Body(), &arr); err != nil || len(arr) == 0 {
panic(errors.New("API 返回为空或解析失败"))
}
}
ip, port := arr[0].IP, arr[0].Port
// 使用代理访问目标站
client := &fasthttp.Client{
Dial: fasthttpproxy.FasthttpHTTPDialer(fmt.Sprintf("%s:%d", ip, port)),
}
req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(resp)
req.SetRequestURI("https://httpbin.org/ip")
req.Header.SetMethod(fasthttp.MethodGet)
req.Header.Set("User-Agent", "Mozilla/5.0")
if err := client.DoTimeout(req, resp, 10*time.Second); err != nil {
panic(fmt.Errorf("访问目标站失败: %w", err))
}
if resp.StatusCode() != 200 {
panic(fmt.Errorf("访问失败: %d", resp.StatusCode()))
}
fmt.Println(string(resp.Body()))
}
Colly(对标 Java WebMagic/crawler4j)
package main
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/gocolly/colly/v2"
"github.com/gocolly/colly/v2/proxy"
"github.com/imroc/req/v3"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func fetchFirstProxy(api string) (string, int, error) {
r := req.C().SetTimeout(10 * time.Second)
resp, err := r.R().Get(api)
if err != nil {
return "", 0, err
}
if resp.StatusCode != 200 {
return "", 0, fmt.Errorf("API错误: %d", resp.StatusCode)
}
var arr []ProxyItem
if err := json.Unmarshal(resp.Bytes(), &arr); err != nil || len(arr) == 0 {
return "", 0, fmt.Errorf("API 返回为空或解析失败")
}
return arr[0].IP, arr[0].Port, nil
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
ip, port, err := fetchFirstProxy(apiUrl)
if err != nil {
log.Fatal(err)
}
c := colly.NewCollector(colly.UserAgent("Mozilla/5.0"))
rp, _ := proxy.RoundRobinProxySwitcher(fmt.Sprintf("http://%s:%d", ip, port))
c.SetProxyFunc(rp)
c.OnResponse(func(r *colly.Response) { fmt.Println(string(r.Body)) })
c.OnError(func(r *colly.Response, e error) { log.Printf("访问失败: %d %v", r.StatusCode, e) })
if err := c.Visit("https://httpbin.org/ip"); err != nil {
log.Fatal(err)
}
}
goquery(对标 Java Jsoup)
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"time"
"github.com/PuerkitoBio/goquery"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func fetchProxy(api string) (string, int, error) {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get(api)
if err != nil {
return "", 0, err
}
defer resp.Body.Close()
var arr []ProxyItem
if err := json.NewDecoder(resp.Body).Decode(&arr); err != nil || len(arr) == 0 {
return "", 0, fmt.Errorf("API 返回为空或解析失败")
}
return arr[0].IP, arr[0].Port, nil
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
ip, port, err := fetchProxy(apiUrl)
if err != nil { log.Fatal(err) }
proxyURL, _ := url.Parse(fmt.Sprintf("http://%s:%d", ip, port))
tr := &http.Transport{Proxy: http.ProxyURL(proxyURL)}
client := &http.Client{Transport: tr, Timeout: 10 * time.Second}
req, _ := http.NewRequest(http.MethodGet, "https://httpbin.org/ip", nil)
req.Header.Set("User-Agent", "Mozilla/5.0")
resp, err := client.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil { log.Fatal(err) }
fmt.Println(doc.Text())
}
chromedp(对标 Java Selenium)
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/chromedp/chromedp"
"net/http"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func fetchProxy(api string) (string, int, error) {
c := &http.Client{Timeout: 10 * time.Second}
r, err := c.Get(api)
if err != nil { return "", 0, err }
defer r.Body.Close()
var arr []ProxyItem
if err := json.NewDecoder(r.Body).Decode(&arr); err != nil || len(arr) == 0 {
return "", 0, fmt.Errorf("API 返回为空或解析失败")
}
return arr[0].IP, arr[0].Port, nil
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
ip, port, err := fetchProxy(apiUrl)
if err != nil { log.Fatal(err) }
allocOpts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", true),
chromedp.Flag("proxy-server", fmt.Sprintf("http://%s:%d", ip, port)),
)
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), allocOpts...)
defer cancel()
ctx, cancel2 := chromedp.NewContext(allocCtx)
defer cancel2()
var html string
if err := chromedp.Run(ctx,
chromedp.Navigate("https://httpbin.org/ip"),
chromedp.OuterHTML("html", &html),
); err != nil {
log.Fatal(err)
}
fmt.Println(html)
}
rod(浏览器自动化)
package main
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
"net/http"
)
type ProxyItem struct {
IP string `json:"ip"`
Port int `json:"port"`
}
func fetchProxy(api string) (string, int, error) {
c := &http.Client{Timeout: 10 * time.Second}
r, err := c.Get(api)
if err != nil { return "", 0, err }
defer r.Body.Close()
var arr []ProxyItem
if err := json.NewDecoder(r.Body).Decode(&arr); err != nil || len(arr) == 0 {
return "", 0, fmt.Errorf("API 返回为空或解析失败")
}
return arr[0].IP, arr[0].Port, nil
}
func main() {
apiUrl := "http://ip.16yun.cn:817/myip/pl/<ORDER_ID>/?s=<ORDER_SIGN>&u=<USER>&format=json"
ip, port, err := fetchProxy(apiUrl)
if err != nil { log.Fatal(err) }
url := launcher.New().Proxy(fmt.Sprintf("http://%s:%d", ip, port)).MustLaunch()
browser := rod.New().ControlURL(url).MustConnect()
defer browser.MustClose()
page := browser.MustPage("https://httpbin.org/ip")
html := page.MustHTML()
fmt.Println(html)
}