<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucy Green</title>
    <description>The latest articles on DEV Community by Lucy Green (@lucy-green).</description>
    <link>https://dev.to/lucy-green</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948792%2Ff87afe80-7cce-4547-aae8-742cf98586e3.png</url>
      <title>DEV Community: Lucy Green</title>
      <link>https://dev.to/lucy-green</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucy-green"/>
    <language>en</language>
    <item>
      <title>Semrush vs SerpSpur: A Practical Comparison of SEO Tools for Speed, Depth, and Workflow</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Wed, 07 Oct 2026 05:48:29 +0000</pubDate>
      <link>https://dev.to/lucy-green/semrush-vs-serpspur-a-practical-comparison-of-seo-tools-for-speed-depth-and-workflow-1681</link>
      <guid>https://dev.to/lucy-green/semrush-vs-serpspur-a-practical-comparison-of-seo-tools-for-speed-depth-and-workflow-1681</guid>
      <description>&lt;p&gt;Semrush is the default choice for many SEO teams, and for good reason. But default doesn't mean optimal. If your workflow involves jumping between five dashboards just to answer one client question, the tool is adding friction instead of removing it.&lt;/p&gt;

&lt;p&gt;Here's the practical breakdown.&lt;/p&gt;

&lt;p&gt;Keyword research: Semrush wins on raw database size. SerpSpur focuses on intent clustering and SERP-driven suggestions, which means fewer exports and faster briefs. If you need historical volume depth across obscure regions, Semrush is stronger. If you need to move from keyword to content plan in one screen, SerpSpur is faster.&lt;/p&gt;

&lt;p&gt;Rank tracking: Both handle daily tracking. Semrush offers more granular reporting and integrations. SerpSpur's advantage is a cleaner project view where rankings, competitors, and alerts sit on one page instead of three.&lt;/p&gt;

&lt;p&gt;Site audits: Semrush's audit is thorough but heavy. SerpSpur runs lighter crawls with prioritized fixes ranked by traffic impact, useful when you're auditing 40 client sites a month.&lt;/p&gt;

&lt;p&gt;Competitor analysis and backlinks: Semrush has the larger link index, no debate. SerpSpur surfaces competitor gaps and link opportunities with less noise, which matters more for small teams than raw index size.&lt;/p&gt;

&lt;p&gt;Usability and value: This is where the split becomes clear. Semrush is a platform you learn. SerpSpur is a workspace you use. For agencies running lean, the time saved on navigation is the real cost difference.&lt;/p&gt;

&lt;p&gt;Pick Semrush for enterprise depth. Pick SerpSpur for speed and workflow clarity.&lt;a href="https://serpspur.com/" rel="noopener noreferrer"&gt;https://serpspur.com/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SerpSpur vs Semrush: Which SEO Tool Fits Your Workflow?</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Tue, 06 Oct 2026 07:23:11 +0000</pubDate>
      <link>https://dev.to/lucy-green/serpspur-vs-semrush-which-seo-tool-fits-your-workflow-o2b</link>
      <guid>https://dev.to/lucy-green/serpspur-vs-semrush-which-seo-tool-fits-your-workflow-o2b</guid>
      <description>&lt;p&gt;The SEO tooling trap is real. You start with a single project, then add keyword research, rank tracking, and site audits. Six months later you are juggling five dashboards, paying enterprise pricing, and spending more time configuring tools than doing SEO.&lt;/p&gt;

&lt;p&gt;Semrush is a powerful suite. It covers keyword research, competitor analysis, backlink auditing, and rank tracking in one place. For large agencies with dedicated analysts, that depth is a legitimate advantage. The friction appears when you need fast answers: the interface is dense, workflows take multiple clicks, and costs scale quickly as you add seats and projects.&lt;/p&gt;

&lt;p&gt;SerpSpur takes a different path. Instead of layering every feature into one heavy platform, it focuses on the core jobs SEOs actually repeat daily. Keyword research returns clean, prioritized clusters without noise. Rank tracking updates fast and stays readable. Site audits surface issues in a flat list you can act on immediately rather than a report you have to decode. Competitor and backlink views are streamlined for quick gap analysis, not exhaustive data dumps.&lt;/p&gt;

&lt;p&gt;The practical difference is workflow. Semrush rewards depth and patience. SerpSpur rewards speed and clarity. If you run a lean team, manage multiple client sites, or just want to stop context-switching between modules, the lighter model removes real overhead.&lt;/p&gt;

&lt;p&gt;Neither tool is universally better. Pick Semrush if you need its full data ecosystem and have the team to use it. Pick SerpSpur if you want the essential SEO stack without the weight. Test both on a live project before committing.&lt;a href="https://serpspur.com/" rel="noopener noreferrer"&gt;https://serpspur.com/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Google PageRank Is Dead: Why Legacy Scores No Longer Tell the Full SEO Story</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Mon, 05 Oct 2026 05:41:46 +0000</pubDate>
      <link>https://dev.to/lucy-green/google-pagerank-is-dead-why-legacy-scores-no-longer-tell-the-full-seo-story-bm8</link>
      <guid>https://dev.to/lucy-green/google-pagerank-is-dead-why-legacy-scores-no-longer-tell-the-full-seo-story-bm8</guid>
      <description>&lt;p&gt;Legacy Google PageRank data still lingers in old SEO spreadsheets, and plenty of teams keep referencing it during authority audits. The problem is that the toolbar has been dead for years, so anyone quoting a PR4 or PR6 is working from stale signals that no longer reflect how a domain performs today.&lt;/p&gt;

&lt;p&gt;Here is a practical way to handle it. Use a Google PageRank checker to pull whatever historical PageRank value is still archived for a domain, then immediately place it next to a live authority metric. The SERPSpur Google PageRank Checker does exactly this: it surfaces legacy PR data and pairs it with SERPSpur Trust Rate in a single view, so you can see how a domain's old reputation lines up with its current standing.&lt;/p&gt;

&lt;p&gt;The workflow is simple. First, run the domain through the checker to capture its archived PageRank. Second, read the Trust Rate alongside it to gauge present-day link equity and credibility. Third, use the gap between the two as your signal. A site with high legacy PR but a weak Trust Rate has likely lost authority or changed ownership. A site with modest legacy PR but a strong Trust Rate is climbing.&lt;/p&gt;

&lt;p&gt;Treat legacy PageRank as historical context, not a ranking factor. Pair it with a modern trust metric, and your authority analysis gets sharper without the guesswork.&lt;a href="https://serpspur.com/tool/google-pagerank-checker/" rel="noopener noreferrer"&gt;https://serpspur.com/tool/google-pagerank-checker/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop Using a Different Converter for Every File Format: Build a Smarter Conversion Workflow</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Mon, 05 Oct 2026 05:40:19 +0000</pubDate>
      <link>https://dev.to/lucy-green/stop-using-a-different-converter-for-every-file-format-build-a-smarter-conversion-workflow-126</link>
      <guid>https://dev.to/lucy-green/stop-using-a-different-converter-for-every-file-format-build-a-smarter-conversion-workflow-126</guid>
      <description>&lt;p&gt;Most file converters you find online are built for a single format pair. Need PDF to DOCX? One site. WebP to PNG? Another. CSV to XLSX? A third, probably with a file size cap and a watermark. The friction adds up fast when you're converting across a dozen formats in a week.&lt;/p&gt;

&lt;p&gt;The practical setup I'd recommend: keep a local toolkit for the conversions you run constantly, and use a universal converter for the long tail. Local first, because it's faster and your data never leaves the machine.&lt;/p&gt;

&lt;p&gt;bash&lt;/p&gt;

&lt;h1&gt;
  
  
  Batch image conversion with ImageMagick
&lt;/h1&gt;

&lt;p&gt;mogrify -format webp -quality 85 *.png&lt;/p&gt;

&lt;h1&gt;
  
  
  Documents via LibreOffice headless
&lt;/h1&gt;

&lt;p&gt;libreoffice --headless --convert-to docx *.pdf&lt;/p&gt;

&lt;h1&gt;
  
  
  CSV to XLSX with pandas
&lt;/h1&gt;

&lt;p&gt;python -c "import pandas as pd; pd.read_csv('in.csv').to_excel('out.xlsx', index=False)"&lt;/p&gt;

&lt;p&gt;That handles 90% of routine work. The remaining 10% is the awkward stuff—an HTML invoice that needs to become CSV, a format you've never heard of, a one-off from a client. That's where a universal converter that covers 200+ formats including PDF, DOCX, WebP, and CSV earns its place, because installing a new library for a single conversion is a waste of an afternoon.&lt;/p&gt;

&lt;p&gt;Two things to check on any converter before you rely on it: does it preserve metadata (EXIF, document properties) or strip it, and does it handle batch input or force you to click through files one at a time? Batch support matters more than raw speed once you're past a handful of files. And if the files are sensitive, local conversion sidesteps the trust question entirely.&lt;a href="https://serpspur.com/tool/all-type-free-file-converter/" rel="noopener noreferrer"&gt;https://serpspur.com/tool/all-type-free-file-converter/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SerpSpur vs Semrush: Which SEO Tool Fits Your Everyday SEO Workflow?</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Sun, 04 Oct 2026 17:27:24 +0000</pubDate>
      <link>https://dev.to/lucy-green/serpspur-vs-semrush-which-seo-tool-fits-your-everyday-seo-workflow-f42</link>
      <guid>https://dev.to/lucy-green/serpspur-vs-semrush-which-seo-tool-fits-your-everyday-seo-workflow-f42</guid>
      <description>&lt;p&gt;Comparing SEO tools is tricky because the honest answer is usually "it depends on the job." Here's a fair look at SerpSpur vs Semrush across the features that actually matter day to day.&lt;/p&gt;

&lt;p&gt;Keyword research — Semrush has a massive keyword database with deep historical data. SerpSpur covers volume, difficulty, and SERP intent well, and it's fast for quick lookups. If you're doing heavy market sizing, Semrush wins. For everyday targeting, SerpSpur holds up.&lt;/p&gt;

&lt;p&gt;Rank tracking — Both track positions. SerpSpur's interface is lighter and updates are quick; Semrush offers more granular historical charts and segmentation.&lt;/p&gt;

&lt;p&gt;Site audits — SerpSpur's audit surfaces technical issues, broken links, indexing problems, and metadata gaps cleanly. Semrush's crawler is more configurable for enterprise needs.&lt;/p&gt;

&lt;p&gt;Backlinks — Semrush's index is genuinely hard to replicate. SerpSpur's Bulk Backlink Exporter is great for organizing and processing link data, but if link forensics is your core work, Semrush is the deeper well.&lt;/p&gt;

&lt;p&gt;Usability &amp;amp; value — This is where SerpSpur pulls ahead for most teams. Modern UI, faster workflows, no complexity tax on the 90% of tasks that don't need enterprise depth.&lt;/p&gt;

&lt;p&gt;A practical setup: SerpSpur for daily monitoring, audits, and reporting; Semrush for quarterly deep dives. Test it yourself — export a keyword list from Semrush into SerpSpur and see if the data holds for your niche.&lt;/p&gt;

&lt;p&gt;Try it: &lt;a href="https://serpspur.com" rel="noopener noreferrer"&gt;https://serpspur.com&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Your Rankings Aren’t Broken—Your Technical SEO Debt Is</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Thu, 10 Sep 2026 11:27:07 +0000</pubDate>
      <link>https://dev.to/lucy-green/your-rankings-arent-broken-your-technical-seo-debt-is-46ji</link>
      <guid>https://dev.to/lucy-green/your-rankings-arent-broken-your-technical-seo-debt-is-46ji</guid>
      <description>&lt;p&gt;You fixed the redirect chain, updated the sitemap, and resubmitted. Two weeks later, half your product pages are still sitting in "Discovered - currently not indexed" and a chunk of revenue-driving URLs return 404s from an old migration nobody documented.&lt;/p&gt;

&lt;p&gt;This is the real cost of technical SEO debt: it hides in plain sight. Google quietly drops pages, crawl budget gets wasted on parameter junk, and duplicate title tags split relevance across near-identical URLs. You do not notice until traffic dips and the culprit is buried three layers deep in a crawl report.&lt;/p&gt;

&lt;p&gt;SerpSpur Site Audit approaches this as a diagnostic pass, not a vanity score. Point it at your domain and it surfaces the issues that actually move rankings: broken internal and external links, indexing blockers like stray noindex tags and canonical conflicts, missing or duplicated metadata, thin pages that dilute topical authority, and orphaned URLs no internal link reaches.&lt;/p&gt;

&lt;p&gt;What makes it practical is the ordering. Instead of dumping 40,000 warnings, it groups findings by impact so you fix the handful of problems affecting crawl efficiency and indexation first. Each issue comes with the affected URL, the reason it matters, and the fix. You can rerun the audit after deploying to confirm the count drops.&lt;/p&gt;

&lt;p&gt;A useful routine: audit monthly, and always immediately after a migration, CMS upgrade, or bulk content publish. Most ranking recoveries are not new content plays. They are old technical debt finally cleared.&lt;/p&gt;

&lt;p&gt;Run the audit on your site, fix the top ten findings, then rerun. That loop beats speculation every time.&lt;a href="https://serpspur.com/" rel="noopener noreferrer"&gt;https://serpspur.com/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop Overpaying for SEO Tools: A Faster, Smarter Alternative to Expensive SEO Suites</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Tue, 08 Sep 2026 16:18:29 +0000</pubDate>
      <link>https://dev.to/lucy-green/stop-overpaying-for-seo-tools-a-faster-smarter-alternative-to-expensive-seo-suites-3k8a</link>
      <guid>https://dev.to/lucy-green/stop-overpaying-for-seo-tools-a-faster-smarter-alternative-to-expensive-seo-suites-3k8a</guid>
      <description>&lt;p&gt;Tired of paying $200+ a month just to peek at your competitor’s keywords? You are not alone. The real pain point isn’t the data—it’s the bloat. Legacy SEO suites force you to navigate clunky dashboards and pay for modules you never touch, just to answer one simple question: “Why is my traffic flat?”&lt;/p&gt;

&lt;p&gt;Here is the fix. Stop renting a legacy monolith and start using a modular, agile toolkit. SerpSpur gives you the exact metrics that matter—organic traffic volume, keyword difficulty, backlink gaps, and on-page health—without the enterprise bloat. Instead of waiting for a weekly PDF report, you get real-time site audits that flag technical issues like broken links or slow CLS scores instantly.&lt;/p&gt;

&lt;p&gt;The actionable workflow? Run a domain audit first. SerpSpur crawls every page to expose thin content and duplicate meta tags. Then, pivot to the competitor gap analysis. Enter your top rival’s URL, filter by “keywords they rank for but you don’t,” and export that list to your content calendar. Done. No learning curve, no training videos.&lt;/p&gt;

&lt;p&gt;Modern SEO does not require a legacy price tag. It requires speed and specificity. Pull up the live dashboard, spot the drop in branded traffic, and drill down to the exact referring page that lost its backlink.&lt;/p&gt;

&lt;p&gt;Stop overpaying for complexity. Start analyzing with clarity. Try SerpSpur free and see your traffic blind spots today.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Google PageRank Is Dead: Stop Using Outdated Scores to Evaluate Domains</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Tue, 08 Sep 2026 07:52:30 +0000</pubDate>
      <link>https://dev.to/lucy-green/google-pagerank-is-dead-stop-using-outdated-scores-to-evaluate-domains-284p</link>
      <guid>https://dev.to/lucy-green/google-pagerank-is-dead-stop-using-outdated-scores-to-evaluate-domains-284p</guid>
      <description>&lt;p&gt;Google killed the public PageRank API back in 2016, yet countless SEOs still chase that outdated 0-10 metric. You check an old directory, see a 4, and assume the domain is solid. But that number is frozen in time. It doesn't reflect current link velocity, content quality, or algorithmic penalties.&lt;/p&gt;

&lt;p&gt;The real problem is trust. You cannot make a confident decision about a domain or competitor using a relic. You need a live signal that correlates with how Google actually evaluates authority today.&lt;/p&gt;

&lt;p&gt;That is why we built the Google PageRank Checker with a modern twist. Instead of forcing you to interpret a dead number, it pulls the historical PageRank data for context and pairs it instantly with the SERPSpur Trust Rate. This is our proprietary, real-time score that analyzes backlink profiles, domain age, and current ranking signals.&lt;/p&gt;

&lt;p&gt;The workflow is simple. Paste any URL, hit check, and you get a side-by-side view. See if the legacy authority matches the current reality. A high historical PageRank with a low Trust Rate exposes a decayed or penalized site. A low historical score with a high Trust Rate reveals a rising player you would otherwise ignore.&lt;/p&gt;

&lt;p&gt;Stop guessing with outdated data. Get the historical context, but make your decision on the live metric that matters now. Check any domain at SERPSpur.&lt;a href="https://serpspur.com/tool/google-pagerank-checker/" rel="noopener noreferrer"&gt;https://serpspur.com/tool/google-pagerank-checker/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop Fighting Invoice Formats: Convert PDFs, Excel Files, and HTML to CSV Without the Headaches</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Thu, 06 Aug 2026 06:34:45 +0000</pubDate>
      <link>https://dev.to/lucy-green/stop-fighting-invoice-formats-convert-pdfs-excel-files-and-html-to-csv-without-the-headaches-5018</link>
      <guid>https://dev.to/lucy-green/stop-fighting-invoice-formats-convert-pdfs-excel-files-and-html-to-csv-without-the-headaches-5018</guid>
      <description>&lt;p&gt;Anyone who's worked with financial data knows that invoice processing is rarely as simple as it sounds.&lt;/p&gt;

&lt;p&gt;One client sends PDFs.&lt;/p&gt;

&lt;p&gt;Another sends old Excel spreadsheets.&lt;/p&gt;

&lt;p&gt;Someone else exports HTML reports from a legacy accounting system.&lt;/p&gt;

&lt;p&gt;Yet your accounting software, reporting dashboard, or Python script only wants one thing: a clean CSV file.&lt;/p&gt;

&lt;p&gt;That's where the real work begins.&lt;/p&gt;

&lt;p&gt;Why Invoice Parsing Is Harder Than It Looks&lt;/p&gt;

&lt;p&gt;Developers usually start by writing a parser.&lt;/p&gt;

&lt;p&gt;For PDFs, libraries like pdfplumber, camelot, or tabula-py are popular choices.&lt;/p&gt;

&lt;p&gt;They work well—until they don't.&lt;/p&gt;

&lt;p&gt;PDFs aren't designed for structured data extraction. Tables often contain:&lt;/p&gt;

&lt;p&gt;Misaligned columns&lt;br&gt;
Empty cells&lt;br&gt;
Broken rows&lt;br&gt;
Unexpected whitespace&lt;br&gt;
Missing headers&lt;/p&gt;

&lt;p&gt;A simple extraction frequently turns into a cleanup project.&lt;/p&gt;

&lt;p&gt;Here's a basic example using pdfplumber.&lt;/p&gt;

&lt;p&gt;import pdfplumber&lt;br&gt;
import pandas as pd&lt;/p&gt;

&lt;p&gt;with pdfplumber.open("invoice.pdf") as pdf:&lt;br&gt;
    page = pdf.pages[0]&lt;br&gt;
    table = page.extract_table()&lt;/p&gt;

&lt;p&gt;clean_rows = []&lt;/p&gt;

&lt;p&gt;for row in table:&lt;br&gt;
    clean_rows.append([&lt;br&gt;
        cell.strip() if cell else ""&lt;br&gt;
        for cell in row&lt;br&gt;
    ])&lt;/p&gt;

&lt;p&gt;df = pd.DataFrame(clean_rows)&lt;/p&gt;

&lt;p&gt;df.to_csv("output.csv", index=False)&lt;/p&gt;

&lt;p&gt;This works for one invoice.&lt;/p&gt;

&lt;p&gt;Now imagine processing 100 invoices from multiple clients.&lt;/p&gt;

&lt;p&gt;Suddenly you're maintaining different parsers for:&lt;/p&gt;

&lt;p&gt;PDF&lt;br&gt;
XLS&lt;br&gt;
XLSX&lt;br&gt;
HTML&lt;br&gt;
Different invoice layouts&lt;br&gt;
Various encodings&lt;/p&gt;

&lt;p&gt;Instead of analyzing financial data, you're debugging file formats.&lt;/p&gt;

&lt;p&gt;A Simpler Workflow&lt;/p&gt;

&lt;p&gt;For one-off imports and bulk migrations, I prefer converting everything into a consistent CSV format first.&lt;/p&gt;

&lt;p&gt;Once every file looks the same, the rest of the workflow becomes much easier.&lt;/p&gt;

&lt;p&gt;Recently I tested the Invoice to CSV Converter from SERPSpur, which supports PDF, XLS, XLSX, and HTML files without requiring custom parsing logic.&lt;/p&gt;

&lt;p&gt;Instead of spending time cleaning extracted tables, I received a structured CSV that was immediately usable.&lt;/p&gt;

&lt;p&gt;Working with the Converted Data&lt;/p&gt;

&lt;p&gt;Once the invoices are converted, you can focus on analysis instead of extraction.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;import pandas as pd&lt;/p&gt;

&lt;p&gt;df = pd.read_csv("converted_invoices.csv")&lt;/p&gt;

&lt;p&gt;df["Total"] = (&lt;br&gt;
    df["Total"]&lt;br&gt;
      .str.replace("$", "", regex=False)&lt;br&gt;
      .astype(float)&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;vendor_totals = (&lt;br&gt;
    df.groupby("Vendor")["Total"]&lt;br&gt;
      .sum()&lt;br&gt;
      .sort_values(ascending=False)&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;print(vendor_totals)&lt;/p&gt;

&lt;p&gt;Now you can build reports, automate bookkeeping, or import everything into your accounting system with minimal effort.&lt;/p&gt;

&lt;p&gt;Why This Approach Saves Time&lt;/p&gt;

&lt;p&gt;Converting invoices into a standardized CSV upfront has several advantages:&lt;/p&gt;

&lt;p&gt;One consistent format regardless of the original file type&lt;br&gt;
Less custom parsing code&lt;br&gt;
Fewer data-cleaning issues&lt;br&gt;
Easier automation with Pandas&lt;br&gt;
Faster imports into databases or accounting software&lt;/p&gt;

&lt;p&gt;For occasional migrations, this is often much more efficient than building a parser for every document format you'll encounter.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Writing parsers can be fun.&lt;/p&gt;

&lt;p&gt;Cleaning messy invoice exports usually isn't.&lt;/p&gt;

&lt;p&gt;If your goal is simply to move data from invoices into a spreadsheet, database, or analytics pipeline, it's often better to normalize the files first and spend your time working with the data—not fixing extraction errors.&lt;/p&gt;

&lt;p&gt;For developers, that means fewer edge cases, cleaner code, and a workflow that's much easier to maintain.maintain.&lt;a href="https://serpspur.com/tool/invoice-pdf-to-csv-converter/" rel="noopener noreferrer"&gt;https://serpspur.com/tool/invoice-pdf-to-csv-converter/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop Missing Easy Backlinks: Find Competitor Link Opportunities with Backlink Gap Analysis 🔗📈</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Tue, 04 Aug 2026 06:37:12 +0000</pubDate>
      <link>https://dev.to/lucy-green/stop-missing-easy-backlinks-find-competitor-link-opportunities-with-backlink-gap-analysis-3ld5</link>
      <guid>https://dev.to/lucy-green/stop-missing-easy-backlinks-find-competitor-link-opportunities-with-backlink-gap-analysis-3ld5</guid>
      <description>&lt;p&gt;Ever feel like your competitors are getting all the good backlinks while you’re stuck scraping the bottom of the barrel? I’ve been there. You publish great content, but your domain authority crawls while theirs skyrockets. The secret isn’t always creating better content—it’s finding the links you’re missing.&lt;/p&gt;

&lt;p&gt;That’s where a &lt;strong&gt;backlink gap analysis&lt;/strong&gt; comes in. It’s the SEO equivalent of finding a cheat code. The idea is simple: find websites that link to your competitors but not to you. Those are low-hanging fruit—sites already interested in your niche.&lt;/p&gt;

&lt;p&gt;Here’s a quick, technical way to do this without burning hours in spreadsheets.&lt;/p&gt;

&lt;p&gt;First, you need a list of your main competitors. Let’s say you run a SaaS for project management. Your competitors might be Asana, Trello, and Monday.com.&lt;/p&gt;

&lt;p&gt;Next, you need a tool that exports link data. While many paid tools do this, I’ve been using a free backlink gap checker from SerpSpur (&lt;a href="https://serpspur.com/tool/backlink-gap/" rel="noopener noreferrer"&gt;https://serpspur.com/tool/backlink-gap/&lt;/a&gt;). It pulls the intersection of domains linking to them but not you.&lt;/p&gt;

&lt;p&gt;Here’s a sample of what the output logic looks like in Python if you were to build this yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# Pseudocode for fetching and comparing backlinks
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_backlinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# API call to your favorite index
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;source&amp;amp;#039;] for link in api_response)
&lt;/span&gt;
&lt;span class="n"&gt;competitors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;asana.com&amp;amp;#039;, &amp;amp;#039;trello.com&amp;amp;#039;, &amp;amp;#039;monday.com&amp;amp;#039;]
&lt;/span&gt;&lt;span class="n"&gt;my_domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;yourproject.com&amp;amp;#039;
&lt;/span&gt;
&lt;span class="n"&gt;my_links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_backlinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gap_links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;comp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;competitors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;comp_links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_backlinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gap_links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comp_links&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;my_links&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gap_links&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="n"&gt;potential&lt;/span&gt; &lt;span class="n"&gt;prospects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have that list, filter it. Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sites with real traffic (check via SimilarWeb)&lt;/li&gt;
&lt;li&gt;Pages that are resource lists or "best tools" roundups&lt;/li&gt;
&lt;li&gt;Blogs that recently wrote about your niche&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, do the outreach. The key is to personalize. Don’t say, "I noticed you linked to Trello." Instead, say, "I saw your roundup on project management tools. I’ve built a comparison table that includes Trello and Asana, but I think adding [Your Tool] would give your readers a fresh perspective since we offer [unique feature]."&lt;/p&gt;

&lt;p&gt;That’s it. Rinse and repeat monthly. The gap shrinks, and your authority climbs.&lt;/p&gt;

&lt;p&gt;If you want to skip the coding, just use the tool I linked above—it does the heavy lifting and exports a CSV you can filter in Google Sheets. Happy hunting.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Uncover Hidden Internal Linking Issues with a Free SEO Audit Workflow 🔍</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Tue, 28 Jul 2026 06:18:26 +0000</pubDate>
      <link>https://dev.to/lucy-green/uncover-hidden-internal-linking-issues-with-a-free-seo-audit-workflow-49np</link>
      <guid>https://dev.to/lucy-green/uncover-hidden-internal-linking-issues-with-a-free-seo-audit-workflow-49np</guid>
      <description>&lt;p&gt;Tired of burning cash on expensive SEO suites just to run a simple competitor analysis? Me too. That’s why I built a lean workflow using the Chrome DevTools Network tab and a lightweight, free Python script to map out any page’s internal link structure—no paid subscriptions required.&lt;/p&gt;

&lt;p&gt;Most people don’t realize the DOM alone can be misleading. JavaScript frameworks often inject placeholder links that differ from the actual &lt;code&gt;href&lt;/code&gt; attributes rendered after hydration. The real source of truth? The network waterfall. When you scrape the &lt;code&gt;fetch/XHR&lt;/code&gt; requests from a live page, you get the raw data that drives the site’s navigation.&lt;/p&gt;

&lt;p&gt;Here’s a quick snippet to grab internal links from a page’s response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urljoin&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_internal_links&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;html.parser&amp;amp;#039;)
&lt;/span&gt;        &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;netloc&lt;/span&gt;
        &lt;span class="n"&gt;internal_links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a_tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;a&amp;amp;#039;, href=True):
&lt;/span&gt;            &lt;span class="n"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;href&amp;amp;#039;]
&lt;/span&gt;            &lt;span class="n"&gt;full_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urljoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urlparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;netloc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="c1"&gt;#039;http&amp;amp;#039;, &amp;amp;#039;https&amp;amp;#039;):
&lt;/span&gt;                &lt;span class="n"&gt;internal_links&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;internal_links&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_internal_links&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;links&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="n"&gt;internal&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;quot&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script filters out external links, ads, and social media icons. But it still misses dynamic routes loaded via API calls. To catch those, you’d hook into the Network tab using a headless browser like Playwright—but that’s a tutorial for another day.&lt;/p&gt;

&lt;p&gt;Why does this matter? SEO audits often get stuck on homepage metrics. But internal link depth signals content priority to search engines. If your “Services” page is three clicks deep and only linked from a footer, it’s practically invisible.&lt;/p&gt;

&lt;p&gt;I’ve been using this approach to spot orphaned pages and broken breadcrumbs on client sites. If you want to scale this into a full crawl—including backlink gaps and keyword mapping—you’ll eventually need a tool that automates the heavy lifting. That’s where SerpSpur comes in as a solid Semrush alternative. It gives you the same site audit, backlink analyzer, and keyword tracking without the enterprise price tag.&lt;/p&gt;

&lt;p&gt;But for today, just run the script on your own site. You’ll be surprised what you find. Drop your results in the comments—I’d love to see what you uncover.&lt;/p&gt;

</description>
      <category>python</category>
      <category>seo</category>
      <category>tools</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Uncover High-Value Link Opportunities with Backlink Gap Analysis 🔗📈</title>
      <dc:creator>Lucy Green</dc:creator>
      <pubDate>Thu, 23 Jul 2026 10:18:10 +0000</pubDate>
      <link>https://dev.to/lucy-green/uncover-high-value-link-opportunities-with-backlink-gap-analysis-4k0g</link>
      <guid>https://dev.to/lucy-green/uncover-high-value-link-opportunities-with-backlink-gap-analysis-4k0g</guid>
      <description>&lt;p&gt;Building backlinks is hard, but finding opportunities shouldn't be. I recently started using a backlink gap analysis tool, and it's been a game-changer.&lt;/p&gt;

&lt;p&gt;The idea is simple: you compare your domain with competitors and find websites that link to them but not to you. These are low-hanging fruit for outreach.&lt;/p&gt;

&lt;p&gt;Here's how I approach it. First, I enter my domain and up to five competitors into the tool. It scans their backlink profiles and highlights links unique to them.&lt;/p&gt;

&lt;p&gt;javascript&lt;br&gt;
// Example: Fetching gap data&lt;br&gt;
const apiUrl = "&lt;a href="https://serpspur.com/tool/backlink-gap/" rel="noopener noreferrer"&gt;https://serpspur.com/tool/backlink-gap/&lt;/a&gt;";&lt;br&gt;
const params = { domain: "mysite.com", competitors: ["comp1.com", "comp2.com"] };&lt;/p&gt;

&lt;p&gt;fetch(apiUrl, {&lt;br&gt;
    method: "POST",&lt;br&gt;
    body: JSON.stringify(params)&lt;br&gt;
})&lt;br&gt;
.then(response =&amp;gt; response.json())&lt;br&gt;
.then(data =&amp;gt; {&lt;br&gt;
    console.log("Missing links:", data.missing_links);&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;The output lists domains linking to competitors but not me. From there, I prioritize by domain authority and relevance. For each, I craft a personalized email explaining why my content is a better fit.&lt;/p&gt;

&lt;p&gt;This method has already earned me several high-quality backlinks. If you want to try it, the tool is free to use. Visit &lt;a href="https://serpspur.com" rel="noopener noreferrer"&gt;SERPSpur&lt;/a&gt; for more details.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2wmipaialr8q78yhbd91.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2wmipaialr8q78yhbd91.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
