From opengraph

opengraph-py3 latest releaseopengraph-py3 supported Pythonsopengraph-py3 licenseopengraph-py3 monthly downloadsopengraph-py3 total downloadsopengraph-py3 GitHub starsopengraph-py3 last commit

opengraph (the opengraph_py3 Python 3 fork of erikriver’s opengraph) reads a page’s Open Graph card. OpenGraph(html=...) builds a BeautifulSoup tree over html5lib, scans the head for og:-prefixed <meta> tags, and returns a dict subclass keyed by the property name minus its og: prefix, with is_valid() reporting whether the required tags are present. OpenGraph(url=...) fetches the page first; a scrape=True mode falls back to the <title> and body <img> when the og tags are missing.

It is a small, single-purpose scraping helper for lifting share-card metadata off a page. turbohtml serves that same shape from turbohtml.extract.opengraph(), a string entry point over turbohtml.Document.opengraph(): the og: <meta> tags are gathered in one C walk of the already parsed WHATWG tree, and the result is a frozen, fully typed OpenGraph mapping with the same prefix-stripped keys and the same is_valid check.

turbohtml vs opengraph

Dimension

turbohtml

opengraph

Scope

Full WHATWG parser; Open Graph extraction is one feature of many

Single-purpose Open Graph card reader

Feature breadth

og: mapping via opengraph(), plus twitter: tags and full structured data (JSON-LD, Microdata) off the same tree

og: tags only, with an optional scrape fallback to <title> and body <img>

Performance

One C walk of the parsed tree; 94-133x faster on a social card

BeautifulSoup tree over html5lib, scanned in Python

Typing

Frozen, fully typed read-only OpenGraph mapping, py.typed

Plain dict subclass, no shipped type hints

Dependencies

Zero runtime deps (self-contained C extension)

BeautifulSoup and html5lib, plus an HTTP client for url= mode

Maintenance

Actively developed single extension

Small community fork, low cadence

Feature overlap

The shared surface you can port one-to-one:

What turbohtml adds

  • The twitter: card, which opengraph never reads: turbohtml.Document.opengraph() returns the og: and twitter: tags together (with prefixes kept) off the same walk.

  • Every other structured-data format on the same tree: json_ld(), microdata(), and structured_data() for the whole set at once.

  • A read-only mapping that holds no reference back into the tree, so it outlives the document it came from.

  • One C walk under the per-tree critical section, 94-133x faster than the BeautifulSoup scan.

  • Zero third-party runtime dependencies: no BeautifulSoup, no html5lib to install or pin.

  • Full type coverage: OpenGraph is annotated and shipped with py.typed.

What opengraph has that turbohtml does not

  • URL fetching (OpenGraph(url=...)): no equivalent. turbohtml does not fetch; run your own HTTP client and pass the response body to opengraph().

  • Scrape fallback (scrape=True, which falls back to <title> and body <img> when the og tags are missing): no equivalent. Select those elements yourself with turbohtml.Node.find() when the card is incomplete.

  • ``to_json()`` / ``to_html()`` serializers: no equivalent. Build them from dict(og) yourself with json or your own markup.

Performance

Both libraries start from the raw HTML string, so each parses before it reads the tags: opengraph builds a BeautifulSoup tree over html5lib and scans the head in Python, where opengraph() parses to the WHATWG tree and gathers the og: tags in one C walk. On a social-card head, and on an 8 KiB article carrying that head, the walk runs 94 to 133 times faster:

social-card extraction

turbohtml

opengraph

head

1.9 µs

192 µs (101x)

article 8 KiB

25.3 µs

4.14 ms (164x)

Across hand-built cards covering the protocol’s basic, structured-image, and required-tag shapes, the two libraries return byte-identical mappings.

How to migrate

Swap the import and the constructor for a single function call on the HTML you already have.

opengraph

turbohtml

OpenGraph(html=html)

turbohtml.extract.opengraph()

OpenGraph(url=url)

opengraph() on the HTML you fetch yourself

og["title"] / og.get("title")

the same keys on OpenGraph

og.is_valid()

turbohtml.extract.OpenGraph.is_valid()

og.to_json() / og.to_html()

build these from dict(og) yourself

Before, with opengraph, the constructor parses and the dict subclass carries the prefix-stripped keys:

from opengraph import OpenGraph

og = OpenGraph(
    html="<head>"
    '<meta property="og:title" content="The Rock">'
    '<meta property="og:type" content="video.movie">'
    '<meta property="og:image" content="https://x/rock.jpg">'
    '<meta property="og:url" content="https://x/tt0117500/">'
    '<meta name="twitter:card" content="summary">'
    "</head>"
)
print(og["title"])
print(dict(og))
print(og.is_valid())

After, opengraph() returns the same shape from one C walk, as a read-only mapping that keeps no reference into the tree:

from turbohtml.extract import opengraph

og = opengraph(
    "<head>"
    '<meta property="og:title" content="The Rock">'
    '<meta property="og:type" content="video.movie">'
    '<meta property="og:image" content="https://x/rock.jpg">'
    '<meta property="og:url" content="https://x/tt0117500/">'
    '<meta name="twitter:card" content="summary">'
    "</head>"
)
print(og["title"])
print(dict(og))
print(og.is_valid())
The Rock
{'title': 'The Rock', 'type': 'video.movie', 'image': 'https://x/rock.jpg', 'url': 'https://x/tt0117500/'}
True

turbohtml does not fetch URLs; OpenGraph(url=...) becomes your HTTP client plus opengraph() on the response body. A card missing a required property reads back fine but fails the validity check:

og = opengraph('<head><meta property="og:title" content="Only a title"></head>')
print(og.get("title"), og.is_valid())
Only a title False

Gotchas and pitfalls

  • Keys drop the og: prefix (og["title"], not og["og:title"]), matching opengraph and diverging from the prefixed keys turbohtml.Document.opengraph() returns. Structured sub-properties keep their tail, so og:image:width reads as og["image:width"].

  • opengraph() drops the twitter: tags that turbohtml.Document.opengraph() also returns, because opengraph only reads og:-prefixed properties. Call opengraph() when you want the Twitter card too.

  • is_valid() requires the Open Graph protocol’s four basic properties (og:title, og:type, og:image, og:url). The opengraph_py3 fork additionally requires og:description, so a card with the four but no description is valid here and invalid there.

  • The mapping is read-only. opengraph returns a mutable dict subclass you can assign into; OpenGraph supports only the read surface, so copy into dict(og) first if you need to edit.

  • turbohtml takes an already-decoded str and applies the WHATWG encoding rules; opengraph in url= mode decodes the fetched bytes itself. Decode the response body to str before handing it over rather than passing raw bytes.