package main import ( e "codeberg.org/pebbe/errors" "github.com/jbowtie/gokogiri" "golang.org/x/net/publicsuffix" u "git.web.rug.nl/p209327/nlnieuws/internal/util" "bytes" "encoding/json" "encoding/xml" "errors" "fmt" "io" "net/http" "net/http/cookiejar" "net/url" "os" "strings" "time" ) type Rss struct { XMLName xml.Name `xml:"rss"` Items []ItemT `xml:"channel>item"` } type ItemT struct { Title string `xml:"title"` PubDate string `xml:"pubDate"` UnixTime int64 `xml:"unixTime"` Guid string `xml:"guid"` Link string `xml:"https://www.rssboard.org/rss-specification link"` // i.p.v. atom:link Premium bool `xml:"premium"` Data []byte `xml:",innerxml"` } type LdJson struct { Type string `json:"@type"` IsFree string `json:"isAccessibleForFree"` H1 string `json:"headline"` H2 string `json:"description"` } var ( p = e.PanicErr w = e.WarnErr agent = "AhrefsBot/7.0" // agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" jar *cookiejar.Jar ) func exists(filename string) bool { _, err := os.Stat(filename) return err == nil } func fileDate(filename string) string { b, err := os.ReadFile(filename) if err != nil { return "" } s := string(b) i1 := strings.Index(s, "") + 10 i2 := strings.Index(s, "") if i2 < i1 { return "" } return s[i1:i2] } func main() { defer func() { if e.Panicked { _ = recover() os.Exit(1) } }() var err error jar, err = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) p(err) myLock := "/net/corpora/nlnieuws/Sikkom/lock" u.MkLock(myLock) defer func() { _ = os.Remove(myLock) }() resp, err := http.Get("https://www.sikkom.nl/rss") p(err) body, err := io.ReadAll(resp.Body) p(err) p(resp.Body.Close()) // zet de default namespace om onderscheid te kunnen maken met overige namespaces b2 := bytes.Replace(body, []byte("\n\n")) p(fmt.Fprintf(fp, "%d", t.Unix())) p(fp.Write(item.Data)) p(fp.WriteString("\n")) p(fp.Close()) p(os.Chtimes(filename+".xml", t, t)) ok = doArticle(filename, item.Link, item.Title, t, needUpdate) }() } } func doArticle(filename string, url string, title string, timestamp time.Time, needUpdate bool) bool { if exists(filename + ".skip") { return true } if needUpdate { _ = os.Remove(filename + ".err") _ = os.Remove(filename + ".html") _ = os.Remove(filename + ".json") _ = os.Remove(filename + ".txt") } else { if exists(filename+".json") && exists(filename+".txt") { return true } } time.Sleep(2 * time.Second) req, err := http.NewRequest("GET", url, nil) p(err) req.Header.Set("User-Agent", agent) client := &http.Client{Jar: jar} resp, err := client.Do(req) p(err) body, err := io.ReadAll(resp.Body) p(err) p(resp.Body.Close()) body = u.HtmlFix(body) doc, err := gokogiri.ParseHtml(body) p(err) root := doc.Root() ldjsons, err := root.Search(`//script[@type="application/ld+json"]`) p(err) found := false var jsmain LdJson for _, ldjson := range ldjsons { s := ldjson.Content() var js LdJson p(json.Unmarshal([]byte(s), &js)) if js.Type == "NewsArticle" { found = true fp, err := os.Create(filename + ".json") p(err) p(fp.WriteString(s)) p(fp.Close()) p(os.Chtimes(filename+".json", timestamp, timestamp)) jsmain = js break } } if !found { _ = w(fmt.Errorf("script jsonld not found: %s", url)) fp, err := os.Create(filename + ".err") p(err) p(fmt.Fprintf(fp, "script jsonld not found: %s\n", url)) p(fp.Close()) p(os.Chtimes(filename+".err", timestamp, timestamp)) fp, err = os.Create(filename + ".html") p(err) p(fp.Write(body)) p(fp.Close()) p(os.Chtimes(filename+".html", timestamp, timestamp)) return false } locations, err := root.Search(`//span[starts-with(@class, "location")]`) p(err) for _, location := range locations { location.Remove() } articles, err := root.Search(`//article[starts-with(@class, "article")]`) p(err) if len(articles) != 1 { msg := fmt.Sprintf("len(articles) == %d in %s", len(articles), url) _ = w(errors.New(msg)) fp, err := os.Create(filename + ".err") p(err) p(fmt.Fprintln(fp, msg)) p(fp.Close()) p(os.Chtimes(filename+".err", timestamp, timestamp)) fp, err = os.Create(filename + ".html") p(err) p(fp.Write(body)) p(err) p(fp.Close()) p(os.Chtimes(filename+".html", timestamp, timestamp)) return false } art := articles[0] text := make([]string, 0) if jsmain.H1 != "" { text = append(text, jsmain.H1) } else { text = append(text, title) } if jsmain.H2 != "" { text = append(text, jsmain.H2) } abb, err := art.Search(`.//div[starts-with(@class, "article-body")]`) p(err) found = false for _, ab := range abb { ell, err := ab.Search(`.//p | .//h4`) p(err) for _, el := range ell { if el.Name() == "p" { found = true } text = append(text, el.Content()) } } if !found { _ = w(fmt.Errorf("empty body: %s", url)) fp, err := os.Create(filename + ".err") p(err) p(fmt.Fprintf(fp, "empty body: %s\n", url)) p(fp.Close()) p(os.Chtimes(filename+".err", timestamp, timestamp)) fp, err = os.Create(filename + ".html") p(err) p(fp.Write(body)) p(fp.Close()) p(os.Chtimes(filename+".html", timestamp, timestamp)) return false } fp, err := os.Create(filename + ".txt") p(err) for _, t := range text { p(fp.WriteString(u.AddEnd(u.FixSpace(t)))) } p(fp.Close()) return true }