htmlcompare
A Python library to check whether two HTML documents are "the same". It ignores
differences which do not change what the document means, so a test does not fail
just because a template engine emitted the attributes in a different order or
wrote margin: 0px instead of margin: 0.
Usage
import htmlcompare
result = htmlcompare.compare_html(expected_html, actual_html)
if not result:
print(result)
compare_html() returns a ComparisonResult which is true when both documents
are the same. result.differences contains one Difference per mismatch,
including the path to the node which caused it.
For tests the library provides two assertions:
from htmlcompare import assert_different_html, assert_same_html
assert_same_html('<div />', '<div></div>')
assert_different_html('<br>', '<p>')
Two files can also be compared on the command line:
htmlcompare expected.html actual.html
What is ignored
- whitespace between tags, and
<div />written as<div></div> - the order of HTML attributes, and the order of the CSS classes inside a
classattribute - an empty
styleorclassattribute, which is treated like an absent one - HTML comments
- the formatting of CSS.
styleattributes and<style>tags are parsed with a real CSS parser, so trailing semicolons, the order of declarations, the case of property, function and at-rule names, equivalent numeric spellings (.5px,0.5px), the case of dimension units (1PX,1px), the spelling of a URL (url(a.png),url("a.png")), a unit on a zero length and any whitespace CSS has no use for do not matter.
What is a difference
Something is only ignored when HTML and CSS guarantee that both spellings mean the same thing. Where the meaning can change, the documents differ:
- whitespace which CSS gives a meaning to:
.a .bis the descendant combinator and not.a.b,font-family: Arial Blackis notfont-family: ArialBlack - the order of declarations which can override each other, such as
backgroundandbackground-color - custom CSS syntax beginning with
--, whose names are case-sensitive, and custom-property values, whichvar()substitutes literally:--x: 0pxis not the same as--x: 0 - conditional comments in both their forms, because dropping one changes which clients display the content
Malformed CSS never raises an exception. Whatever the CSS parser can not represent is compared as source text instead, which may report a difference which is not one but never hides one.
Options
compare_html() and both assertions take a CompareOptions instance:
| option | default | effect |
|---|---|---|
ignore_comments |
True |
ignore HTML comments |
ignore_conditional_comments |
False |
ignore conditional comments as well |
compare_document_prefix |
False |
compare text before the <!DOCTYPE> (e.g. {# … #} metadata emitted by a template engine). Such a prefix is always preserved, it is just not compared by default. |
Limitations / Plans
No validation of conditional comments. Their condition is compared but not checked for validity. Not sure which library I can use here but at some point I'll likely need this as well.
JavaScript - for obvious reasons it will be impossible to implement perfect JS comparison but it might be possible to run some kind of "beautifier" to take care of insignificant stylistic changes. However I don't need this feature so this is unlikely to get implemented (unless contributed by someone else).
Custom hooks could help adapting the comparison to your specific needs. However I don't know which API would be best so this will wait until there are real-world use cases.
Better API: The current API is very minimal and implements just what I needed right now. I hope to improve the API once I use this project in more complex scenarios.
Other projects
xmldiff is a well established project to compare two XML documents. However it seems as if the code does not contain knowledge about specific HTML semantics (e.g. CSS, empty attributes, insignificant attribute order).
Misc
The code is licensed under the MIT license. It requires Python 3.9+.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file htmlcompare-0.5.0.tar.gz.
File metadata
- Download URL: htmlcompare-0.5.0.tar.gz
- Upload date:
- Size: 60.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b83a957f13c61661010bb23e981340b530597070a713bb38697e619852bef79
|
|
| MD5 |
e4d50833b3a0293b4c479a45e8027011
|
|
| BLAKE2b-256 |
448322a87a3495c2605394076282b668ea9c86c914fc94f7d1faeca9cfd3cb54
|
Provenance
The following attestation bundles were made for htmlcompare-0.5.0.tar.gz:
Publisher:
release.yml on FelixSchwarz/htmlcompare
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
htmlcompare-0.5.0.tar.gz -
Subject digest:
7b83a957f13c61661010bb23e981340b530597070a713bb38697e619852bef79 - Sigstore transparency entry: 2797299099
- Sigstore integration time:
-
Permalink:
FelixSchwarz/htmlcompare@6b71d3baf2961a734fb41c155e5d9e96e1c4de4f -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/FelixSchwarz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6b71d3baf2961a734fb41c155e5d9e96e1c4de4f -
Trigger Event:
push
-
Statement type:
File details
Details for the file htmlcompare-0.5.0-py3-none-any.whl.
File metadata
- Download URL: htmlcompare-0.5.0-py3-none-any.whl
- Upload date:
- Size: 43.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3403b27125b7da2bf4e04c37ffcfa3bc07791cf1b1eeddc5b9aebe6e64f7a7ec
|
|
| MD5 |
65a236b02ca6755038f40b250188c327
|
|
| BLAKE2b-256 |
e113e91df2087097ae7fcdad6b747ffce68c8c709ad60c2e16007691834f16e3
|
Provenance
The following attestation bundles were made for htmlcompare-0.5.0-py3-none-any.whl:
Publisher:
release.yml on FelixSchwarz/htmlcompare
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
htmlcompare-0.5.0-py3-none-any.whl -
Subject digest:
3403b27125b7da2bf4e04c37ffcfa3bc07791cf1b1eeddc5b9aebe6e64f7a7ec - Sigstore transparency entry: 2797299153
- Sigstore integration time:
-
Permalink:
FelixSchwarz/htmlcompare@6b71d3baf2961a734fb41c155e5d9e96e1c4de4f -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/FelixSchwarz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6b71d3baf2961a734fb41c155e5d9e96e1c4de4f -
Trigger Event:
push
-
Statement type: