使用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

1 回复

更多关于使用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)是最可靠和高效的,因为:

  1. 不需要处理JavaScript渲染
  2. 响应是结构化的JSON数据
  3. 请求速度更快,资源消耗更少

你需要从ipstack.com注册获取免费的API密钥(每月有1万次请求额度)。

回到顶部