url-metadata

repository·master·Indexed 41 days ago

https://github.com/laurengarcia/url-metadata

A versatile metadata scraping library for Node.js (v18.17+) and browsers. It extracts SEO tags, social media cards (Open Graph, Twitter), JSON-LD, and performance metrics from URLs, HTML strings, or Response objects. Features include a Proxy Mode to bypass bot detection, SSRF protection via request-filtering-agent, and customizable field extraction to optimize performance.

Tokens
7K
Snippets
15
Records
22
Agent score
51%

What's inside url-metadata

  1. Overview of url-metadata features

    master

    url-metadata is a tool for fetching a URL and scraping its metadata. It supports Node.js and browser environments.

    Key Capabilities:

    • Proxy Mode: Supports routing through unblocking services, headless JavaScript rendering, and taking screenshots.
    • Parser Mode: Allows passing in an HTML string or a Response object instead of a URL.
    • Automatic Detection: Handles charset detection and decoding.
    • Metadata Extraction: Extracts redirects, response headers, performance metrics, meta tags, hreflang, favicons, citations (Google Scholar spec), Open Graph (og:) tags, Twitter Card tags, JSON-LD, h1-h6 tags, and img tags.
    • Full Body Access: Can optionally return the full response body as an HTML string.
  2. Optimize performance and token efficiency

    master

    To reduce response size and skip unnecessary extraction work, use the following options:

    • fields: Request only the specific metadata fields you need for your use-case.
    • omitEmpty: true: Use this option to drop any fields that contain no data.
  3. Understand the return value of urlMetadata

    master

    The urlMetadata function returns a Promise that resolves to a JSON object containing extracted metadata.

    Key behaviors:

    • Redirects: The url field in the returned object represents the final destination in the request chain. The redirects field provides a count and a chain of RedirectHop objects.
    • Raw Content: Extracted content is returned raw and unsanitized. You are responsible for sanitizing all values (e.g., using DOMPurify for HTML or parameterized queries for SQL) before use.
    • Dynamic Fields: Any meta tags found on the page that are not part of the standard set are appended to the object as new fields.
    • Special Field Formats:
      • canonical: A string of the first <link rel="canonical"> found. canonicalUrls is an array of all canonical URLs found.
      • hreflang, favicons, and responseHeaders: Arrays of objects.
      • jsonld: An array of objects.
      • citation_* tags: These return as an array of strings to support the Google Scholar spec (e.g., citation_author: ['Author A', 'Author B']).
      • Other meta tags: Usually returned as strings with comma-separated values.
  4. Security protections in url-metadata

    master

    The package includes built-in protections against common web scraping vulnerabilities:

    • Infinite Redirect Loops: Controlled via the maxRedirects option (defaults to 10).
    • SSRF Attacks: Uses request-filtering-agent in Node.js v18+ to prevent Server-Side Request Forgery.
    • Memory-Exhaustion Attacks: Protects against gzip bombs and oversized responses using the size option. It is recommended to pair size with the timeout option to prevent slow or connection-holding responses.
    • Sensitive Header Leaks: Protects against leaking auth or cookies if a target URL redirects to a different host.
  5. Basic Usage of url-metadata

    master

    To extract metadata from a URL, import urlMetadata and call it with the target URL as the first argument. It returns a Promise that resolves to the metadata object. You can pass an optional configuration object as the second argument. The function supports both ESM import and CommonJS require.

    import urlMetadata from 'url-metadata';
    
    async function getMetadata(url) {
      try {
        return await urlMetadata(url); // pass options as 2nd argument
      } catch (err) {
        console.error(err);
      }
    }
    
    const metadata = await getMetadata('https://en.wikipedia.org/wiki/WHATWG');
  6. Install url-metadata

    master

    Install url-metadata via npm. The package works with Node.js versions >=18.17 or can be used in the browser when bundled. Example build configurations for Next.js, Vite, Webpack, and TypeScript are available in the repository's /example-* directories.

    npm install url-metadata --save
  7. Use Proxy Mode for blocked web pages

    master

    To bypass 403 errors or access restricted pages, use proxyUrl. Providing a proxyUrl triggers proxy mode, where the request is routed through a third-party service.

    • Timeout: In proxy mode, the default timeout automatically increases from 10s to 60s.
    • Parameters: Use proxyParams to pass vendor-specific query parameters (e.g., API keys) verbatim. If a vendor requires authentication via headers, use requestHeaders instead.
    • Limitations: This package only parses HTML. If the proxy returns JSON or CSV, it will throw unsupported content type.
    • Metadata: redirects, responseHeaders, and performance timing reflect the proxy's interaction, not the target server's.
    // Example with ScraperAPI
    const metadata = await urlMetadata('https://hardto.get', {
      proxyUrl: 'https://api.scraperapi.com/',
      proxyParams: {
        api_key: '<YOUR_API_KEY>'
      }
    });
    
    // Example with ScrapingAnt
    const metadata = await urlMetadata('https://hardto.get', {
      proxyUrl: 'https://api.scrapingant.com/v2/general',
      proxyParams: {
        'x-api-key': '<YOUR_API_KEY>'
      }
    });
  8. Use header-only mode for performance

    master

    If you only need transport/network information (like status codes, headers, or timing) and do not need to parse the HTML body, you can use header-only mode. This is highly efficient as it skips reading and parsing the response body.

    How to trigger it:

    1. Set fields to a subset of the network group (e.g., ['status', 'headers']).
    2. Ensure parseResponseObject is not used (this mode requires a live fetch).
    3. Ensure includeResponseBody is false (default).

    Note: In proxy mode, header-only mode is disallowed because the transport data would reflect the proxy, not the target.

  9. Parse an existing Response object

    master

    If you have already fetched a response (e.g., using fetch or axios), you can pass it directly to url-metadata to avoid a second network round-trip. This is done by passing the response object into the parseResponseObject option.

    Constraints:

    • You cannot use the network field (or any transport-related fields) when using parseResponseObject mode, as there is no live network transaction to probe.
    • The library will use the provided object to extract metadata based on the content.
    const response = await fetch('https://example.com');
    const metadata = await urlMetadata('https://example.com', {
      parseResponseObject: response
    });
  10. Troubleshoot common url-metadata issues

    master

    403, 404, or CAPTCHA errors

    If the target server blocks your request, use the built-in Proxy Mode to bypass bot detection.

    Proxy mode 'unsupported content type' errors

    This occurs if your proxy URL or parameters return something other than HTML. Ensure your proxy is configured to return HTML content.

    Proxy mode 'unsupported content type: text/x-component' errors

    This happens when using render: true (headless browser) on sites like Next.js App Router that return React Server Component payloads instead of HTML. This is a target site behavior. Try retrying without render: true or using a different proxy.

    'No fetch implementation found'

    This occurs in environments without a native fetch API (e.g., older browsers or Node.js < 18.17).

    • Solution: Upgrade your environment or downgrade to url-metadata version 2.5.0 which uses the request module.

    'DNS Lookup' errors

    The package includes an SSRF filtering agent that prevents calls to private, link-local, or reserved IP addresses.

    • Solution: Pass custom requestFilteringAgentOptions to modify or disable this behavior.

    'Response status code 0' or 'CORS' errors

    This indicates a network or protocol-level failure. Possible causes:

    • CORS: Change the mode option (e.g., cors, same-origin) or ensure the target server allows your origin.
    • Certificates/Protocol: Accessing https with invalid certificates or mixing http and https origins.
    • Browser Extensions: Ad-blockers or privacy protectors may be interfering.
  11. Parse an existing Response object or HTML string

    master

    Instead of providing a URL, you can pass null as the first argument and provide a Response object via the parseResponseObject option. This is useful if you have already fetched the content or have an HTML string available.

    // 1. Parsing a Response object from a fetch call
    try {
      const response = await fetch('https://en.wikipedia.org/wiki/WHATWG');
      const metadata = await urlMetadata(null, {
        parseResponseObject: response
      });
      console.log(metadata);
    } catch (err) {
      console.error(err);
    }
    
    // 2. Parsing an HTML string by creating a Response object
    const html = `<html><head><title>Metadata page</title></head><body></body></html>`;
    const response = new Response(html, {
      headers: {
        'Content-Type': 'text/html'
      }
    });
    const metadata = await urlMetadata(null, {
      parseResponseObject: response
    });
    console.log(metadata);
  12. Use TypeScript for typed metadata

    master

    By default, the return type Result is loose (any) to ensure compatibility. To get autocompletion and type safety, you should cast the result using one of the following interfaces:

    1. urlMetadata.KnownFields: Includes all standard known fields. Any extra meta tags found on the page will be accessible via any (e.g., metadata.foobar).
    2. urlMetadata.KnownFieldsStrict: A closed shape containing only known fields. This will cause compile errors if you attempt to access arbitrary page-specific meta tags.
    3. Partial<urlMetadata.KnownFields>: When using the fields or omitEmpty: true options, the return type automatically narrows to this partial type, making known fields optional.

    Refer to index.d.ts for the full list of exported interfaces including Options, HreflangTag, FaviconTag, Heading, ImgTag, RedirectHop, and UrlMetadataError.

    import urlMetadata from 'url-metadata';
    
    // 1. Known fields + arbitrary meta tags (loose)
    const metadata = await urlMetadata(url) as urlMetadata.KnownFields;
    metadata.title;      // string
    metadata.foobar;     // any
    
    // 2. Known fields ONLY (strict)
    const strict = await urlMetadata(url) as urlMetadata.KnownFieldsStrict;
    
    // 3. Automatic narrowing with options
    const partial = await urlMetadata(url, { fields: ['og', 'network'] });
    partial['og:title'];        // string | undefined
    partial.responseStatusCode; // number | undefined