From 06b8116b799bc9a5d19ba1f271982d03e8369fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E5=B3=B0?= Date: Fri, 10 Apr 2026 14:52:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=8B=E5=8A=A8=E6=B7=BB=E5=8A=A0=E7=9A=84ur?= =?UTF-8?q?l=E8=BF=94=E5=9B=9E=E7=9A=84url=E4=B8=8D=E9=99=90=E5=88=B6?= =?UTF-8?q?=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 10 ++++++++++ crawler/crawler.go | 34 ++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/config/config.go b/config/config.go index 44b1349..0785fc5 100644 --- a/config/config.go +++ b/config/config.go @@ -47,6 +47,7 @@ type CrawlerConfig struct { RecrawlMaxAge int `yaml:"recrawl_max_age"` // URL 过期时间(秒),超过此时间的 URL 允许被重爬,默认 30 天 RecrawlCheckInterval int `yaml:"recrawl_check_interval"` // 运行期间检查过期 URL 的间隔(秒),默认 1 小时 RecrawlBatchSize int `yaml:"recrawl_batch_size"` // 每次检查最多释放多少个过期 URL,默认 500 + MaxPriorityChildren int `yaml:"max_priority_children"` // priorityChildren 队列的最大链接数,默认 100 } // SearchConfig 搜索结果排序权重配置 @@ -128,6 +129,7 @@ func GetDefaultConfig() Config { RecrawlMaxAge: 30 * 86400, // 30 天 RecrawlCheckInterval: 3600, // 1 小时 RecrawlBatchSize: 500, + MaxPriorityChildren: 100, }, Search: SearchConfig{ UseOnlineSnippet: true, @@ -274,5 +276,13 @@ func PromPortBacklink() int { return Global.Prometheus.BacklinkPort } // PromPortSearch 返回配置值 func PromPortSearch() int { return Global.Prometheus.SearchPort } +// MaxPriorityChildren 返回 priorityChildren 队列的最大链接数(0=不限)。 +func MaxPriorityChildren() int { + if Global.Crawler.MaxPriorityChildren <= 0 { + return 100 // 默认 100 + } + return Global.Crawler.MaxPriorityChildren +} + // 为了向后兼容,保留 StoragePath 常量 const StoragePath = "./savedata" diff --git a/crawler/crawler.go b/crawler/crawler.go index 4779858..4c2ce7b 100644 --- a/crawler/crawler.go +++ b/crawler/crawler.go @@ -240,8 +240,8 @@ func (c *Crawler) runPriorityWorker() { log.Printf("[crawler] priority crawl started: %s", rawURL) - // 直接调用 visitURL,绕过队列调度 - hrefs := c.visitURL(rawURL) + // 直接调用 visitURLUnlimited,绕过队列调度和链接数限制 + hrefs := c.visitURLUnlimited(rawURL) // 将子链接加入优先队列(保持优先级) if len(hrefs) > 0 { @@ -436,15 +436,15 @@ func (c *Crawler) Run(entryURL string, maxEpoch int) { } } -// visitURL 抓取一个 URL,提取关键词、缓存摘要、更新网站元信息,返回页面中发现的子链接。 -func (c *Crawler) visitURL(rawURL string) (hrefs []string) { +// visitURLRaw 抓取 URL 的核心逻辑,提取标题、描述、正文、子链接。 +// 不包含链接数限制,用于优先级爬取。 +func (c *Crawler) visitURLRaw(rawURL string) (title, desc, text string, hrefs []string) { // recover 保护:防止任何模块(analyzer/storage/parser)的 panic 杀死 goroutine defer func() { if r := recover(); r != nil { log.Printf("[crawler] visitURL panic recovered: url=%s error=%v", rawURL, r) } }() - atomic.AddInt64(&c.stats.VisitedURLs, 1) // 计数器 +1 // 使用 sync.WaitGroup + select 实现硬超时包装器, // 确保即使 http.Client.Timout 被某些底层操作忽略,goroutine 也不会永久阻塞。 @@ -478,7 +478,7 @@ func (c *Crawler) visitURL(rawURL string) (hrefs []string) { atomic.AddInt64(&c.stats.SuccessURLs, 1) // 成功计数器 +1 // 解析 HTML:提取标题、描述、正文和所有超链接 - title, desc, text, hrefs := parser.ParseHTML(res.Body, res.FinalURL) + title, desc, text, hrefs = parser.ParseHTML(res.Body, res.FinalURL) // 计算正文内容哈希(FNV-1a),用于增量重爬检测 contentHash := fnvHash(text) @@ -539,13 +539,31 @@ func (c *Crawler) visitURL(rawURL string) (hrefs []string) { }) } + return +} + +// visitURL 抓取一个 URL,提取关键词、缓存摘要、更新网站元信息,返回页面中发现的子链接。 +// 限制返回的链接数,防止下一轮队列爆炸。 +func (c *Crawler) visitURL(rawURL string) (hrefs []string) { + atomic.AddInt64(&c.stats.VisitedURLs, 1) // 计数器 +1 + _, _, _, hrefs = c.visitURLRaw(rawURL) + // 限制返回的链接数,防止下一轮队列爆炸 - if len(hrefs) > 100 { - hrefs = sampleStrings(hrefs, 100) + maxLinks := config.MaxPriorityChildren() + if maxLinks > 0 && len(hrefs) > maxLinks { + hrefs = sampleStrings(hrefs, maxLinks) } return hrefs } +// visitURLUnlimited 抓取一个 URL,返回页面中发现的子链接(无链接数限制)。 +// 用于手动添加的 Priority URL,保留全部子链接。 +func (c *Crawler) visitURLUnlimited(rawURL string) (hrefs []string) { + atomic.AddInt64(&c.stats.VisitedURLs, 1) // 计数器 +1 + _, _, _, hrefs = c.visitURLRaw(rawURL) + return hrefs +} + // updateSiteFailure 当某 URL 抓取失败时,更新该网站的访问成功率(指数衰减)。 func (c *Crawler) updateSiteFailure(rawURL string) { host := netloc(rawURL)