// Package novelfire provides a NovelScraper implementation for novelfire.net. // // Site structure (as of 2025): // // Catalogue : https://novelfire.net/genre-all/sort-new/status-all/all-novel?page=N // Book page : https://novelfire.net/book/{slug} // Chapters : https://novelfire.net/book/{slug}/chapters?page=N // Chapter : https://novelfire.net/book/{slug}/{chapter-slug} package novelfire import ( "context" "fmt" "log/slog" "net/url" "strconv" "strings" "github.com/libnovel/scraper/internal/browser" "github.com/libnovel/scraper/internal/scraper" "github.com/libnovel/scraper/internal/scraper/htmlutil" ) const ( baseURL = "https://novelfire.net" cataloguePath = "/genre-all/sort-new/status-all/all-novel" ) // Scraper is the novelfire.net implementation of scraper.NovelScraper. // It uses the /content strategy by default (rendered HTML via Browserless). type Scraper struct { client browser.BrowserClient log *slog.Logger } // New returns a new novelfire Scraper. func New(client browser.BrowserClient, log *slog.Logger) *Scraper { if log == nil { log = slog.Default() } return &Scraper{client: client, log: log} } // SourceName implements NovelScraper. func (s *Scraper) SourceName() string { return "novelfire.net" } // ─── CatalogueProvider ─────────────────────────────────────────────────────── func (s *Scraper) CatalogueURL() string { return baseURL + cataloguePath } func (s *Scraper) EntriesSelector() scraper.Selector { // Each novel card:
...