webfetch

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

go-webfetch

Go 语言实现的网页抓取与 PDF 解析库,面向 AI Agent 场景设计。

核心亮点

1. 智能 HTML → Markdown 转换

不是简单的标签剥离。管线式处理:预清理噪声标签 → 结构保护 → readability 正文提取 → 启发式 fallback → GFM Markdown 转换(含表格、代码块、列表)。

原始 HTML → 去噪 → 锚定结构 → readability 提取 → html-to-markdown(GFM) → 干净 Markdown
2. 安全防护
  • SSRF 防护:自动拦截私有 IP / 内网地址请求
  • WAF 检测:识别 Cloudflare、SafeLine(长亭雷池)、DataDome 等常见 WAF 拦截
  • 404 快速失败:明确的 NotFoundError,方便 Agent 快速判断 URL 是否有效
  • 空内容检测:HTTP 200 但内容为空时返回 EmptyContentError(常见于反爬空壳页面)
3. PDF 解析

支持本地文件和远程 URL 的 PDF 解析,自动清理中文 PDF 的单字换行噪声。

4. Agent 友好输出
  • 大内容自动写入文件,返回文件路径 + 预览摘要
  • AgentHint 提示 Agent 如何分段读取(read_file + grep_search
  • 流式写入,不在内存中持有完整大文件
  • 100KB 内容上限,避免撑爆 LLM 上下文
5. GitHub URL 自动转换

github.com/user/repo/blob/branch/path 自动转为 raw.githubusercontent.com/...,Agent 传入 blob URL 也能正常抓取。

快速开始

package main

import (
    "context"
    "fmt"
    webfetch "go-webfetch"
)

func main() {
    engine, err := webfetch.New(webfetch.Config{})
    if err != nil {
        panic(err)
    }
    defer engine.Close()

    result, err := engine.Fetch(context.Background(), "https://example.com")
    if err != nil {
        // err 可能是 *WAFError, *NotFoundError, *EmptyContentError, *SSRFError
        panic(err)
    }

    fmt.Printf("Title: %s\n", result.Title)
    fmt.Printf("Mode:  %s\n", result.Mode) // "inline" 或 "saved_to_file"
    if result.Mode == "inline" {
        fmt.Println(result.Markdown)
    } else {
        fmt.Printf("File: %s (%d lines, %d chars)\n", result.FilePath, result.TotalLines, result.TotalChars)
        fmt.Println(result.AgentHint)
    }
}

配置项

webfetch.Config{
    // HTTP
    Timeout:       15 * time.Second,
    MaxRedirects:  5,
    UserAgent:     "...",

    // 安全
    BlockPrivateIP: true,  // SSRF 防护
    MaxURLLength:   2048,
    MaxBodyBytes:   5 * 1024 * 1024, // 5MB

    // 内容输出
    MaxInlineLines:   100,     // 超过此行数写文件
    MaxInlineChars:   0,       // 超过此字符数写文件
    MaxContentLength: 100000,  // 100KB 内容上限
    FileOutputDir:    "",      // 默认 os.TempDir()/webfetch/
    FileTTL:          24 * time.Hour,

    // 代理
    ProxyURL: "", // 支持 http/https/socks5
}

错误类型

类型 含义
*NotFoundError URL 返回 404/410,URL 不存在
*WAFError 被 WAF/反爬拦截
*EmptyContentError HTTP 200 但内容为空(反爬空壳页面)
*SSRFError 目标解析到私有 IP

适用场景

  • AI Agent 网页浏览:结构化输出、大文件自动落盘、Agent 提示
  • 内容采集管线:高质量 Markdown 转换,保留表格/代码/列表结构
  • 安全敏感环境:SSRF 防护、WAF 检测、URL 校验
  • PDF 文档处理:中文 PDF 解析,自动清理排版噪声

相比常见实现的优势

维度 go-webfetch 常见实现
HTML→Markdown readability + 启发式 + GFM 表格 正则剥标签 / html-to-text
安全 SSRF + WAF + 404 检测
大文件 流式写文件 + Agent 提示 全部内存 / 截断
PDF 解析 + 中文噪声清理 不支持
输出 结构化 JSON(title/method/mode/lines) 纯文本

License

Apache License 2.0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// ── HTTP ──
	UserAgent    string            // 默认 Chrome 124 UA
	ExtraHeaders map[string]string // 额外请求头
	Timeout      time.Duration     // 单次请求超时,默认 15s
	MaxRedirects int               // 最大重定向次数,默认 5

	// ── 代理 ──
	ProxyURL       string // HTTP 代理地址,如 "http://user:pass@host:port"
	UseSystemProxy bool   // 自动使用系统代理(读取 HTTP_PROXY/HTTPS_PROXY 环境变量)

	// ── 安全 ──
	BlockPrivateIP bool  // SSRF 防护,默认 true
	MaxURLLength   int   // 默认 2048
	MaxBodyBytes   int64 // 下载上限,默认 5MB

	// ── 内容输出 ──
	MaxInlineLines   int           // Markdown 超过此行数则写文件,默认 100
	MaxInlineChars   int           // Markdown 超过此字符数则写文件,默认 0(不启用)
	MaxContentLength int           // 输出给 LLM 的内容上限,默认 100000(100KB)
	FileOutputDir    string        // 文件输出目录,默认 os.TempDir()/webfetch/
	FileTTL          time.Duration // 文件保留时间,默认 24h

	// ── 反爬 ──
	FastFailWAF bool // 遇 WAF 立即失败,默认 true
	MaxRetries  int  // 最大重试次数,默认 3

	// ── PDF ──
	PDFMaxPages  int  // 最大解析页数,默认 200
	PDFHeuristic bool // 是否启用启发式结构化,默认 true
}

Config 引擎配置

type EmptyContentError

type EmptyContentError struct {
	URL       string
	Title     string
	CharCount int
}

EmptyContentError 表示页面返回了 HTTP 200 但内容为空或过少 通常是反爬机制返回的空壳页面(JS 渲染、Token 验证等)

func (*EmptyContentError) Error

func (e *EmptyContentError) Error() string

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine 抓取引擎

func New

func New(cfg Config) (*Engine, error)

New 创建引擎实例

func (*Engine) CleanFiles

func (e *Engine) CleanFiles() (int, error)

CleanFiles 清理过期的缓存文件

func (*Engine) Close

func (e *Engine) Close() error

Close 释放资源

func (*Engine) Fetch

func (e *Engine) Fetch(ctx context.Context, url string) (*FetchResult, error)

Fetch 抓取 URL 并返回结果

func (*Engine) FetchWithOpts

func (e *Engine) FetchWithOpts(ctx context.Context, url string, opts FetchOptions) (*FetchResult, error)

FetchWithOpts 带选项的抓取

func (*Engine) ParsePDFBytes

func (e *Engine) ParsePDFBytes(ctx context.Context, data []byte, name string) (*PDFResult, error)

ParsePDFBytes 解析 PDF 字节流

func (*Engine) ParsePDFFile

func (e *Engine) ParsePDFFile(ctx context.Context, filePath string) (*PDFResult, error)

ParsePDFFile 解析本地 PDF 文件

func (*Engine) ParsePDFURL

func (e *Engine) ParsePDFURL(ctx context.Context, url string) (*PDFResult, error)

ParsePDFURL 下载并解析远程 PDF

type FetchOptions

type FetchOptions struct {
	MaxInlineLines *int
	MaxInlineChars *int
}

FetchOptions 抓取选项(FetchWithOpts 使用)

type FetchResult

type FetchResult struct {
	URL         string        `json:"url"`
	FinalURL    string        `json:"final_url"`
	Title       string        `json:"title"`
	StatusCode  int           `json:"status_code"`
	ContentType string        `json:"content_type"`
	Method      string        `json:"method"` // "native-http" | "headless" | "direct"
	FetchedAt   time.Time     `json:"fetched_at"`
	Elapsed     time.Duration `json:"elapsed"`

	// 输出二选一
	Mode     string `json:"mode"` // "inline" | "saved_to_file"
	Markdown string `json:"markdown,omitempty"`
	FilePath string `json:"file_path,omitempty"`

	// saved_to_file 时的元数据
	TotalLines int    `json:"total_lines,omitempty"`
	TotalChars int    `json:"total_chars,omitempty"`
	Summary    string `json:"summary,omitempty"`
	AgentHint  string `json:"agent_hint,omitempty"`

	Error     string `json:"error,omitempty"`
	ErrorType string `json:"error_type,omitempty"`
}

FetchResult 网页抓取结果

type NotFoundError

type NotFoundError struct {
	URL        string
	StatusCode int
}

NotFoundError 表示 URL 返回 404

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

type PDFParseError

type PDFParseError struct {
	Source string // 文件路径或 "bytes:{name}"
	Reason string
}

PDFParseError 表示 PDF 解析失败

func (*PDFParseError) Error

func (e *PDFParseError) Error() string

type PDFResult

type PDFResult struct {
	SourcePath string `json:"source_path"`
	Title      string `json:"title"`
	PageCount  int    `json:"page_count"`
	Method     string `json:"method"` // "go-fitz"

	Mode     string `json:"mode"`
	Markdown string `json:"markdown,omitempty"`
	FilePath string `json:"file_path,omitempty"`

	TotalLines int    `json:"total_lines,omitempty"`
	TotalChars int    `json:"total_chars,omitempty"`
	Summary    string `json:"summary,omitempty"`
	AgentHint  string `json:"agent_hint,omitempty"`

	Error     string `json:"error,omitempty"`
	ErrorType string `json:"error_type,omitempty"`
}

PDFResult PDF 解析结果

type SSRFError

type SSRFError struct {
	Host string
	IP   string
}

SSRFError 表示目标 URL 解析到内网/保留 IP

func (*SSRFError) Error

func (e *SSRFError) Error() string

type TimeoutError

type TimeoutError struct {
	URL string
}

TimeoutError 表示请求超时

func (*TimeoutError) Error

func (e *TimeoutError) Error() string

type UnsupportedContentTypeError

type UnsupportedContentTypeError struct {
	ContentType string
}

UnsupportedContentTypeError 表示不支持的 Content-Type

func (*UnsupportedContentTypeError) Error

type WAFError

type WAFError struct {
	Reason string // "cloudflare_403" | "challenge_detected" | "js_render_required"
	URL    string
}

WAFError 表示被 WAF/反爬机制拦截

func (*WAFError) Error

func (e *WAFError) Error() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL