Closure HTML is an HTML4 parser, which offers an API quite like SAX, but trimmed down to the features that HTML actually needs.

You can find full details on HAX at the bottom of this page.

But the key feature of HAX is that you don't need to worry about it if you're using cxml-related libraries, because HAX and SAX events get translated between each other automatically.

Want to use WITH-XML-OUTPUT but write HTML syntax? No problem, just plug in chtml:make-string-sink instead of cxml:make-string-sink.

CL-USER>(cxml:with-xml-output (chtml:make-string-sink)
          (cxml:with-element "td"
            (cxml:attribute "nowrap" "nowrap")
            (cxml:text "Note lack of attribute value")))
"<td nowrap>Note lack of attribute value</td>"

Or to convert from/to HTML, use these:

(defun html2xml (str) (chtml:parse str (cxml:make-string-sink)))
(defun xml2html (str) (cxml:parse str (chtml:make-string-sink)))

Closure HTML documentation has more examples.

One detail which sometimes needs to be kept in mind: The translation turns HTML into XHTML, which means that the XHTML namespace is being used, not the empty namespace. (In the reverse direction, namespaces don't matter though.)

Symbols exported by HAX:

HAX:CONTENT-HANDLER (hax:abstract-handler) class
HAX:DEFAULT-HANDLER (hax:content-handler) class
HAX:START-ELEMENT (handler name attrs) generic function
HAX:END-ELEMENT (handler name) generic function
(handler name public-id system-id)
generic function
HAX:END-DOCUMENT (handler) generic function
HAX:CHARACTERS (handler data-string) generic function
HAX:UNESCAPED (handler data-string) generic function
HAX:COMMENT (handler data-string) generic function