DEV Community

carollin rolling
carollin rolling

Posted on

How to Check Whether a Backlink Exists in Raw HTML with Python

A page can mention a website without linking to it. The address might appear as plain text, inside a comment, or in a JavaScript object. Searching the source for a domain will find those mentions, too.

That distinction matters when you maintain a spreadsheet of directory listings, resource pages, or website references. Before marking a placement as a link, check whether an <a> element actually points to the destination.

Here is a small Python tool for that check. It reads an HTML file you have saved locally and prints matching anchor tags, including their source line numbers. It uses the standard library and makes no network requests.

A domain mention and an anchor are different things

This is a mention:

<p>Website: https://example.org/</p>
Enter fullscreen mode Exit fullscreen mode

This is an anchor with a destination:

<a href="https://example.org/" rel="ugc nofollow">Website</a>
Enter fullscreen mode Exit fullscreen mode

The href attribute is the important difference. Google's link best practices describe <a> elements with href attributes as the standard crawlable format. An anchor's presence alone does not establish its indexing or ranking effect.

For this walkthrough, the destination to look for is rummy91apk.app. The example concerns HTML link detection; it does not evaluate the destination's content, ownership, or downloads.

1. Save the source you want to inspect

Open the page that is supposed to contain the link. Use View Page Source, then save that source as a UTF-8 file named source.html.

Alternatively, use your browser's Network panel to inspect the main document's response and save its HTML. Record the final document URL after any redirects. The script needs that URL to resolve relative links.

Keep the distinction between the source and the rendered page in mind. The browser's Elements panel can show changes made by JavaScript after the initial response. This script examines only the file you provide.

2. Parse anchors instead of searching for strings

Save the following as audit_links.py:

import argparse
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urljoin, urlsplit


class AnchorParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.base_href = None
        self.anchors = []

    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        href = attrs.get("href")

        if tag == "base" and href is not None:
            if self.base_href is None:
                self.base_href = href

        if tag == "a" and href is not None:
            self.anchors.append(
                (self.getpos()[0], href, self.get_starttag_text())
            )


def url_key(url):
    parts = urlsplit(url)
    if parts.scheme not in {"http", "https"} or not parts.netloc:
        return None
    return (
        parts.scheme,
        parts.netloc.lower(),
        parts.path or "/",
        parts.query,
    )


def main():
    cli = argparse.ArgumentParser()
    cli.add_argument("html_file", type=Path)
    cli.add_argument("source_url")
    cli.add_argument("target_url")
    args = cli.parse_args()

    target = url_key(args.target_url)
    if target is None:
        cli.error("target_url must be an absolute HTTP or HTTPS URL")

    parser = AnchorParser()
    parser.feed(args.html_file.read_text(encoding="utf-8"))
    parser.close()

    base = urljoin(args.source_url, parser.base_href or "")
    matches = 0
    for line, href, markup in parser.anchors:
        if url_key(urljoin(base, href.strip())) == target:
            print(f"Line {line}: {markup}")
            matches += 1

    if not matches:
        print("No matching <a href> found in this HTML file.")


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Python's HTMLParser provides parsed tags and attributes. urljoin resolves relative URLs against the document URL, or against its first <base href> when one is present.

The script prints the original opening tag so you can inspect attributes such as rel without losing their values.

3. Run it against a small fixture first

Before checking a real source file, save this as source.html:

<!doctype html>
<p>Plain text: https://rummy91apk.app/</p>
<a href="https://rummy91apk.app/" rel="ugc nofollow">Website</a>
<script>const destination = "https://rummy91apk.app/";</script>
Enter fullscreen mode Exit fullscreen mode

Run:

python audit_links.py source.html https://listing.example/profile https://rummy91apk.app/
Enter fullscreen mode Exit fullscreen mode

The middle URL represents the page whose HTML you saved. For this local fixture, it is a placeholder. When auditing an actual page, replace it with that page's final URL.

Expected output:

Line 3: <a href="https://rummy91apk.app/" rel="ugc nofollow">
Enter fullscreen mode Exit fullscreen mode

Although the destination appears three times in the fixture, only one occurrence is an anchor. Remove that anchor and run the command again. The script should report that it found no matching <a href>.

4. Keep URL matching conservative

The script ignores fragments, so a URL ending in #details matches the same document URL without the fragment. It also treats an empty root path and / as equivalent.

Other distinctions remain intact:

  • HTTP and HTTPS are separate.
  • A hostname with www and one without it are separate.
  • Query strings remain part of the comparison.
  • /guide and /guide/ are separate paths.
  • Explicit port numbers remain in the comparison.

This avoids silently assuming that different addresses lead to the same place. The tool does not follow redirects or establish canonical URLs. It assumes ordinary, well-formed web URLs; it is not a general URL validator.

5. Report what the file actually proves

A useful audit note might read:

Matching anchor found in saved source, line 3. The opening tag includes rel="ugc nofollow".

That is more precise than recording only “backlink present.” It preserves the evidence and the link attributes without making a claim about SEO value.

If the script finds nothing, record that narrower result: no matching anchor found in the inspected HTML file. A link could still be inserted during rendering, point through a redirect URL, or appear in content missing from that response.

This is a source-markup check, not a browser visibility test. It can find anchors in hidden or inert markup, including templates. For a live-page assessment, inspect the rendered DOM and the surrounding content as well.

For a spreadsheet, useful columns are the source URL, target URL, inspection date, matching tag, rel value, and whether the evidence came from the original response or the rendered DOM. Those details make the result easier to reproduce when a page changes.

Top comments (0)