Processor
Processor は HTML ライブラリのコア処理エンジンです。パッケージ関数と比較して、Processor は内部リソース(キャッシュ、エンコーディング検出器)を再利用し、高頻度の呼び出しに適しています。
作成
New
Processor インスタンスを作成します。オプションで設定を渡せます。
func New(cfg ...Config) (*Processor, error)パラメータ:最大 1 つの Config。未指定時は DefaultConfig() が使用されます。
p, err := html.New(html.DefaultConfig())
if err != nil {
log.Fatal(err)
}
defer p.Close()内部の初期化:
New は単なる代入ではなく、以下のステップを実行して返された Processor が即座に使えるようにします:
- 設定の検証:
Config.Validate()を呼び出し、無効な設定なら*ConfigErrorを返します(errors.Is(err, ErrInvalidConfig)が真)。検証範囲には数値の境界(MaxInputSize、MaxCacheEntries、WorkerPoolSize、MaxDepthが負や上限超過でないか)とフォーマット文字列(InlineImageFormat/InlineLinkFormat/TableFormatの値が合法か)を含みます。 - Scorer の設定:カスタム
Scorerが設定されていればscorerAdapterで内部インターフェースに適合させ、そうでなければSharedDefaultScorer(読み取り専用、並行安全)を使います。 - フォーマット文字列の事前計算:
InlineImageFormat/InlineLinkFormatを正規化(小文字化+空白削除、空文字列は"none"にマッピング)してimageFormat/linkFormatフィールドにキャッシュし、ホットパスでのstrings.ToLowerの繰り返しを避けます。 - キャッシュクリーンアップの起動:
CacheTTL>0かつCacheCleanup>0の場合にのみバックグラウンドクリーンアップ goroutine を起動し、どちらかが 0 なら起動しません。
並行安全性
並行利用
Processor は複数の goroutine 間で安全に共有でき、追加のロックは不要です。並行性の保証は以下から:
- 設定の不変性:
configはNew()後に不変です(*Configポインタは再代入も変更もされません)。そのためExtractToMarkdownなどのフォーマットメソッドは、ロックなしで安全に値コピーを行い一時 Processor を生成できます——フォーマットの上書きが共有設定に書き戻されることはありません。 - 統計カウンタ:
TotalProcessed/CacheHits/CacheMisses/ErrorCount/totalProcessTimeはすべてatomic操作を使います。 - キャッシュ:内部
Cacheが独自のロックを持ち、読み書きともに安全です。 - Scorer:組み込みの
DefaultScorerは読み取り専用です。カスタムScorerは自前で並行安全を保証する必要があります(内部でロックを持つなど)。1 つの Processor が並行Extractを行うと、複数の goroutine からそのScore/ShouldRemoveが呼ばれるためです。
コンテンツ抽出
エラー戻り値
Extract メソッド群は処理の各段階で明確なセンチネルエラーを返し、errors.Is で正確に判定できます:
| エラー | トリガ条件 | 備考 |
|---|---|---|
ErrProcessorClosed | p が nil または既に Close 済み | すべてのメソッドに共通 |
ErrInputTooLarge | 入力バイト数が MaxInputSize を超過 | *InputError にラップ、実際のサイズ/上限を含む |
| エンコーディング検出エラー | エンコーディング検出または UTF-8 変換の失敗 | 元のエラーがラップされる |
ErrInvalidHTML | バイトが HTML として解析できない | 底層の解析エラーも合わせてラップ |
ErrMaxDepthExceeded | 要素のネスト深度が MaxDepth を超過 | 反復式の検証でスタックオーバーフローを防止 |
ErrProcessingTimeout | 処理時間が ProcessingTimeout を超過 | ProcessingTimeout=0 は時間無制限を意味 |
ErrInternalPanic | 内部の予期しない panic がリカバリされた | 最後の砦の保護で、通常使用では出現しないはず |
context 付きの版はさらに context.Canceled(ユーザーキャンセル)や context.DeadlineExceeded(コンテキストのタイムアウト、ErrProcessingTimeout に正規化済み)を返すことがあります。
Extract
func (p *Processor) Extract(htmlBytes []byte) (*Result, error)HTML バイトからコンテンツを抽出し、エンコーディングを自動検出します。
ExtractFromFile
func (p *Processor) ExtractFromFile(filePath string) (*Result, error)ファイルからコンテンツを抽出します。
ExtractText
func (p *Processor) ExtractText(htmlBytes []byte) (string, error)プレーンテキストのみを返します。
ExtractTextFromFile
func (p *Processor) ExtractTextFromFile(filePath string) (string, error)ファイルからプレーンテキストを抽出します。
コンテキスト付きバージョン
すべての抽出メソッドには ExtractWithContext 付きのバージョンがあります:
func (p *Processor) ExtractWithContext(ctx context.Context, htmlBytes []byte) (*Result, error)
func (p *Processor) ExtractFromFileWithContext(ctx context.Context, filePath string) (*Result, error)
func (p *Processor) ExtractTextWithContext(ctx context.Context, htmlBytes []byte) (string, error)
func (p *Processor) ExtractTextFromFileWithContext(ctx context.Context, filePath string) (string, error)出力フォーマット
func (p *Processor) ExtractToMarkdown(htmlBytes []byte) (string, error)
func (p *Processor) ExtractToMarkdownFromFile(filePath string) (string, error)
func (p *Processor) ExtractToJSON(htmlBytes []byte) ([]byte, error)
func (p *Processor) ExtractToJSONFromFile(filePath string) ([]byte, error)コンテキスト付きバージョン:
func (p *Processor) ExtractToMarkdownWithContext(ctx context.Context, htmlBytes []byte) (string, error)
func (p *Processor) ExtractToMarkdownFromFileWithContext(ctx context.Context, filePath string) (string, error)
func (p *Processor) ExtractToJSONWithContext(ctx context.Context, htmlBytes []byte) ([]byte, error)
func (p *Processor) ExtractToJSONFromFileWithContext(ctx context.Context, filePath string) ([]byte, error)キャッシュ動作の違い
両者はキャッシュの扱いがまったく異なります:
ExtractToMarkdownは一時 Processor を構築し(不変のconfigをコピーするが、MaxCacheEntriesはゼロ、監査は無効化)、主キャッシュの読み書きをしないため、主 Processor のキャッシュを汚染もヒットもしません。Markdown フォーマットの結果もキャッシュされません。ExtractToJSONは直接p.Extractを呼び出し、通常のキャッシュ経路を通ります——主キャッシュにヒット/書き込みし、統計カウンタも更新されます。
Markdown 出力もキャッシュを利用したい場合は、MarkdownConfig() で専用 Processor を作って Extract を呼ぶか、出力を自分でキャッシュしてください。
リンク抽出
func (p *Processor) ExtractAllLinks(htmlBytes []byte) ([]LinkResource, error)
func (p *Processor) ExtractAllLinksFromFile(filePath string) ([]LinkResource, error)
func (p *Processor) ExtractAllLinksWithContext(ctx context.Context, htmlBytes []byte) ([]LinkResource, error)
func (p *Processor) ExtractAllLinksFromFileWithContext(ctx context.Context, filePath string) ([]LinkResource, error)バッチ処理
func (p *Processor) ExtractBatch(htmlContents [][]byte) *BatchResult
func (p *Processor) ExtractBatchWithContext(ctx context.Context, htmlContents [][]byte) *BatchResult
func (p *Processor) ExtractBatchFiles(filePaths []string) *BatchResult
func (p *Processor) ExtractBatchFilesWithContext(ctx context.Context, filePaths []string) *BatchResult統計とキャッシュ
キャッシュ動作の詳細
MaxCacheEntries > 0 の場合、Extract はキャッシュを有効化します:
- ヒット経路:キャッシュ項目を検出した後、
CacheHitsとTotalProcessedをそれぞれ +1 し、返すのはcloneResult——Images/Links/Videos/Audiosなどのスライスにcopyを行うディープコピーです。呼び出し側が戻り値を変更してもキャッシュ内のエントリには影響せず、並行ヒット時の読み取りでのデータ競合も回避できます。 - ミス経路:処理完了後に結果をキャッシュへ書き込み、それから
cloneResult(同じくディープコピー)を返します。そのためキャッシュエントリと戻り値はエイリアスしません。 - キャッシュ無効化:
MaxCacheEntries = 0の場合、Extractはキャッシュキーの生成とGet/Setをスキップ(ショートサーキット)し、キャッシュのオーバーヘッドは一切ありません。
GetStatistics
現在の処理統計情報を返します。
func (p *Processor) GetStatistics() StatisticsStatistics の各フィールドの意味:
| フィールド | 説明 |
|---|---|
TotalProcessed | エラーなく完了した抽出回数、キャッシュヒットを含む |
CacheHits | キャッシュで直接ヒットした回数 |
CacheMisses | ミスして完全な処理が必要だった回数 |
ErrorCount | エラーを返した抽出回数 |
AverageProcessTime | 1 回あたりの抽出の平均実時間(TotalProcessed が 0 の場合は 0) |
stats := p.GetStatistics()
fmt.Printf("処理済み:%d, キャッシュヒット:%d\n",
stats.TotalProcessed, stats.CacheHits)ClearCache
キャッシュをクリアし、累積統計は保持します。
func (p *Processor) ClearCache()ResetStatistics
すべての統計カウンターをリセットします。
func (p *Processor) ResetStatistics()監査
GetAuditLog
監査ログエントリを取得します。
func (p *Processor) GetAuditLog() []AuditEntryClearAuditLog
監査ログをクリアします。
func (p *Processor) ClearAuditLog()ライフサイクル
Close
Processor が保持しているリソースを解放します。使用後に必ず呼び出してください。
func (p *Processor) Close() errorp, _ := html.New(cfg)
defer p.Close()
// ... p を使って抽出処理ライフサイクルのベストプラクティス
- シングルトン再利用:長稼働サービス(HTTP handler、worker)では Processor を 1 つ作成して並行リクエスト間で共有し、キャッシュと組み合わせて恩恵を最大化します。Processor 自体が並行安全なので、リクエストごとに新設する必要はありません。
defer Close():作成直後にdefer p.Close()を置き、異常パスでもバックグラウンドのクリーンアップ goroutine と監査リソースを解放できるようにします。Closeはキャッシュクリーンアップ goroutine を停止し、キャッシュをクリアし、監査 sink を閉じます。- クローズ後に使わない:
Close後にどのメソッドを呼び出してもErrProcessorClosedを返します。CloseはCompareAndSwapで冪等性を保証し、重複呼び出しは安全ですが無意味です。