Package: unmarkd
NOTE: This project ismaintained.While it may seem inactive, it is because there is nothing to add. If you want an enhancement or want to file a bug report, please go to theissues.🔄 UnmarkdA markdown reverser.Unmarkd is aBeautifulSoup-poweredMarkdownreverser written in Python and for Python.WhyThis is created as aStackSearch(one of my other projects) dependency. In order to create a better API, I needed a way to reverse HTML. So I created this.There aresimilar projects(written in Ruby)but I have not found any written in Python (or for Python)later I found a popular library,html2text.InstallationYou know the drillpipinstallunmarkdComparisonTL;DR: Html2Text is fast. If you don't need much configuration, you could use Html2Text for the little speed increase.Click to expandSpeedTL;DR: Unmarkd < Html2TextHtml2Text is basically faster:(TheDOCvariable used can be foundhere)Unmarkd sacrifices speed forpower.Html2Text directly uses Python'shtml.parsermodule (in the standard library). On the other hand, Unmarkd uses the powerful HTML parsing library,beautifulsoup4. BeautifulSoup can be configured to use different HTML parsers. In Unmarkd, we configure it to use Python'shtml.parser, too.But another layer of code means more code is ran.I hope that's a good explanation of the speed difference.CorrectnessTL;DR: Unmarkd == Html2TextI actually foundtwohtml-to-markdown libraries. One of them wasTomdwhich had anincorrect implementation:It seems to be abandoned, anyway.Now with Html2Text and Unmarkd:In other words, theyworkConfigurabilityTL;DR: Unmarkd > Html2TextThis is Unmarkd's strong point.In Html2Text, you only have a limitedset of options.In Unmarkd, you can subclass theBaseUnmarkerand implement conversions for new tags (e.g.<q>), etc. In my opinion, it's much easier to extend and configure Unmarkd.Unmarkd was originally written as a StackSearch dependancy.Html2Text has no options for configuring parsing of code blocks. Unmarkd doesDocumentationHere's an example of basic usageimportunmarkdprint(unmarkd.unmark("<b>I <i>love</i> markdown!</b>"))# Output: **I *love* markdown!**or something more complex (shamelessly taken fromhere):importunmarkdhtml_doc=R"""<h1 id="sample-markdown">Sample Markdown</h1><p>This is some basic, sample markdown.</p><h2 id="second-heading">Second Heading</h2><ul><li>Unordered lists, and:<ol><li>One</li><li>Two</li><li>Three</li></ol></li><li>More</li></ul><blockquote><p>Blockquote</p></blockquote><p>And <strong>bold</strong>, <em>italics</em>, and even <em>italics and later <strong>bold</strong></em>. Even <del>strikethrough</del>. <a href="https://markdowntohtml.com">A link</a> to somewhere.</p><p>And code highlighting:</p><pre><code class="lang-js"><span class="hljs-keyword">var</span> foo = <span class="hljs-string">'bar'</span>;<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">baz</span><span class="hljs-params">(s)</span> </span>{<span class="hljs-keyword">return</span> foo + <span class="hljs-string">':'</span> + s;}</code></pre><p>Or inline code like <code>var foo = &#39;bar&#39;;</code>.</p><p>Or an image of bears</p><p><img src="http://placebear.com/200/200" alt="bears"></p><p>The end ...</p>"""print(unmarkd.unmark(html_doc))and the output:# Sample Markdown


    This is some basic, sample markdown.

    ## Second Heading-Unordered lists, and:1.One2.Two3.Three-More

    >Blockquote


    And**bold**,*italics*, and even*italics and later **bold***. Even~~strikethrough~~. [A link](https://markdowntohtml.com) to somewhere.

    And code highlighting:```jsvarfoo='bar';functionbaz(s){returnfoo+':'+s;}```Or inline code like`var foo = 'bar';`.

    Or an image of bears

    ![bears](http://placebear.com/200/200)

    The end ...ExtendingBrief OverviewMost functionality should be covered by theBasicUnmarkerclass defined inunmarkd.unmarkers.If you need to reverse markdown from StackExchange (as in the case for my other project), you may use theStackOverflowUnmarker(or it's alias,StackExchangeUnmarker), which is also defined inunmarkd.unmarkers.CustomizingIf the above two classes do not suit your needs, you can subclass theunmarkd.unmarkers.BaseUnmarkerabstract class.Currently, you canoptionallyoverride the following methods:detect_language(parameters:1)Parameters:html:bs4.BeautifulSoupWhen a fenced code block is approached, this function is called with a parameter of typebs4.BeautifulSouppassed to it; this is the element the code block was detected from (i.e.pre).This function is responsible for detecting the programming language (or returning''if none was detected) of the code block.Note: This method is different fromunmarkd.unmarkers.BasicUnmarker. It is simpler and does less checking/filteringBut Unmarkd is more flexible than that.Customizable constantsThere are currently 3 constants you may override:Formats:
NOTE: Use theFormat String SyntaxUNORDERED_FORMATThe string format of unordered (bulleted) lists.ORDERED_FORMATThe string format of ordered (numbered) lists.Miscellaneous:ESCAPABLESA container (preferably aset) of length-1strthat should be escapedCustomize converting HTML tagsFor an HTML tagsome_tag, you can customize how it's converted to markdown by overriding a method like so:fromunmarkd.unmarkersimportBaseUnmarkerclassMyCustomUnmarker(BaseUnmarker):deftag_some_tag(self,element)->str:...# parse code hereTo reduce code duplication, if your tag also has aliases (e.g.strongis an alias forbin HTML) then you may modify theTAG_ALIASES.If you really need to, you may also modifyDEFAULT_TAG_ALIASES. Be warned: if you do so,you will also need to implement the aliases(currentlyemandstrong).Common PatternsI find myself iterating through the children of the tag a lot. But that would lead to us needing to handle new tags, which could be anything. So here's the template/pattern I recommend:fromunmarkd.unmarkersimportBaseUnmarkerclassMyCustomUnmarker(BaseUnmarker):deftag_some_tag(self,element)->str:forchildinelement.children:ifnon_tag_output:=self.parse_non_tags(child):output+=non_tag_outputcontinueassertisinstance(element,bs4.Tag),type(element)...# Do whatever you want with the childUtility functions when overridingYou may use (when extending) the following functions:__parse, 2 parameters:html:bs4.BeautifulSoupThe html to unmark. This is used internally by theunmarkmethod and is slightly faster.escape:boolWhether to escape the characters inside the string or not. Defaults toFalse.escape: 1 parameter:string:strThe string to escape and make markdown-safewrap: 2 parameters:element:bs4.BeautifulSoupThe element to wrap.around_with:strThe character to wrap the element around with.WILL NOT BE ESCPAEDAnd, of course,tag_*anddetect_language.