一个基于 Go 的高性能网站内容采集与 HTML 转 Markdown 工具包,聚焦"抓取网页 + LLM(大语言模型)内容处理"理念,支持递归抓取、深度/数量/延迟/并发控制、同域名限制、自定义 Header、日志输出、可选 Markdown 转换等。
- 支持递归抓取指定网站及其子页面
- 支持最大抓取数量、最大深度、延迟、并发等参数
- 支持同域名限制、自定义请求头、随机 User-Agent、自定义 User-Agent 池
- 支持抓取内容自动转换为 Markdown(可选)
- 支持自定义日志输出
- 遵循 robots.txt 协议,仅抓取允许的链接
- 内置智能去重:默认使用 Colly 框架的重复 URL 过滤机制,无需手动判断
- 异步处理模式:支持启用异步处理提升并发性能,适合高吞吐量场景
go get gitee.com/JJ-H/web-harvester
package main
import (
"fmt"
"time"
"gitee.com/JJ-H/crawler/pkg/web-harvester"
)
type simpleLogger struct{}
func (l *simpleLogger) Info(format string, args ...interface{}) { fmt.Printf("[INFO] "+format+"\n", args...) }
func (l *simpleLogger) Error(format string, args ...interface{}) { fmt.Printf("[ERROR] "+format+"\n", args...) }
func main() {
c := crawler.NewCrawler(
crawler.WithMaxCount(10),
crawler.WithMaxDepth(2),
crawler.WithDelay(500*time.Millisecond),
crawler.WithConcurrency(2),
crawler.WithSameDomainOnly(true),
crawler.WithRandomUserAgent(),
crawler.WithConvertToMarkdown(true),
crawler.WithLogger(&simpleLogger{}),
)
results, err := c.Crawl("https://example.com/xxx")
if err != nil {
fmt.Println("抓取出错:", err)
return
}
for i, page := range results {
fmt.Printf("\n--- 第%d页 ---\nURL: %s\n抓取时间: %s\nMarkdown内容预览:\n%s\n", i+1, page.URL, page.FetchedAt.Format(time.RFC3339), page.Markdown)
}
}
func myPageHandler(res crawler.PageResult) error {
// 这里可以直接写入数据库、文件等
fmt.Println("抓到页面:", res.URL, "标题:", res.Title)
return nil
}
func main() {
c := crawler.NewCrawler(
crawler.WithMaxDepth(2),
crawler.WithDelay(500*time.Millisecond),
// ...其他配置
)
err := c.CrawlWithHandler("https://example.com/xxx", myPageHandler)
if err != nil {
fmt.Println("抓取出错:", err)
}
}
对于需要更高并发性能的场景,可以启用异步处理模式:
func main() {
// 创建异步模式爬虫
asyncCrawler := crawler.NewCrawler(
crawler.WithMaxCount(100),
crawler.WithMaxDepth(3),
crawler.WithDelay(200*time.Millisecond),
crawler.WithConcurrency(20), // 更高的并发数
crawler.WithSameDomainOnly(true),
crawler.WithConvertToMarkdown(true),
crawler.WithLogger(&simpleLogger{}),
crawler.WithAsync(true), // 启用异步处理模式
)
fmt.Println("开始异步抓取...")
start := time.Now()
results, err := asyncCrawler.Crawl("https://example.com")
if err != nil {
fmt.Println("异步抓取出错:", err)
return
}
duration := time.Since(start)
fmt.Printf("异步抓取完成,用时: %v,共抓取 %d 页\n", duration, len(results))
for i, page := range results {
fmt.Printf("页面 #%d: %s (标题: %s)\n", i+1, page.URL, page.Title)
}
}
异步模式特点:
- 底层使用 Colly 的异步处理机制(
colly.Async(true)
) - 显著提升高并发场景下的抓取性能
- 适合需要处理大量页面的批量抓取任务
- 可与其他配置选项(如并发数、延迟等)配合使用
字段 | 类型 | 说明 |
---|---|---|
URL | string | 页面地址 |
FetchedAt | time.Time | 抓取时间 |
HTML | string | 原始 HTML 内容 |
Markdown | string | 转换后的 Markdown(如启用) |
Title | string | 页面 <title> |
Crawl(url)
:一次性抓取,返回所有页面结果切片,适合小批量或需要整体处理的场景。CrawlWithHandler(url, handler)
:每抓到一页就回调 handler,适合大批量、需要实时处理/持久化的场景。
两种方式可并存,按需选择。
- 默认内置一组常见浏览器 User-Agent,每次请求自动随机选取。
- 如需自定义 UA 池,可通过
WithUserAgentPool([]string{...})
传入。 - 如需固定 UA,可用
WithHeader("User-Agent", "xxx")
,但若同时启用随机 UA,则以随机 UA 为准。
- 爬虫会自动下载并解析目标站点 robots.txt 文件。
- 仅抓取 robots.txt 中允许(Allow)的链接,Disallow 的路径不会被访问。
- 默认使用 UA 池的第一个 User-Agent 作为 robots.txt 匹配 UA(如无则用 *)。
- 若 robots.txt 获取失败,则默认全部允许。
配置选项 | 类型 | 默认值 | 说明 |
---|---|---|---|
WithMaxCount(n) |
int | 无限制 | 设置最大抓取页面数量 |
WithMaxDepth(n) |
int | 无限制 | 设置最大抓取深度 |
WithDelay(d) |
time.Duration | 0 | 设置请求间隔延迟 |
WithConcurrency(n) |
int | 1 | 设置并发协程数量 |
WithSameDomainOnly(b) |
bool | false | 是否仅抓取同域名页面 |
WithHeader(key, value) |
string | - | 添加自定义请求头 |
WithRetry(n) |
int | 0 | 设置请求失败重试次数 |
WithLogger(logger) |
Logger | nil | 设置自定义日志记录器 |
WithConvertToMarkdown(b) |
bool | false | 是否将 HTML 转换为 Markdown |
WithRandomUserAgent() |
- | - | 启用随机 User-Agent |
WithUserAgentPool(pool) |
[]string | 内置池 | 设置自定义 User-Agent 池 |
WithAllowURLRevisit(b) |
bool | false | 是否允许重复访问同一 URL |
WithAsync(b) |
bool | false | 启用异步处理模式,提升并发性能 |
异步模式注意事项:
- 异步模式下建议适当增加
WithConcurrency
的值以发挥性能优势 - 异步模式适合处理大量页面的批量抓取任务
- 可与延迟控制配合使用,避免对目标服务器造成过大压力
.
├── pkg/crawler # 核心库代码
├── sample.go # 使用示例
├── README.md
├── .gitignore
└── ...
MIT