npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@d-zero/beholder

v4.1.0

Published

Page-level scraper for web crawling and auditing

Readme

@d-zero/beholder

Puppeteer の Page を受け取り、単一ページのメタデータ・リンク・画像・ネットワークリソースに加え、メインコンテンツの定量データと desktop/mobile の scrollHeight を収集するインプロセス型スクレイパー。結果は ScrapeResult として戻り値で返却される(イベント経由ではない)。ブラウザ管理は呼び出し側の責任。

Installation

yarn add @d-zero/beholder

Usage

import Scraper from '@d-zero/beholder';
import { parseUrl } from '@d-zero/shared/parse-url';
import { launch } from 'puppeteer';

const browser = await launch();
const page = await browser.newPage();

const scraper = new Scraper();
scraper.on('changePhase', (event) => console.log(event.message));

const result = await scraper.scrapeStart(page, parseUrl('https://example.com'), {
	captureImages: true,
	isExternal: false,
});

if (result.type === 'success' && result.pageData) {
	console.log(result.pageData.meta.title);
	console.log(result.pageData.mainContents?.wordCount);
	console.log(result.pageData.scrollHeight);
}

設計判断(イベントではなく戻り値で返す理由、page のライフサイクル責務、リトライ機構など)は src/scraper.ts の JSDoc を参照。

取得フロー

flowchart TD
  goto[page.goto]
  ct{content-type is text/html?}
  early[both null]
  html[get HTML]
  ext{isExternal?}
  extOut[title-only; both null]
  idle[networkidle]
  mc["page.evaluate: mainContents"]
  anchors[getAnchorList / getMeta]
  imgs{captureImages?}
  fetchImg["#fetchImages + collect scrollHeight"]
  light["measureScrollHeight light path"]
  done[return PageData]

  goto --> ct
  ct -->|no| early
  ct -->|yes| html --> ext
  ext -->|yes| extOut
  ext -->|no| idle --> mc --> anchors --> imgs
  imgs -->|yes| fetchImg --> done
  imgs -->|no| light --> done

返却データ(PageData の追加分)

  • mainContents: メイン領域の定量(wordCount / bodyWordCount、見出し・画像・表・ボタン・iframe / video / audio / canvas の配列、検出した main の識別情報)。メイン未検出時もオブジェクトは返り、配列は空・wordCount は 0(非 HTML 等で取得しなかったときだけ null
  • scrollHeight: { desktop, mobile }(各 number | null)。未計測時はフィールド全体が null
  • 配列要素のフィールド詳細は型(MainContents* / ScrollHeightData)を参照

console ログ・未捕捉例外の収集

ScrapeResult.consoleLogsConsoleLogEntry[])に、内部ページ(isExternal: false)が出力した console メッセージ(全type)と、未捕捉例外・未処理の Promise rejection(page.on('pageerror')type: 'pageerror' として区別、スタックトレース付き)が格納される。resources と同様に pageUrl を持つ戻り値配列で、イベント経由では提供されない。result.type"success" / "skipped" / "error" のいずれであっても常に配列として存在する(該当ログがなければ空配列)ため、エラー発生時のデバッグにもそのまま使える。

for (const entry of result.consoleLogs) {
	console.log(entry.type, entry.text, entry.args);
}

DOM 文字列からメタ抽出(Puppeteer なし)

HTML 文字列を jsdom などでパースしてから Meta を取り出したい場合、extractMetaFromDocument を使う。Scraper が内部で呼ぶ collectHead → detectTags → classify パイプラインと同じ実装を再利用するため、戻り値の Meta 形状は scrapeStart と同一。DOM ライブラリ(jsdom 等)はユーザランドの責務。

import { extractMetaFromDocument } from '@d-zero/beholder';
import { JSDOM } from 'jsdom';

const url = 'https://example.com/';
const html = await (await fetch(url)).text();
const dom = new JSDOM(html, { url });

// `as unknown as Window` は jsdom の `DOMWindow` 型が lib.dom の `Window` と
// 構造的に完全一致しないための型キャスト。ランタイムでは互換。
const meta = await extractMetaFromDocument(dom.window as unknown as Window, {
	url,
	html,
});

console.log(meta.title);
console.log(meta.og?.image);
console.log(meta.tags.entries);

context.html を省略すると window.document.documentElement.outerHTML がフォールバックされる。ただし Wappalyzer の HTML パターンはスクリプト実行前の生 HTML に合わせて作られているので、可能なら取得直後の HTML 文字列を明示的に渡す方が検出が安定する。