######################## Parse HTML into a tree ######################## Turn markup into a navigable tree with :func:`turbohtml.parse`: parse a whole document, feed one arriving in chunks, or parse a fragment in a given context. ************************************* Parse a document arriving in chunks ************************************* When a document arrives over a stream you do not have to buffer the whole thing before parsing. Feed each chunk to an :class:`turbohtml.IncrementalParser` and call ``close()`` for the finished :class:`~turbohtml.Document`; the parser holds only the bytes it has not yet consumed, never the whole source, and the result is identical to parsing the joined string with :func:`turbohtml.parse`: .. testcode:: parser = turbohtml.IncrementalParser() for chunk in ("
caf") parser.feed("é
".encode()) document = parser.close() print(document.find("p").text) .. testoutput:: café **************************************** Inspect the parse errors of a document **************************************** :func:`turbohtml.parse` recovers from malformed markup the way a browser does and records each WHATWG parse error it recovered from on :attr:`~turbohtml.Document.errors`. Each :class:`~turbohtml.ParseError` carries the spec ``code`` and the source position (1-based ``line``, 0-based ``col``); a well-formed document yields an empty list: .. testcode:: import turbohtml document = turbohtml.parse("") for error in document.errors: print(f"{error.code} at {error.line}:{error.col}") .. testoutput:: duplicate-attribute at 1:6 To fail instead of recover (in a linter or a strict ingest pipeline), pass ``strict=True`` and catch :class:`~turbohtml.HTMLParseError`, whose ``error`` attribute is the first :class:`~turbohtml.ParseError`: .. testcode:: try: turbohtml.parse("ax
", positions=False).find("p").source_line) print(turbohtml.Element("div").position) .. testoutput:: None None