Как преобразовать таблицу HTML в массив с помощью golang

У меня возникла проблема при попытке преобразовать таблицу HTML в массив Golang. Я пытался добиться этого, используя x/net/html и goquery, но безуспешно для них обоих.

Допустим, у нас есть эта таблица HTML:

<html>
  <body>
    <table>
      <tr>
        <td>Row 1, Content 1<td>
        <td>Row 1, Content 2<td>
        <td>Row 1, Content 3<td>
        <td>Row 1, Content 4<td>
      </tr>
      <tr>
        <td>Row 2, Content 1<td>
        <td>Row 2, Content 2<td>
        <td>Row 2, Content 3<td>
        <td>Row 2, Content 4<td>
      </tr>
    </table>
  </body>
</html>

И я хотел бы закончить с этим массивом:

------------------------------------
|Row 1, Content 1| Row 1, Content 2|
------------------------------------
|Row 2, Content 1| Row 2, Content 2|
------------------------------------

Как вы видите, я просто игнорирую содержание 3 и 4.

Мой код извлечения:

func extractValue(content []byte) {
  doc, _ := goquery.NewDocumentFromReader(bytes.NewReader(content))

  doc.Find("table tr td").Each(func(i int, td *goquery.Selection) {
    // ...
  })
}

Я попытался добавить номер контроллера, который будет отвечать за игнорирование <td> что я не хочу конвертировать и звоню

td.NextAll()

но без удачи. Ребята, вы знаете, что мне делать, чтобы это сделать?

Благодарю.

3 ответа

Решение

Вы можете уйти с пакетом golang.org/x/net/html только.

var body = strings.NewReader(`                                                                                                                            
        <html>                                                                                                                                            
        <body>                                                                                                                                            
        <table>                                                                                                                                           
        <tr>                                                                                                                                              
        <td>Row 1, Content 1<td>                                                                                                                          
        <td>Row 1, Content 2<td>                                                                                                                          
        <td>Row 1, Content 3<td>                                                                                                                          
        <td>Row 1, Content 4<td>                                                                                                                          
        </tr>                                                                                                                                             
        <tr>                                                                                                                                              
        <td>Row 2, Content 1<td>                                                                                                        
        <td>Row 2, Content 2<td>                                                                                                                          
        <td>Row 2, Content 3<td>                                                                                                                          
        <td>Row 2, Content 4<td>                                                                                                                          
        </tr>  
        </table>                                                                                                                                          
        </body>                                                                                                                                           
        </html>`)          

func main() {
    z := html.NewTokenizer(body)
    content := []string{}

    // While have not hit the </html> tag
    for z.Token().Data != "html" {
        tt := z.Next()
        if tt == html.StartTagToken {
            t := z.Token()
            if t.Data == "td" {
                inner := z.Next()
                if inner == html.TextToken {
                    text := (string)(z.Text())
                    t := strings.TrimSpace(text)
                    content = append(content, t)
                }
            }
        }
    }
    // Print to check the slice's content
    fmt.Println(content)
}

Этот код написан только для этого типичного HTML-шаблона, но рефакторинг, чтобы сделать его более общим, не составит труда.

Если вам нужен более структурированный способ извлечения данных из HTML-таблиц, https://github.com/nfx/go-htmltable поддерживает row/colspans.

      type AM4 struct {
    Model             string `header:"Model"`
    ReleaseDate       string `header:"Release date"`
    PCIeSupport       string `header:"PCIesupport[a]"`
    MultiGpuCrossFire bool   `header:"Multi-GPU CrossFire"`
    MultiGpuSLI       bool   `header:"Multi-GPU SLI"`
    USBSupport        string `header:"USBsupport[b]"`
    SATAPorts         int    `header:"Storage features SATAports"`
    RAID              string `header:"Storage features RAID"`
    AMDStoreMI        bool   `header:"Storage features AMD StoreMI"`
    Overclocking      string `header:"Processoroverclocking"`
    TDP               string `header:"TDP"`
    SupportExcavator  string `header:"CPU support[14] Excavator"`
    SupportZen        string `header:"CPU support[14] Zen"`
    SupportZenPlus    string `header:"CPU support[14] Zen+"`
    SupportZen2       string `header:"CPU support[14] Zen 2"`
    SupportZen3       string `header:"CPU support[14] Zen 3"`
    Architecture      string `header:"Architecture"`
}
am4Chipsets, _ := htmltable.NewSliceFromURL[AM4]("https://en.wikipedia.org/wiki/List_of_AMD_chipsets")
fmt.Println(am4Chipsets[2].Model)
fmt.Println(am4Chipsets[2].SupportZen2)

// Output:
// X370
// Varies[c]

Попробуйте такой подход, чтобы создать 2d-массив и обрабатывать переменные размеры строк:

    z := html.NewTokenizer(body)
    table := [][]string{}
    row := []string{}

    for z.Token().Data != "html" {
        tt := z.Next()
        if tt == html.StartTagToken {
            t := z.Token()

            if t.Data == "tr" {
                if len(row) > 0 {
                    table = append(table, row)
                    row = []string{}
                }
            }

            if t.Data == "td" {
                inner := z.Next()

                if inner == html.TextToken {
                    text := (string)(z.Text())
                    t := strings.TrimSpace(text)
                    row = append(row, t)
                }
            }

        }
    }
    if len(row) > 0 {
        table = append(table, row)
    }
Другие вопросы по тегам