Golang爬取Haremaltin站点ATA_ESKI金价字段问题及解决方案
问题描述
编写Go代码爬取黄金报价站点HTML表格中指定单元格的数值,目标站点地址为https://www.haremaltin.com/altin-fiyatlari,在浏览器开发者工具元素检查面板搜索satis__ATA_ESKI即可定位到目标值(旧阿塔金币售价)。作为Go初学者尝试多种写法均无法成功获取该值,已知信息如下:
- 站点未提供公开API
- 最初尝试静态爬取时需要添加等待逻辑等待页面加载完成,若返回结果为
-则代表页面尚未加载完成
初始尝试代码
package main import ( "fmt" "log" "net/http" "github.com/PuerkitoBio/goquery" ) func main() { url := "https://www.haremaltin.com/altin-fiyatlari" resp, err := http.Get(url) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != 200 { log.Fatalf("failed to fetch data: %d %s", resp.StatusCode, resp.Status) } doc, err := goquery.NewDocumentFromReader(resp.Body) if err != nil { log.Fatal(err) } doc.Find("tr__ATA_ESKI tr").Each(func(j int, tr *goquery.Selection) { data := []string{} tr.Find("td").Each(func(ix int, td *goquery.Selection) { e := td.Text() data = append(data, e) fmt.Println(data) }) }) }
解决方案
静态爬取失败的核心原因:页面价格数据并非内嵌在初始返回的静态HTML中,而是页面加载完成后通过AJAX异步拉取再渲染到DOM节点上,直接请求静态页无法拿到渲染后的数值,也不需要模拟浏览器等待加载,直接请求站点内部使用的异步数据接口即可拿到结构化JSON数据,解析效率更高。
核心提取逻辑为遍历接口返回的Map结构,匹配ATA_ESKI键下的satis(售价)字段即可,核心代码片段如下:
for _, v := range data { // 遍历Map提取值 m, ok := v.(map[string]interface{}) // 类型断言以支持遍历 if !ok { fmt.Printf("类型错误,实际类型: %T", v) } for k, l := range m { if k == "ATA_ESKI"{ // 目标数据存储在该键对应的Map中 a, ok := l.(map[string]interface{}) // 二次类型断言 if !ok { fmt.Printf("类型错误,实际类型: %T", v) } for b,c := range a{ if b == "satis"{ // 提取目标售价字段 fmt.Println("当前价格为", c) } } } } }
完整可运行代码
package main import ( "encoding/json" "fmt" "io/ioutil" "net/http" "strings" ) func main() { fetchData() } func fetchData() (map[string]interface{}, error) { body := strings.NewReader("dil_kodu=tr") req, err := http.NewRequest("POST", "https://www.haremaltin.com/dashboard/ajax/doviz", body) if err != nil { return nil, err } // 必须携带该请求头模拟AJAX请求,否则接口会返回异常 req.Header.Set("X-Requested-With", "XMLHttpRequest") resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() jsonData, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var data map[string]interface{} err = json.Unmarshal(jsonData, &data) if err != nil { return nil, err } for _, v := range data { m, ok := v.(map[string]interface{}) if !ok { fmt.Printf("类型错误,实际类型: %T", v) } for k, l := range m { if k == "ATA_ESKI" { a, ok := l.(map[string]interface{}) if !ok { fmt.Printf("类型错误,实际类型: %T", v) } for b, c := range a { if b == "satis" { fmt.Println("Price", c) } } } } } return data, nil }
注:已修正原代码中
fecthData的拼写错误为标准fetchData,如果有更简洁的实现方式欢迎交流。
内容的提问来源于stack exchange,提问作者eyup.tatar
相关产品推荐
相关产品推荐

