如何在服务器端用Golang从Google Maps短链接获取坐标?
解决Google Maps短链接转坐标的问题
你遇到的错误核心原因是:短链接的后缀(比如y2HccUDkhVjhWJWb8)并不是Google Geocoding API认可的PlaceID,直接把它传给ReverseGeocode肯定会触发参数无效的错误。
下面提供两种可行的服务器端Golang实现方案:
方案一:解析短链接跳转目标,直接提取坐标
Google Maps短链接(maps.app.goo.gl/xxx)本质是重定向链接,会跳转到包含坐标或PlaceID的完整Google Maps URL。我们可以先通过HTTP请求获取跳转后的真实URL,再从中提取坐标信息。
代码示例
package main import ( "fmt" "log" "net/http" "net/url" "strings" ) // 获取短链接跳转后的最终URL func resolveShortLink(shortURL string) (string, error) { client := &http.Client{ // 禁用自动跟随重定向,手动处理以便获取Location头 CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } req, err := http.NewRequest(http.MethodHead, shortURL, nil) if err != nil { return "", err } resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() // 处理3xx重定向状态码 if resp.StatusCode >= 300 && resp.StatusCode < 400 { return resp.Header.Get("Location"), nil } return "", fmt.Errorf("no redirect found for %s", shortURL) } // 从Google Maps URL中提取坐标 func extractCoordinatesFromURL(mapsURL string) (float64, float64, error) { parsedURL, err := url.Parse(mapsURL) if err != nil { return 0, 0, err } // 情况1:URL包含lat和lng参数 latStr := parsedURL.Query().Get("lat") lngStr := parsedURL.Query().Get("lng") if latStr != "" && lngStr != "" { var lat, lng float64 fmt.Sscanf(latStr, "%f", &lat) fmt.Sscanf(lngStr, "%f", &lng) return lat, lng, nil } // 情况2:URL路径包含坐标(比如/place/.../@lat,lng,...) pathParts := strings.Split(parsedURL.Path, "/") for _, part := range pathParts { if strings.HasPrefix(part, "@") { coordPart := strings.TrimPrefix(part, "@") coordSegments := strings.Split(coordPart, ",") if len(coordSegments) >= 2 { var lat, lng float64 fmt.Sscanf(coordSegments[0], "%f", &lat) fmt.Sscanf(coordSegments[1], "%f", &lng) return lat, lng, nil } } } return 0, 0, fmt.Errorf("no coordinates found in URL %s", mapsURL) } func main() { shortLinks := []string{ "https://maps.app.goo.gl/y2HccUDkhVjhWJWb8", "https://maps.app.goo.gl/6LRg48ifPQGau8bV7", "https://maps.app.goo.gl/rB4hNMWWczsQ58FP9", } for _, link := range shortLinks { finalURL, err := resolveShortLink(link) if err != nil { log.Printf("Failed to resolve %s: %v", link, err) continue } lat, lng, err := extractCoordinatesFromURL(finalURL) if err != nil { log.Printf("Failed to extract coordinates from %s: %v", finalURL, err) continue } fmt.Printf("Short link %s -> Coordinates: %.6f, %.6f\n", link, lat, lng) } }
方案二:解析跳转URL获取PlaceID,再调用Geocoding API
如果需要更详细的地址信息,而非仅坐标,可以从跳转后的URL中提取PlaceID,再通过Google Geocoding API的ReverseGeocode或Geocode接口获取完整数据。
代码示例(基于方案一扩展)
package main import ( "context" "fmt" "log" "net/http" "net/url" "strings" "github.com/googlemaps/google-maps-services-go/maps" ) // 获取短链接跳转后的最终URL(复用方案一中的函数) func resolveShortLink(shortURL string) (string, error) { client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } req, err := http.NewRequest(http.MethodHead, shortURL, nil) if err != nil { return "", err } resp, err := client.Do(req) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode >= 300 && resp.StatusCode < 400 { return resp.Header.Get("Location"), nil } return "", fmt.Errorf("no redirect found for %s", shortURL) } // 从URL中提取PlaceID func extractPlaceIDFromURL(mapsURL string) (string, error) { parsedURL, err := url.Parse(mapsURL) if err != nil { return "", err } // 情况1:URL包含place_id参数 placeID := parsedURL.Query().Get("place_id") if placeID != "" { return placeID, nil } // 情况2:URL路径包含place_id(比如/place/PlaceName/place_id:xxx/...) path := parsedURL.Path if strings.Contains(path, "place_id:") { startIdx := strings.Index(path, "place_id:") + len("place_id:") endIdx := strings.Index(path[startIdx:], "/") if endIdx != -1 { return path[startIdx : startIdx+endIdx], nil } return path[startIdx:], nil } return "", fmt.Errorf("no place_id found in URL %s", mapsURL) } func main() { myAPIKey := "YOUR_GOOGLE_MAPS_API_KEY" shortLinks := []string{ "https://maps.app.goo.gl/y2HccUDkhVjhWJWb8", "https://maps.app.goo.gl/6LRg48ifPQGau8bV7", "https://maps.app.goo.gl/rB4hNMWWczsQ58FP9", } // 初始化Geocoding客户端 c, err := maps.NewClient(maps.WithAPIKey(myAPIKey)) if err != nil { log.Fatalf("Failed to create maps client: %v", err) } for _, link := range shortLinks { finalURL, err := resolveShortLink(link) if err != nil { log.Printf("Failed to resolve %s: %v", link, err) continue } placeID, err := extractPlaceIDFromURL(finalURL) if err != nil { log.Printf("Failed to extract place_id from %s: %v", finalURL, err) continue } // 调用ReverseGeocode获取详细信息 r := &maps.GeocodingRequest{ PlaceID: placeID, } resp, err := c.ReverseGeocode(context.Background(), r) if err != nil { log.Printf("Failed to reverse geocode place_id %s: %v", placeID, err) continue } if len(resp) > 0 { location := resp[0].Geometry.Location fmt.Printf("Short link %s -> Coordinates: %.6f, %.6f\n", link, location.Lat, location.Lng) fmt.Printf("Address: %s\n", resp[0].FormattedAddress) } } }
注意事项
- 确保你的Google Maps API Key已启用Geocoding API权限,否则会触发权限错误。
- 处理短链接时,若存在多次重定向,可修改
resolveShortLink函数实现自动跟随所有重定向直到最终URL。 - Google Maps的URL格式可能会有变化,需要定期维护解析逻辑。
内容的提问来源于stack exchange,提问作者Emanuel Grech
相关产品推荐
相关产品推荐

