forked from go-shiori/go-readability
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser-parse.go
129 lines (108 loc) · 3.53 KB
/
parser-parse.go
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package readability
import (
"fmt"
"io"
nurl "net/url"
"strings"
"github.com/go-shiori/dom"
"golang.org/x/net/html"
)
// Parse parses a reader and find the main readable content.
func (ps *Parser) Parse(input io.Reader, pageURL *nurl.URL) (Article, error) {
// Parse input
doc, err := dom.Parse(input)
if err != nil {
return Article{}, fmt.Errorf("failed to parse input: %v", err)
}
return ps.ParseDocument(doc, pageURL)
}
// ParseDocument parses the specified document and find the main readable content.
func (ps *Parser) ParseDocument(doc *html.Node, pageURL *nurl.URL) (Article, error) {
// Clone document to make sure the original kept untouched
ps.doc = dom.Clone(doc, true)
// Reset parser data
ps.articleTitle = ""
ps.articleByline = ""
ps.articleDir = ""
ps.articleSiteName = ""
ps.documentURI = pageURL
ps.attempts = []parseAttempt{}
ps.flags = flags{
stripUnlikelys: true,
useWeightClasses: true,
cleanConditionally: true,
}
// Avoid parsing too large documents, as per configuration option
if ps.MaxElemsToParse > 0 {
numTags := len(dom.GetElementsByTagName(ps.doc, "*"))
if numTags > ps.MaxElemsToParse {
return Article{}, fmt.Errorf("documents too large: %d elements", numTags)
}
}
// Unwrap image from noscript
ps.unwrapNoscriptImages(ps.doc)
// Extract JSON-LD metadata before removing scripts
var jsonLd map[string]string
if !ps.DisableJSONLD {
jsonLd, _ = ps.getJSONLD()
}
// Remove script tags from the document.
ps.removeScripts(ps.doc)
// Prepares the HTML document
ps.prepDocument()
// Fetch metadata
metadata := ps.getArticleMetadata(jsonLd)
ps.articleTitle = metadata["title"]
// Try to grab article content
finalHTMLContent := ""
finalTextContent := ""
articleContent := ps.grabArticle()
var readableNode *html.Node
if articleContent != nil {
ps.postProcessContent(articleContent)
// If we haven't found an excerpt in the article's metadata,
// use the article's first paragraph as the excerpt. This is used
// for displaying a preview of the article's content.
if metadata["excerpt"] == "" {
paragraphs := dom.GetElementsByTagName(articleContent, "p")
if len(paragraphs) > 0 {
metadata["excerpt"] = strings.TrimSpace(dom.TextContent(paragraphs[0]))
}
}
readableNode = dom.FirstElementChild(articleContent)
finalHTMLContent = dom.InnerHTML(articleContent)
finalTextContent = dom.TextContent(articleContent)
finalTextContent = strings.TrimSpace(finalTextContent)
}
finalByline := metadata["byline"]
if finalByline == "" {
finalByline = ps.articleByline
}
// Excerpt is an supposed to be short and concise,
// so it shouldn't have any new line
excerpt := strings.TrimSpace(metadata["excerpt"])
excerpt = strings.Join(strings.Fields(excerpt), " ")
// go-readability special:
// Internet is dangerous and weird, and sometimes we will find
// metadata isn't encoded using a valid Utf-8, so here we check it.
var replacementTitle string
if pageURL != nil {
replacementTitle = pageURL.String()
}
validTitle := strings.ToValidUTF8(ps.articleTitle, replacementTitle)
validByline := strings.ToValidUTF8(finalByline, "")
validExcerpt := strings.ToValidUTF8(excerpt, "")
return Article{
Title: validTitle,
Byline: validByline,
Node: readableNode,
Content: finalHTMLContent,
TextContent: finalTextContent,
Length: charCount(finalTextContent),
Excerpt: validExcerpt,
SiteName: metadata["siteName"],
Image: metadata["image"],
Favicon: metadata["favicon"],
Language: ps.articleLang,
}, nil
}