############## From extruct ############## .. package-meta:: extruct scrapinghub/extruct `extruct `_ pulls the machine-readable metadata a page embeds: JSON-LD, Microdata, RDFa, microformats, the OpenGraph/Twitter card tags, and Dublin Core. You call one ``extract(html)`` entry point, optionally naming a ``syntaxes`` list, and get back a dict keyed by syntax name. Under the hood it builds an lxml tree once and then runs a separate extractor per syntax you asked for, each on top of ``lxml``, ``w3lib``, ``mf2py``, and an RDFa/DublinCore stack. It is a scraping-pipeline staple, developed by Zyte (formerly Scrapinghub) and used alongside Scrapy to lift structured product, article, and event data off crawled pages. turbohtml covers the same ground from the other direction: it is a WHATWG HTML parser first, and :meth:`~turbohtml.Document.structured_data` reads every supported format off the already parsed tree in a single C walk, so extraction is not a second tree build but a walk of the one you already have. ********************** turbohtml vs extruct ********************** .. list-table:: :header-rows: 1 :widths: 20 40 40 - - Dimension - turbohtml - extruct - - Scope - Full WHATWG parser; structured data is one feature of many - Focused metadata extractor, one job - - Feature breadth - JSON-LD, Microdata, OpenGraph/Twitter, RDFa, Dublin Core; microformats reserved (empty list) - JSON-LD, Microdata, RDFa, OpenGraph, microformats, Dublin Core - - Performance - One C walk of the parsed tree; ~9-11x faster on a combined page - Builds an lxml tree, then one extractor per requested syntax - - Typing - Frozen, fully typed records (:class:`~turbohtml.StructuredData`, :class:`~turbohtml.MicrodataItem`), ``py.typed`` - Plain dicts and lists, no shipped type hints - - Dependencies - Zero runtime deps (self-contained C extension) - ``lxml``, ``w3lib``, ``mf2py``, plus an RDFa/DublinCore stack - - Maintenance - Actively developed single extension - Mature, Zyte-backed, slower cadence Feature overlap =============== The shared surface you can port one-to-one: - ``extruct.extract(html)`` -> :meth:`~turbohtml.Document.structured_data`, one call gathering every format. - ``syntaxes=["json-ld"]`` -> :meth:`~turbohtml.Document.json_ld`, each ``' '
9.99
' ) data = extruct.extract(html, syntaxes=["json-ld", "opengraph", "microdata"]) print(data["json-ld"]) print(data["opengraph"]) print(data["microdata"]) After, one walk returns a typed record whose fields you read by attribute: .. testcode:: from turbohtml import parse doc = parse( '' '' '
9.99
' ) data = doc.structured_data() print(data.json_ld) print(data.opengraph) offer = data.microdata[0] print(offer.type, offer.properties) .. testoutput:: [{'@type': 'Product'}] {'og:title': 'Widget'} https://schema.org/Offer {'price': ['9.99']} The per-format helpers return the same plain objects, holding no reference back into the tree, so you can keep them after the document is gone: .. testcode:: doc = parse('') print(doc.json_ld()) .. testoutput:: [{'@type': 'Article', 'name': 'Hi'}] ********************** Gotchas and pitfalls ********************** - The OpenGraph result is a flat ``{key: value}`` mapping, not ``extruct``'s list of namespaced property tuples, and ``og:`` and ``twitter:`` tags share the one mapping because pages mix the ``property`` and ``name`` attributes freely. When a key repeats, the last occurrence wins; read :meth:`~turbohtml.Document.json_ld` when you need every occurrence of a repeated key. - :attr:`~turbohtml.StructuredData.microformats` is a later phase: :meth:`~turbohtml.Document.structured_data` returns it as an empty list today, so code that reads it will not break when it lands, but the values are not there yet. RDFa and Dublin Core are populated, in :attr:`~turbohtml.StructuredData.rdfa` and :attr:`~turbohtml.StructuredData.dublin_core`. - RDFa ``property`` keys and ``typeof`` IRIs are expanded against the in-scope ``@vocab``/``@prefix`` (with the RDFa 1.1 initial context seeding the common prefixes); an undeclared prefix or a bare term with no vocabulary in scope stays verbatim. Pass ``base_url=`` to :meth:`~turbohtml.Document.rdfa` to absolutize the ``resource``/``href``/``src`` IRIs. - A JSON-LD block whose body is not valid JSON is skipped rather than raising, matching ``extruct``'s default error handling; ``extruct``'s ``errors="strict"`` mode has no turbohtml equivalent. Pass the raw ``