使用Golang通过HTML选择器实现网页爬虫的方法
使用Golang通过HTML选择器实现网页爬虫的方法
尝试使用 html/css 选择器 div.ipchecker 从网站 https://ipstack.com/ 获取一些数据,该选择器显示了客户详细信息的对象。
以下是我的代码:
package main
import (
"fmt"
"net/http"
"github.com/PuerkitoBio/goquery"
)
const (
site1 = "https://ipstack.com/"
site2 = "https://geoip.nekudo.com"
)
var count int
func get(s string) (count int, body string) {
resp, err := http.Get(s)
if err != nil {
return 0, err.Error()
}
defer resp.Body.Close()
count++
doc, _ := goquery.NewDocumentFromReader(resp.Body)
return count, doc.Find("div.ipchecker").Text()
}
func main() {
_, b := get(site1)
fmt.Println(b)
}
我使用了 goquery,但当我运行程序时,并没有获取到完整的对象,而是只得到了右上角的按钮。我怀疑在尝试通过选择器抓取数据时,有某种方法可以隐藏数据。不确定问题出在哪里,也许有更好的方法来抓取这个对象?
更多关于使用Golang通过HTML选择器实现网页爬虫的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于使用Golang通过HTML选择器实现网页爬虫的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
从你的代码来看,问题在于目标网站使用了JavaScript动态加载数据,而goquery只能解析静态HTML。div.ipchecker元素在初始页面加载时只包含按钮,详细数据是通过后续的JavaScript请求获取的。
以下是两种解决方案:
方案1:直接调用网站API(推荐)
通过浏览器开发者工具分析网络请求,发现该网站通过API获取数据:
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type IPInfo struct {
IP string `json:"ip"`
Type string `json:"type"`
ContinentCode string `json:"continent_code"`
ContinentName string `json:"continent_name"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
RegionCode string `json:"region_code"`
RegionName string `json:"region_name"`
City string `json:"city"`
Zip string `json:"zip"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Location struct {
GeonameID int `json:"geoname_id"`
Capital string `json:"capital"`
Languages []struct {
Code string `json:"code"`
Name string `json:"name"`
Native string `json:"native"`
} `json:"languages"`
CountryFlag string `json:"country_flag"`
CountryFlagEmoji string `json:"country_flag_emoji"`
CountryFlagEmojiUnicode string `json:"country_flag_emoji_unicode"`
CallingCode string `json:"calling_code"`
IsEU bool `json:"is_eu"`
} `json:"location"`
}
func main() {
// 使用ipstack的API(需要免费API密钥)
apiKey := "your_api_key_here" // 从ipstack.com获取免费API密钥
url := fmt.Sprintf("http://api.ipstack.com/check?access_key=%s", apiKey)
resp, err := http.Get(url)
if err != nil {
fmt.Printf("请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应失败: %v\n", err)
return
}
var info IPInfo
if err := json.Unmarshal(body, &info); err != nil {
fmt.Printf("解析JSON失败: %v\n", err)
return
}
fmt.Printf("IP地址: %s\n", info.IP)
fmt.Printf("国家: %s\n", info.CountryName)
fmt.Printf("城市: %s\n", info.City)
fmt.Printf("经纬度: %.4f, %.4f\n", info.Latitude, info.Longitude)
}
方案2:使用无头浏览器处理JavaScript
如果需要模拟浏览器执行JavaScript,可以使用chromedp:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
defer cancel()
var htmlContent string
err := chromedp.Run(ctx,
chromedp.Navigate("https://ipstack.com/"),
chromedp.WaitVisible(`div.ipchecker`, chromedp.ByQuery),
chromedp.Sleep(3*time.Second), // 等待JavaScript加载数据
chromedp.OuterHTML(`div.ipchecker`, &htmlContent, chromedp.ByQuery),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(htmlContent)
}
方案3:使用Selenium替代方案
package main
import (
"fmt"
"time"
"github.com/tebeka/selenium"
)
func main() {
const (
seleniumPath = "vendor/selenium-server-standalone-3.141.59.jar"
port = 8080
)
opts := []selenium.ServiceOption{}
service, err := selenium.NewSeleniumService(seleniumPath, port, opts...)
if err != nil {
panic(err)
}
defer service.Stop()
caps := selenium.Capabilities{"browserName": "chrome"}
wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
if err != nil {
panic(err)
}
defer wd.Quit()
wd.Get("https://ipstack.com/")
time.Sleep(5 * time.Second)
elem, err := wd.FindElement(selenium.ByCSSSelector, "div.ipchecker")
if err != nil {
panic(err)
}
text, err := elem.Text()
if err != nil {
panic(err)
}
fmt.Println(text)
}
最简单的测试方案
如果只是想快速验证选择器,可以使用网站提供的免费API端点:
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// 使用免费的地理IP服务
resp, err := http.Get("https://ipapi.co/json/")
if err != nil {
fmt.Printf("请求失败: %v\n", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取失败: %v\n", err)
return
}
fmt.Println(string(body))
}
第一个方案(直接调用API)是最可靠和高效的,因为:
- 不需要处理JavaScript渲染
- 响应是结构化的JSON数据
- 请求速度更快,资源消耗更少
你需要从ipstack.com注册获取免费的API密钥(每月有1万次请求额度)。

