You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Go语言ChromeDp转换HTML为PDF格式异常问题求助

使用ChromeDp抓取动态HTML转PDF格式不符问题

我使用Go语言的ChromeDp包,从指定URL抓取包含API动态渲染内容的HTML并转换为PDF。目前抓取到的HTML内容正确,但生成的PDF格式不符合预期。

原代码

func main() {

    t1 := time.Now()
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    url := "url_from_where_you want to capture html"

    var dataHTML string

    if err := chromedp.Run(ctx,
        chromedp.Navigate(url),
        chromedp.WaitVisible("#root", chromedp.ByQuery),
    ); err != nil {
        log.Fatal(err)
    }
    fmt.Println("Page loaded successfully")

    // Sleep for a brief period to ensure the data rendering is complete (adjust timing as needed)
    time.Sleep(2 * time.Second)

    // Capture the HTML content of the element containing the rendered API data
    if err := chromedp.Run(ctx,
        chromedp.OuterHTML("#root", &dataHTML, chromedp.NodeVisible),
    ); err != nil {
        log.Fatal(err)
    }

    fmt.Println("Captured HTML content", dataHTML)

    CreatePdfInBytes(ctx, dataHTML)
    
    t2 := time.Now()
    fmt.Println(t2.Sub(t1))
}

func CreatePdfInBytes(ctx context.Context, html string){
    var wg sync.WaitGroup
    var pdfBuf []byte

    navigate := chromedp.Navigate("about:blank")

    eventLoader := chromedp.ActionFunc(func(ctx context.Context) error {
        loaderctx, cancel := context.WithCancel(ctx)
        chromedp.ListenTarget(loaderctx, func(event interface{}) {
            if _, ok := event.(*page.EventLoadEventFired); ok {
                wg.Done()
                cancel()
            }
        })
        return nil
    })

    setDocContent := chromedp.ActionFunc(func(ctx context.Context) error {
        frameTree, err := page.GetFrameTree().Do(ctx)
        if err != nil {
            return err
        }
        return page.SetDocumentContent(frameTree.Frame.ID, html).Do(ctx)

    })

    loaderWg := chromedp.ActionFunc(func(ctx context.Context) error {
        wg.Wait()
        return nil
    })

    genPdf := chromedp.ActionFunc(func(ctx context.Context) error {
        buf, _, err := page.PrintToPDF().WithMarginTop(0.3).WithMarginBottom(0.3).WithPrintBackground(true).Do(ctx)
        if err != nil {
            return err
        }
        pdfBuf = buf
        return nil
    })

    wg.Add(1)
    err := chromedp.Run(ctx, navigate, eventLoader, setDocContent, loaderWg, genPdf)
    if err != nil {
        fmt.Println(err)
    }

    file, err := os.Create("abc.pdf")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    // Write the PDF data to the file
    _, err = file.Write(pdfBuf)
    if err != nil {
        panic(err)
    }
    return 
}

问题分析

  1. 样式依赖缺失:仅抓取#root元素的HTML,原页面的全局CSS、字体、外部样式表等资源未被包含,新页面渲染时丢失样式,导致PDF布局错乱。
  2. 等待渲染方式不可靠:使用time.Sleep固定等待时间,无法确保动态内容完全渲染完成,可能导致抓取的HTML不完整。
  3. 孤立页面渲染限制:将孤立元素的HTML放入about:blank页面,缺少原页面的完整文档结构(如<head>、<body>)和环境配置,无法还原原页面布局。

修正方案

方案一:直接在原页面生成PDF(推荐)

跳过抓取HTML的步骤,直接在目标页面渲染完成后调用PrintToPDF,保留所有页面资源和样式:

func main() {
    t1 := time.Now()
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    url := "your-target-url"
    var pdfBuf []byte

    // 导航到页面并等待动态内容渲染完成
    if err := chromedp.Run(ctx,
        chromedp.Navigate(url),
        // 用WaitReady替代time.Sleep,确保动态内容完全渲染
        chromedp.WaitReady("#root", chromedp.ByQuery),
        // 直接生成PDF,保留所有页面样式
        chromedp.ActionFunc(func(ctx context.Context) error {
            var err error
            pdfBuf, _, err = page.PrintToPDF().
                WithMarginTop(0.3).
                WithMarginBottom(0.3).
                WithPrintBackground(true).
                WithPaperWidth(8.27). // 设置A4纸张宽度(英寸)
                WithPaperHeight(11.69). // 设置A4纸张高度(英寸)
                WithScale(1.0). // 调整缩放比例适配内容
                Do(ctx)
            return err
        }),
    ); err != nil {
        log.Fatal(err)
    }

    // 写入PDF文件
    file, err := os.Create("abc.pdf")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    _, err = file.Write(pdfBuf)
    if err != nil {
        panic(err)
    }

    t2 := time.Now()
    fmt.Println("PDF生成完成,耗时:", t2.Sub(t1))
}

方案二:抓取完整页面资源(若需保留抓取HTML流程)

如果必须先抓取HTML,需同时获取原页面的所有样式资源,构建完整HTML文档:

  1. 抓取原页面的<head>内容(包含所有CSS、样式定义)
  2. 将#root的HTML与<head>内容拼接成完整文档
  3. 在新页面加载完整文档后生成PDF

示例代码片段:

// 在main函数中替换原抓取OuterHTML的部分
var headHTML, rootHTML string
if err := chromedp.Run(ctx,
    chromedp.OuterHTML("head", &headHTML),
    chromedp.OuterHTML("#root", &rootHTML),
); err != nil {
    log.Fatal(err)
}
// 构建完整HTML文档
fullHTML := fmt.Sprintf("<!DOCTYPE html><html>%s<body>%s</body></html>", headHTML, rootHTML)
// 传入CreatePdfInBytes处理
CreatePdfInBytes(ctx, fullHTML)

关键优化点

  • 用chromedp.WaitReady替代time.Sleep,确保动态内容完全渲染,避免依赖固定等待时间。
  • 直接在原页面生成PDF,保留所有资源和样式,最可靠还原预期布局。
  • 调整PrintToPDF的纸张尺寸、缩放比例等参数,适配目标内容的布局需求。

内容的提问来源于stack exchange,提问作者Rohit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 11:36:17