Extract structured data

Pull the machine-readable metadata a page embeds – JSON-LD, Microdata, OpenGraph and Twitter cards, RDFa – with turbohtml.Document.structured_data(), the extruct successor.

Pull every embedded format in one call

Scrapers want the JSON-LD, Microdata, and OpenGraph/Twitter metadata a page embeds, the job of extruct or metadata_parser. structured_data() pulls every supported format in one walk:

doc = turbohtml.parse(
    '<head><meta property="og:title" content="Widgets"></head>'
    '<body><script type="application/ld+json">{"@type": "Product", "name": "Widget"}</script>'
    '<div itemscope itemtype="https://schema.org/Offer"><span itemprop="price">9.99</span></div></body>'
)
data = doc.structured_data()
print(data.json_ld)
print(data.opengraph)
item = data.microdata[0]
print(item.type, item.properties)
[{'@type': 'Product', 'name': 'Widget'}]
{'og:title': 'Widgets'}
https://schema.org/Offer {'price': ['9.99']}

structured_data() returns a StructuredData record whose fields you read by attribute. The per-format helpers json_ld(), opengraph(), microdata(), rdfa(), and dublin_core() return just one format each. JSON-LD blocks are parsed with the standard library json; a block that is not valid JSON, or whose payload is a scalar or null rather than a node object or array, is skipped, so every entry is a dict or list. The microformats field is reserved for a later phase and is an empty list for now.

RDFa and Dublin Core come off the same walk. RDFa yields RdfaItem records that mirror Microdata: property keys and the typeof IRIs expand against the in-scope @vocab and @prefix (the RDFa 1.1 initial context seeds the well-known prefixes), and Dublin Core gathers the dc.*/dcterms.* <meta> names:

doc = turbohtml.parse(
    '<head><meta name="dcterms.creator" content="Ada"></head>'
    '<body><div vocab="http://schema.org/" typeof="Person">'
    '<span property="name">Grace</span></div></body>'
)
person = doc.rdfa()[0]
print(person.type, person.properties)
print(doc.dublin_core())
['http://schema.org/Person'] {'http://schema.org/name': ['Grace']}
{'dcterms.creator': 'Ada'}

Find the publication date

Scrapers also want the article’s publication date, the job of htmldate. turbohtml.extract.dates() scores the same date signals – publication/modification <meta> tags, JSON-LD, <time> elements, and the URL – off the parsed page, and returns which one it read:

from turbohtml.extract import DateExtraction, dates

page = (
    '<meta property="article:published_time" content="2016-12-23">'
    '<meta property="article:modified_time" content="2017-02-01">'
)
published = dates(page, DateExtraction(original=True))
print(f"{published.date} (from the {published.signal})")
print(dates(page).date)
2016-12-23 (from the meta)
2017-02-01

The default prefers the modification date; DateExtraction(original=True) prefers the first-published one. A PublicationDate carries the formatted date and the signal it came from, or the call returns None when no date inside the [min_date, max_date] window is found. The htmldate migration guide maps the rest of the knobs.