-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtables.go
More file actions
71 lines (63 loc) · 2.35 KB
/
Copy pathtables.go
File metadata and controls
71 lines (63 loc) · 2.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package defuddle
import (
"strings"
"github.com/PuerkitoBio/goquery"
)
// Table is a structured representation of an HTML <table>, suitable for JSON
// output. Headers holds the column labels (from <thead> <th> cells, falling
// back to the first row that contains <th> cells); Rows holds each body row's
// cell text in document order.
type Table struct {
Caption string `json:"caption,omitempty"`
Headers []string `json:"headers"`
Rows [][]string `json:"rows"`
}
// ExtractTables parses an HTML fragment and returns every <table> it contains
// as a structured Table. Cell text is trimmed and internal whitespace
// collapsed to single spaces. Headers come from the table's <thead> <th> cells
// when present, otherwise from the first non-thead row that contains <th>
// cells; that header row is then excluded from Rows.
func ExtractTables(htmlContent string) ([]Table, error) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlContent))
if err != nil {
return nil, err
}
var tables []Table
doc.Find("table").Each(func(_ int, t *goquery.Selection) {
tables = append(tables, extractTable(t))
})
return tables, nil
}
func extractTable(t *goquery.Selection) Table {
tbl := Table{Headers: []string{}, Rows: [][]string{}}
tbl.Caption = collapseWhitespace(t.ChildrenFiltered("caption").Text())
t.Find("thead th").Each(func(_ int, c *goquery.Selection) {
tbl.Headers = append(tbl.Headers, collapseWhitespace(c.Text()))
})
headerRow := -1
t.Find("tr").Each(func(i int, tr *goquery.Selection) {
if tr.Closest("thead").Length() > 0 {
return // header cells already captured above (or empty thead)
}
// First body row of <th> becomes the header when none found in <thead>.
if len(tbl.Headers) == 0 && headerRow == -1 && tr.ChildrenFiltered("th").Length() > 0 {
tr.ChildrenFiltered("th").Each(func(_ int, c *goquery.Selection) {
tbl.Headers = append(tbl.Headers, collapseWhitespace(c.Text()))
})
headerRow = i
return
}
var cells []string
tr.ChildrenFiltered("td,th").Each(func(_ int, c *goquery.Selection) {
cells = append(cells, collapseWhitespace(c.Text()))
})
if len(cells) > 0 {
tbl.Rows = append(tbl.Rows, cells)
}
})
return tbl
}
// collapseWhitespace trims and collapses all runs of whitespace to single spaces.
func collapseWhitespace(s string) string {
return strings.Join(strings.Fields(s), " ")
}