Link Scraper.
Link Scraper is a free bookmarklet that pulls every link off the page you're on, or every result off a Google search page, in one click. It runs entirely in your browser tab: no extension, no server, no account, and nothing leaves your device.

Install the bookmarklet
Drag the button below to your bookmarks bar.
Drag me to your bookmarks barSome browsers block pasted javascript: links, so dragging is the reliable way to install this.
Prefer not to drag-and-drop? See How to create a bookmarklet manually below.
Dragging isn't possible on phones, so add it manually instead (steps below). On Safari/iOS, tapping the bookmark runs it on the current page. On Chrome/Android, tapping does nothing; give the bookmark a short name instead, then type that name into the address bar and pick it from the suggestions to run it.
Why it's useful
For SEOs
No dev tools or crawler needed: it reads the live DOM in your tab and hands back each link's URL, anchor text and position, ready to paste into a spreadsheet.
For SERP & competitor research
Pull organic results, AI Overview citations and People Also Asked links straight off the page you're looking at, deduped and clean, without installing a SERP-scraping extension that can phone home with your searches or that you can't adjust when Google changes its markup.
For quick SEO tasks generally
When the data's already on screen, a bookmarklet beats spinning up AI tooling to go fetch it, and it leaves the tokens for the analysis that actually needs them.
How Link Scraper works
Behavior depends on the page you're on.
On any web page: internal & external links
- Open the page, then click the bookmark.
- Get every internal and external link, with anchor text, position (Navigation, Main Content, Sidebar, Footer, Other) and rel attributes like nofollow, so mismatched anchors and issues jump out immediately.
- Filter by position, clean the URLs, and copy a link, a CSV, or the visible table as text or Markdown.

On a Google search results page: SERP scraping
- Search on Google, then click the bookmark.
- Get every result split by type (AI Overview text and citations, Organic, People Also Asked, Paid Ads), with URLs that show up in more than one position flagged.
- Copy clean URLs straight into your favorite tool or LLM prompt.
A word on why that matters more than it used to: Google has been rolling out /goto redirect links on search results. Instead of the result linking straight to its destination, the href points at an internal Google URL, and the actual target isn't visible until a browser actually follows it, so a plain "read the href off the page" scraper just returns a Google redirect link instead of the real URL. It started as a small-scale test, but Google has confirmed to both Barry Schwartz at Search Engine Roundtable and Search Engine Land that it's expanding the rollout, and by late August 2026 the /goto token itself is a long, opaque, non-decodable string — there's no trick that reads a URL back out of it, on the page or off it; the only way to learn the exact destination of a link Google hasn't otherwise exposed is to actually follow the redirect, once per link, which is a real network request Link Scraper deliberately never makes (see "Privacy" below). What Link Scraper does instead is read the destination data Google embeds elsewhere on the same page for other parts of its own UI, and resolves every /goto link that data covers before it ever reaches your export — the same approach other SEOs have reported keeps scraping working post-rollout. On a real search this typically recovers the exact page for around half of results; for others Google only exposes the domain, and the row is flagged domain-only rather than passing off a guess as the exact page. For results where Google's internal data exposes neither, Link Scraper falls back to the breadcrumb line Google itself shows on-screen under the result (flagged approx-path, since Google truncates that breadcrumb too on longer paths). On the remainder — mostly paid ads, video carousels and People Also Ask entries, which never carried a resolvable destination even before this rollout — it keeps Google's own redirect address rather than showing a wrong one.

What Link Scraper detects
Link Scraper sorts every link into a position category based on where it sits on the page, and checks it against seven attribute flags. Both show up as columns in the results table and in every export (CSV, Markdown, plain text).
Position categories
Ten categories cover both modes: five only appear on a Google results page, four only on any other webpage, and one, Other, can appear in either.
| Category | Appears on | What it means |
|---|---|---|
| Organic Result | Google search results | A standard search result, numbered by its rank in the results list (#1, #2, ...). |
| Paid Ad | Google search results | A sponsored result inside Google's ad slots, numbered within its ad block. |
| AI Text Link | Google search results | A link inside the body text of Google's AI Overview answer. |
| AI Citation | Google search results | A source link the AI Overview cites, shown as a "Related results" chip or an icon link. |
| People Also Ask | Google search results | A link inside a "People also ask" answer panel, numbered by question order. |
| Navigation | Any other webpage | A link inside the page's <nav>, <header>, or a menu/navbar element. |
| Main Content | Any other webpage | A link inside <main>, <article>, or the page's content wrapper. |
| Sidebar | Any other webpage | A link inside <aside> or a sidebar/widget element. |
| Footer | Any other webpage | A link inside the page's <footer>. |
| Other | Both modes | A link that doesn't match any of the categories above. |
Every organic, AI Citation and People Also Ask link on Google now goes through an opaque redirect, and only sometimes does Google expose the real destination elsewhere in the page. Link Scraper always resolves the full page when Google's own data covers it; failing that, it reconstructs a path from the visible citation breadcrumb (approx-path); and when even that isn't there, the row carries a domain-only flag instead of silently passing off a guess as the exact page. See "How Link Scraper works" above for what fraction of results that resolves in practice.
Link attributes
Beyond its category, every link is checked against seven attribute flags and marked in the results table's Attributes column when they apply.
| Flag | What it means | How it's detected |
|---|---|---|
dofollow | Default state: no rel attribute restricting it. Passes ranking signals normally. | Shown when none of the flags below apply. |
nofollow | Tells search engines not to pass ranking credit through the link. | rel="nofollow" |
sponsored | Marks a paid or affiliate link, per Google's link attribute guidelines. | rel="sponsored" |
ugc | Marks user-generated content, like a forum post or comment link. | rel="ugc" |
HTTP/Insecure | The link points to a plain http:// URL from a page served over HTTPS, a mixed-content risk. | Target starts with http: while the current page is https:. |
file | The link points straight at a downloadable file rather than a page. | URL path ends in .pdf, .zip, .doc(x), .xls(x), .ppt(x) or .csv. |
domain-only | Google Search only: the URL is the cited source's homepage, not the exact page, because Google didn't expose the exact page anywhere in this result. | The resolved URL has no path or query string beyond the domain itself. |
approx-path | Google Search only: Google's internal data for this result didn't expose the exact page, so the path was rebuilt from the on-page citation breadcrumb (the "https://site.com › section › page" line Google itself shows under the result). That breadcrumb can end in "…" when Google truncates it, so the last segment may be incomplete. | URL reconstructed from a visible <cite> breadcrumb rather than from Google's internal per-result data. |
A link can carry more than one flag at once (a nofollow file link, say), and they show up together in the table and in the CSV/Markdown exports.
How to create a bookmarklet manually
To add it by hand instead:
- On desktop, show the bookmarks bar if it's hidden: Ctrl + Shift + B (Windows/Linux) or Cmd + Shift + B (Mac). Skip this step on mobile.
- Add a new bookmark: right-click the bookmarks bar and choose "Add page" (desktop), or use your browser's add-bookmark option (mobile; wording varies).
- Give it a short, easy-to-type name (useful on Chrome for Android, see above).
- Paste the full JS code below into the URL field and save.
The bookmarklet's source code
Full, readable source, the same code as the button above, just not URL-encoded.
javascript:(function(){const isGoogle=window.location.hostname.includes('google.')&&window.location.pathname.includes('/search');const query=isGoogle?(new URLSearchParams(window.location.search).get('q')||''):'';const categories=isGoogle?['AI Text Link','AI Citation','Organic Result','People Also Ask','Paid Ad','Other']:['Navigation','Main Content','Sidebar','Footer','Other'];let aiBlock=null;if(isGoogle){const headings=Array.from(document.querySelectorAll('h1, h2, h3, div[role="heading"], span[role="heading"], div.V3FYNc'));const aiHeading=headings.find(h=>h.innerText&&/^(AI Overview|KI-Überblick|KI-Übersicht|Generative KI)/i.test(h.innerText.trim()));if(aiHeading){aiBlock=aiHeading.closest('.M8OgIe, [data-md]')||aiHeading.parentElement.parentElement.parentElement;}if(!aiBlock)aiBlock=document.querySelector('.M8OgIe');}let gotoMap=new Map();if(isGoogle){try{const cleanUrl=u=>u.replace(/\\u([0-9a-fA-F]{4})/g,(mm,hx)=>String.fromCharCode(parseInt(hx,16))).replace(/\\\//g,'/');const isJunkUrl=u=>/gstatic\.com|googleusercontent\.com|doubleclick\.net|googlesyndication\.com|google-analytics\.com|googletagmanager\.com|googleadservices\.com|googleapis\.com|faviconV2/i.test(u)||/^https?:\/\/(?:www\.)?google\.[a-z.]{2,8}\/?$/i.test(u);const addPair=(tok,url)=>{if(tok&&url&&!isJunkUrl(url)&&!gotoMap.has(tok))gotoMap.set(tok,cleanUrl(url));};const goReF=/\/goto\?url(?:=|\\u003[dD])([\w-]+)"\s*\]\s*,\s*\[\s*"(https?:[^"]+)"/g;const urlRe=/"(https?:[^"]+)"/g;const gotoRe=/"\/goto\?url(?:=|\\u003[dD])([\w-]+)"/g;const scanText=t=>{if(!t||t.indexOf('/goto')===-1)return;goReF.lastIndex=0;let gm;while((gm=goReF.exec(t)))addPair(gm[1],gm[2]);let urls=[];urlRe.lastIndex=0;let um;while((um=urlRe.exec(t)))urls.push({idx:um.index,url:um[1]});gotoRe.lastIndex=0;while((gm=gotoRe.exec(t))){let best=null;for(let i=urls.length-1;i>=0;i--){if(urls[i].idx>=gm.index)continue;if(gm.index-urls[i].idx>500)break;if(isJunkUrl(urls[i].url))continue;best=urls[i];break;}if(best)addPair(gm[1],best.url);}};for(const sc of document.scripts)scanText(sc.textContent);const cw=document.createTreeWalker(document.documentElement,NodeFilter.SHOW_COMMENT);let cn;while((cn=cw.nextNode())){let ct=cn.nodeValue||'';if(ct.indexOf('/goto')===-1)continue;ct=ct.replace(/\\\\"/g,'\\"').replace(/\\"/g,'\\"').replace(/"/g,'"').replace(/\\\\u/g,'\\u');scanText(ct);}}catch(e){}}function citeUrlFor(a){try{let node=a;for(let i=0;i<15&&node;i++){node=node.parentElement;if(!node)break;const cite=node.querySelector('cite');if(cite){let t=(cite.childNodes[0]&&cite.childNodes[0].nodeType===3)?cite.childNodes[0].nodeValue:cite.textContent;t=(t||'').trim();if(/^https?:\/\//i.test(t)){let others=node.querySelectorAll('a[href*="/goto?url="]');let distinct=new Set();others.forEach(x=>{if(x!==a)distinct.add(x.getAttribute('href'));});if(distinct.size===0){let parts=t.split(/\s*›\s*/);let base=parts[0].replace(/\/+$/,'');let segs=parts.slice(1).filter(s=>s&&s!=='...').map(s=>s.replace(/\.\.\.$/,''));return base+(segs.length?'/'+segs.join('/'):'');}}}}}catch(e){}return null;}const links=document.querySelectorAll('a[href]');const data={};const currentHost=window.location.hostname;const currentUrl=window.location.href;const isHttpsPage=window.location.protocol==='https:';let domIdx=0;let ctrs={'Organic Result':0,'Paid Ad':0,'People Also Ask':0,'AI Text Link':0,'AI Citation':0,'Other':0};let bMap=new Map();function esc(s){return(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');}function escCsv(s){let v=(s||'');if(/^[=+\-@\t]/.test(v))v="'"+v;return v.replace(/"/g,'""');}function escMd(s){return(s||'').replace(/\|/g,'\\|');}links.forEach(a=>{domIdx++;let rawHref=a.getAttribute('href')||'';if(rawHref.trim().toLowerCase().startsWith('javascript:'))return;if(rawHref.includes('#elementor-action'))return;let url=a.href;let relAttr=(a.getAttribute('rel')||'').toLowerCase();let flags=[];if(relAttr.includes('nofollow'))flags.push('nofollow');if(relAttr.includes('sponsored'))flags.push('sponsored');if(relAttr.includes('ugc'))flags.push('ugc');if(isHttpsPage&&url.startsWith('http:'))flags.push('HTTP/Insecure');if(/\.(pdf|zip|docx?|xlsx?|pptx?|csv)$/i.test(url.split('?')[0]))flags.push('file');if(isGoogle){let dUrl=a.getAttribute('data-adurl')||a.getAttribute('data-rw');if(dUrl)url=dUrl;if(url.includes('/url?')){try{let o=new URL(url);let c=o.searchParams.get('q')||o.searchParams.get('url');if(c)url=c;}catch(e){}}if(url.includes('/aclk?')){try{let o=new URL(url);let c=o.searchParams.get('adurl');if(c)url=c;}catch(e){}}if(url.includes('/goto?url=')){try{let o=new URL(url);let tok=o.searchParams.get('url');let mapped=(tok&&gotoMap.has(tok))?gotoMap.get(tok):null;let mappedDomainOnly=false;if(mapped){try{let ru=new URL(mapped);mappedDomainOnly=(ru.pathname===''||ru.pathname==='/')&&!ru.search;}catch(e){}}if(mapped&&!mappedDomainOnly){url=mapped;}else{let cu=citeUrlFor(a);if(cu){url=cu;flags.push('approx-path');}else if(mapped){url=mapped;flags.push('domain-only');}}}catch(e){}}if((url.includes('google.com')||url.includes('google.de')||url.includes('google.at')||url.includes('google.ch'))&&!url.includes('/goto?url=')||url.startsWith('#'))return;}let flagStr=flags.length?flags.join(', '):'dofollow';let rawCombinedText=(a.innerText||'')+' '+(a.getAttribute('aria-label')||'')+' '+(a.getAttribute('title')||'');let isCitationSignal=/Zugehörige Links ansehen|View related links|Wird in einem neuen Tab geöffnet|Opens in a new tab/i.test(rawCombinedText)||a.closest('[role="button"], [role="listitem"], [data-attrid], [data-ved*="pill"], .v5yA4e, .VDg42d')||/\+\d+/.test(rawCombinedText)||!!a.querySelector('img, svg');let text=(a.innerText||'').trim();if(!text){const img=a.querySelector('img');if(img){if(isGoogle)return;let alt=img.getAttribute('alt');text=(alt&&alt.trim()!=='')?alt.trim()+' [img]':'[Empty Alt] [img]';}}if(!text)text=(a.textContent||'').trim().replace(/\s+/g,' ');if(!text)text=(a.getAttribute('aria-label')||'').trim();if(!text)text=(a.getAttribute('title')||'').trim();if(!text&&isGoogle&&a.parentElement){let pt=(a.parentElement.innerText||'').trim().replace(/\n/g,' | ');if(pt)text=pt.substring(0,65)+(pt.length>65?'...':'');}if(!text)text="[No Text] "+(url.startsWith('http')?new URL(url,location.href).pathname:url);text=text.replace(/\s*[–—-]\s*(Zugehörige Links ansehen|View related links)/gi,'').replace(/\.?\s*(Wird in einem neuen Tab geöffnet|Opens in a new tab)\.?/gi,'').trim();if(!text)text="[No Text] "+(url.startsWith('http')?new URL(url,location.href).pathname:url);if(/^(diese seite übersetzen|translate this page)$/i.test(text))return;let pos='Other';let rank='';if(isGoogle){if(a.closest('#tads, #ads, .commercial-unit, [data-text-ad]')||a.href.includes('/aclk')){pos='Paid Ad';let b=a.closest('.commercial-unit, [data-text-ad], #tads > div');if(b){if(!bMap.has(b)){ctrs[pos]++;bMap.set(b,ctrs[pos]);}rank=' #'+bMap.get(b);}else{ctrs[pos]++;rank=' #'+ctrs[pos];}}else if(aiBlock&&aiBlock.contains(a)){if(isCitationSignal){pos='AI Citation';}else{pos='AI Text Link';}rank='';}else if(a.closest('.related-question-pair, .Wt5Tfe, .ygGdYd, .iDjcJe')){pos='People Also Ask';let b=a.closest('.related-question-pair, .Wt5Tfe, .ygGdYd, .iDjcJe');if(b){if(!bMap.has(b)){ctrs[pos]++;bMap.set(b,ctrs[pos]);}rank=' #'+bMap.get(b);}else{ctrs[pos]++;rank=' #'+ctrs[pos];}}else if(a.closest('.g, .Ww4FFb')||a.querySelector('h3')){pos='Organic Result';let b=a.closest('.g, .Ww4FFb');if(b){if(!bMap.has(b)){ctrs[pos]++;bMap.set(b,ctrs[pos]);}rank=' #'+bMap.get(b);}else{ctrs[pos]++;rank=' #'+ctrs[pos];}}}else{if(a.closest('footer, [data-elementor-type="footer"]')){pos='Footer';}else if(a.closest('nav, header, [data-elementor-type="header"]')){pos='Navigation';}else if(a.closest('aside, .sidebar, #sidebar')){pos='Sidebar';}else if(a.closest('main, article, #main, #content, .main-content, .entry-content, [data-elementor-type="wp-page"], [data-elementor-type="wp-post"], [data-elementor-type="single-post"], .single-post')){pos='Main Content';}else{let parent=a.parentElement;while(parent&&parent!==document.body){let c=((parent.className||'')+' '+(parent.id||'')).toLowerCase();if(/(^|\s)(footer|site-footer|main-footer)(\s|$)/.test(c)){pos='Footer';break;}if(/(^|\s)(nav|navbar|menu|navigation|main-menu|site-nav)(\s|$)/.test(c)){pos='Navigation';break;}if(/(^|\s)(sidebar|aside|widget-area)(\s|$)/.test(c)){pos='Sidebar';break;}if(/(^|\s)(main|content|article|main-content|site-content|entry-content|single-post)(\s|$)/.test(c)){pos='Main Content';break;}parent=parent.parentElement;}}}let isExt=isGoogle?false:((new URL(url,window.location.origin)).hostname!==currentHost);let occKey=text+'|||'+pos+'|||'+rank+'|||'+flagStr;if(!data[url]){data[url]={url:url,occs:[],isExt:isExt};}if(!data[url].occs.some(o=>o.key===occKey)){data[url].occs.push({key:occKey,idx:domIdx});}});let arr=Object.values(data).map(i=>{let minDomIdx=Math.min(...i.occs.map(o=>o.idx));return{url:i.url,isExt:i.isExt,occs:i.occs,minDomIdx:minDomIdx};});arr.sort((a,b)=>isGoogle?(a.minDomIdx-b.minDomIdx):a.url.toLowerCase().localeCompare(b.url.toLowerCase()));let intL=arr.filter(i=>!i.isExt);let extL=arr.filter(i=>i.isExt);let subTitle=isGoogle?`Search Result for "${esc(query)}"`:`Links in: <a href="${esc(currentUrl)}" target="_blank">${esc(currentUrl)}</a>`;let intFilterHtml=`<div class="dd" id="ddInt"><div class="dd-btn" id="ddBtnInt">Filter: Positions ▼</div><div class="dd-m" id="ddMInt"><div class="dd-i dd-all"><label class="dd-lbl"><input type="checkbox" id="ddMst" checked> All</label></div>`;categories.forEach(cat=>{intFilterHtml+=`<div class="dd-i"><label class="dd-lbl"><input type="checkbox" value="${esc(cat)}" checked class="dd-cb"> ${esc(cat)}</label><div class="dd-o" data-cat="${esc(cat)}">ONLY</div></div>`;});intFilterHtml+=`</div></div>`;let extFilterHtml=`<div class="dd" id="ddExt"><div class="dd-btn" id="ddBtnExt">Filter: Positions ▼</div><div class="dd-m" id="ddMExt"><div class="dd-i dd-all"><label class="dd-lbl"><input type="checkbox" id="ddMstExt" checked> All</label></div>`;categories.forEach(cat=>{extFilterHtml+=`<div class="dd-i"><label class="dd-lbl"><input type="checkbox" value="${esc(cat)}" checked class="dd-cb-ext"> ${esc(cat)}</label><div class="dd-o" data-cat="${esc(cat)}">ONLY</div></div>`;});extFilterHtml+=`</div></div>`;let html=`<!DOCTYPE html><html><head><title>Link Scraper Pro</title><meta charset="utf-8"><style>body{font-family:sans-serif;background:#f0f8ff;color:#333;margin:0;padding:5px;font-size:11px}h1{color:#0056b3;font-size:16px;margin:0 0 4px 0}h2{color:#0056b3;font-size:13px;margin:15px 0 5px 0}h3{color:#0056b3;font-size:12px;margin:0}.sub{font-size:11px;color:#555;margin-bottom:8px;word-break:break-all;font-weight:bold}.c{max-width:99%;margin:0 auto;background:#fff;padding:8px;border-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,0.1)}.ctrl{margin-bottom:8px;display:flex;flex-wrap:wrap;gap:10px;align-items:center;background:#f8fbff;padding:6px;border-radius:3px;border:1px solid #cce0ff;position:sticky;top:0;z-index:10}.filters{display:flex;flex-wrap:wrap;gap:10px;align-items:center;border-right:1px solid #cce0ff;padding-right:10px}.filters label{cursor:pointer;display:flex;align-items:center;gap:3px}button{padding:4px 8px;border:1px solid #0056b3;border-radius:3px;background:#e6f2ff;color:#0056b3;cursor:pointer;font-weight:bold;font-size:11px;transition:background 0.2s}button:hover{background:#0056b3;color:#fff}input[type="text"]{padding:4px 6px;border:1px solid #cce0ff;border-radius:3px;font-size:11px;width:160px}table{width:100%;border-collapse:collapse;margin-bottom:20px;table-layout:fixed}th,td{padding:4px 6px;border-bottom:1px solid #cce0ff;text-align:left;word-wrap:break-word;overflow-wrap:break-word}th:nth-child(1){width:50%}th:nth-child(2){width:42%}th:nth-child(3){width:8%}th{background:#0056b3;color:#fff;position:sticky;top:36px;font-size:11px;z-index:9}tr:hover{background:#e6f2ff}a{color:#0056b3;text-decoration:none}a:hover{text-decoration:underline}.copy-btn,.row-copy-btn{font-size:10px;padding:2px 5px;border:1px solid #0056b3;border-radius:3px;background:#e6f2ff;color:#0056b3;cursor:pointer;font-weight:bold}.copy-btn:hover,.row-copy-btn:hover{background:#0056b3;color:#fff}.occ{display:flex;justify-content:space-between;align-items:flex-start;border-bottom:1px dashed #dae6f5;padding:3px 0;gap:5px}.occ:last-child{border-bottom:none}.txt{flex:1}.badg{font-size:9px;padding:2px 5px;border-radius:3px;white-space:nowrap;font-weight:bold}.b-nav{background:#e0f2fe;color:#0369a1}.b-main{background:#dcfce7;color:#15803d}.b-side{background:#fef08a;color:#854d0e}.b-foot{background:#f3e8ff;color:#7e22ce}.b-aio{background:#ffedd5;color:#9a3412}.b-org{background:#dcfce7;color:#15803d}.b-paa{background:#ecfeff;color:#0e7490}.b-pad{background:#ffe4e6;color:#9f1239}.b-oth{background:#f1f5f9;color:#475569}.b-flg{background:#fee2e2;color:#991b1b;margin-left:3px}.tx-row{display:flex;gap:10px;margin-bottom:10px}.tx-col{flex:1}.tx-hdr{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}textarea{width:100%;box-sizing:border-box;padding:8px;border:1px solid #cce0ff;border-radius:3px;background:#f8fbff;color:#333;font-family:monospace;font-size:11px;resize:vertical;white-space:pre;overflow-wrap:normal;overflow-x:auto}.tx-wrap:focus{outline:none;border-color:#0056b3}.ft{margin-top:20px;text-align:center;font-size:10px;color:#666;border-top:1px solid #cce0ff;padding-top:10px}.h{display:none}.dd{position:relative;display:inline-block}.dd-btn{padding:4px 8px;border:1px solid #0056b3;border-radius:3px;background:#e6f2ff;color:#0056b3;cursor:pointer;font-weight:bold;font-size:11px;display:flex;align-items:center;gap:4px}.dd-btn:hover{background:#0056b3;color:#fff}.dd-m{position:absolute;top:100%;left:0;background:#fff;border:1px solid #cce0ff;box-shadow:0 4px 6px rgba(0,0,0,0.1);z-index:99;display:none;min-width:180px;border-radius:3px;margin-top:2px;padding:0}.dd-m.s{display:block}.dd-i{display:flex;justify-content:space-between;align-items:center;padding:5px 8px;cursor:pointer;border-bottom:1px solid #f0f8ff;font-size:11px}.dd-i:hover{background:#f8fbff}.dd-i:last-child{border-bottom:none}.dd-lbl{display:flex;align-items:center;gap:6px;flex:1;cursor:pointer;margin:0}.dd-o{display:none;background:#e2e8f0;color:#334155;font-size:9px;padding:2px 5px;border-radius:3px;font-weight:bold;cursor:pointer}.dd-o:hover{background:#cbd5e1}.dd-i:hover .dd-o{display:block}.dd-all{background:#f1f5f9;font-weight:bold;border-bottom:2px solid #e2e8f0}.dd-all:hover{background:#e2e8f0}</style></head><body><div class="c"><h1>Link Scraper Pro</h1><div class="sub">${subTitle}</div><h2>Internal Links (${intL.length})</h2><div class="ctrl">${intFilterHtml}<input type="text" id="srchInt" placeholder="Search internal links..."><label style="cursor:pointer;display:flex;align-items:center;gap:4px;font-weight:bold;color:#0369a1;"><input type="checkbox" class="cbCleanUrls" style="margin:0;"> Clean URLs</label><label style="cursor:pointer;display:flex;align-items:center;gap:4px;font-weight:bold;color:#0369a1;"><input type="checkbox" class="cbFlattenRows" style="margin:0;"> Flatten Rows</label><button id="btnCopyAllInt">Copy Visible URLs</button><button id="btnCopyTabInt">Copy Table</button><button id="btnCopyMdInt">Copy as MD</button></div><table id="intTbl"><thead><tr><th>URL</th><th>Anchor Text(s), Position & Attributes</th><th>Action</th></tr></thead><tbody></tbody></table><h2>External Links (${extL.length})</h2><div class="ctrl">${extFilterHtml}<input type="text" id="srchExt" placeholder="Search external links..."><label style="cursor:pointer;display:flex;align-items:center;gap:4px;font-weight:bold;color:#0369a1;"><input type="checkbox" class="cbCleanUrls" style="margin:0;"> Clean URLs</label><label style="cursor:pointer;display:flex;align-items:center;gap:4px;font-weight:bold;color:#0369a1;"><input type="checkbox" class="cbFlattenRows" style="margin:0;"> Flatten Rows</label><button id="btnCopyAllExt">Copy Visible URLs</button><button id="btnCopyTabExt">Copy Table</button><button id="btnCopyMdExt">Copy as MD</button></div><table id="extTbl"><thead><tr><th>URL</th><th>Anchor Text(s), Position & Attributes</th><th>Action</th></tr></thead><tbody></tbody></table><h2>Raw Data Export (All Links)</h2><div class="tx-row"><div class="tx-col"><div class="tx-hdr"><h3>Links</h3><button class="tx-copy-btn" data-target="txUrls">Copy</button></div><textarea id="txUrls" class="tx-wrap" rows="6" readonly></textarea></div><div class="tx-col"><div class="tx-hdr"><h3>Anchors</h3><button class="tx-copy-btn" data-target="txAncs">Copy</button></div><textarea id="txAncs" class="tx-wrap" rows="6" readonly></textarea></div></div><div style="margin-bottom:10px"><div class="tx-hdr"><h3>All Data - CSV</h3><button class="tx-copy-btn" data-target="txCsv">Copy</button></div><textarea id="txCsv" class="tx-wrap" rows="8" readonly></textarea></div><div class="ft">Made By Stephan Reiss - SEO / GEO Consultant | <a href="https://www.linkedin.com/in/reiss/" target="_blank">https://www.linkedin.com/in/reiss/</a></div></div></body></html>`;const nw=window.open('','_blank');if(!nw){alert('Pop-up blocked! Please allow pop-ups for this site.');return;}nw.document.open();nw.document.write(html);nw.document.close();const d=nw.document;function copyText(text,btn){const x=d.createElement('textarea');x.value=text;d.body.appendChild(x);x.select();d.execCommand('copy');d.body.removeChild(x);const orig=btn.innerText;btn.innerText='Copied!';window.setTimeout(()=>btn.innerText=orig,1200);}function buildRows(items,isClean,isFlat){let h='';items.forEach(item=>{let origU=item.url;let cleanU=origU.split('?')[0].split('#')[0];let u=isClean?cleanU:origU;let displayU=u.length>100?esc(u.substring(0,100))+'...':esc(u);if(isFlat){item.occs.forEach(o=>{let p=o.key.split('|||');let bc={'Navigation':'b-nav','Main Content':'b-main','Sidebar':'b-side','Footer':'b-foot','AI Text Link':'b-aio','AI Citation':'b-paa','Organic Result':'b-org','People Also Ask':'b-paa','Paid Ad':'b-pad'}[p[1]]||'b-oth';let badgeText=p[1]+(p[2]||'');let flagBadge=p[3]&&p[3]!=='dofollow'?' <span class="badg b-flg">'+esc(p[3])+'</span>':'';let occHtml='<div class="occ" data-cat="'+esc(p[1])+'" data-idx="'+o.idx+'"><span class="txt">'+esc(p[0])+'</span><span><span class="badg '+bc+'">'+esc(badgeText)+'</span>'+flagBadge+'</span></div>';h+='<tr data-pos="'+esc(p[1])+'" data-min-idx="'+o.idx+'"><td><a href="'+esc(u)+'" data-orig="'+esc(origU)+'" data-clean="'+esc(cleanU)+'" class="link-url" target="_blank">'+displayU+'</a></td><td>'+occHtml+'</td><td><button class="row-copy-btn" data-url="'+esc(u)+'">Copy</button></td></tr>';});}else{let posSet=new Set();let occHtml=item.occs.map(o=>{let p=o.key.split('|||');posSet.add(p[1]);let bc={'Navigation':'b-nav','Main Content':'b-main','Sidebar':'b-side','Footer':'b-foot','AI Text Link':'b-aio','AI Citation':'b-paa','Organic Result':'b-org','People Also Ask':'b-paa','Paid Ad':'b-pad'}[p[1]]||'b-oth';let badgeText=p[1]+(p[2]||'');let flagBadge=p[3]&&p[3]!=='dofollow'?' <span class="badg b-flg">'+esc(p[3])+'</span>':'';return '<div class="occ" data-cat="'+esc(p[1])+'" data-idx="'+o.idx+'"><span class="txt">'+esc(p[0])+'</span><span><span class="badg '+bc+'">'+esc(badgeText)+'</span>'+flagBadge+'</span></div>';}).join('');h+='<tr data-pos="'+esc(Array.from(posSet).join(','))+'" data-min-idx="'+item.minDomIdx+'"><td><a href="'+esc(u)+'" data-orig="'+esc(origU)+'" data-clean="'+esc(cleanU)+'" class="link-url" target="_blank">'+displayU+'</a></td><td>'+occHtml+'</td><td><button class="row-copy-btn" data-url="'+esc(u)+'">Copy</button></td></tr>';}});return h;}const ddBtnInt=d.getElementById('ddBtnInt');const ddMInt=d.getElementById('ddMInt');if(ddBtnInt&&ddMInt){ddBtnInt.addEventListener('click',(e)=>{e.stopPropagation();ddMInt.classList.toggle('s');});}const ddCbsInt=d.querySelectorAll('.dd-cb');const masterCbInt=d.getElementById('ddMst');const srchInt=d.getElementById('srchInt');function filterTable(tblId,cbs,masterCb,srchEl){const checked=Array.from(cbs).filter(cb=>cb.checked).map(cb=>cb.value);const q=(srchEl?srchEl.value:'').toLowerCase();const tbody=d.querySelector('#'+tblId+' tbody');if(!tbody)return;const rows=Array.from(tbody.querySelectorAll('tr'));rows.forEach(tr=>{let vCat=0;let minIdx=9999999;tr.querySelectorAll('.occ').forEach(o=>{if(checked.includes(o.getAttribute('data-cat'))){o.classList.remove('h');vCat++;let idx=parseInt(o.getAttribute('data-idx'),10);if(!isNaN(idx)&&idx<minIdx)minIdx=idx;}else{o.classList.add('h');}});const a=tr.querySelector('.link-url');const txtMatch=!q||(a&&a.href.toLowerCase().includes(q))||(tr.innerText.toLowerCase().includes(q));tr.classList.toggle('h',vCat===0||!txtMatch);tr.setAttribute('data-min-idx',minIdx);});if(masterCb)masterCb.checked=(checked.length===cbs.length);if(isGoogle){rows.sort((a,b)=>parseInt(a.getAttribute('data-min-idx'),10)-parseInt(b.getAttribute('data-min-idx'),10));rows.forEach(tr=>tbody.appendChild(tr));}}function updateInternalFilter(){filterTable('intTbl',ddCbsInt,masterCbInt,srchInt);}ddCbsInt.forEach(cb=>cb.addEventListener('change',updateInternalFilter));if(masterCbInt){masterCbInt.addEventListener('change',(e)=>{ddCbsInt.forEach(cb=>cb.checked=e.target.checked);updateInternalFilter();});}if(srchInt)srchInt.addEventListener('input',updateInternalFilter);d.querySelectorAll('#ddMInt .dd-o').forEach(btn=>{btn.addEventListener('click',(e)=>{e.stopPropagation();e.preventDefault();const cat=btn.getAttribute('data-cat');ddCbsInt.forEach(cb=>cb.checked=(cb.value===cat));updateInternalFilter();});});const ddBtnExt=d.getElementById('ddBtnExt');const ddMExt=d.getElementById('ddMExt');if(ddBtnExt&&ddMExt){ddBtnExt.addEventListener('click',(e)=>{e.stopPropagation();ddMExt.classList.toggle('s');});}const ddCbsExt=d.querySelectorAll('.dd-cb-ext');const masterCbExt=d.getElementById('ddMstExt');const srchExt=d.getElementById('srchExt');function updateExternalFilter(){filterTable('extTbl',ddCbsExt,masterCbExt,srchExt);}ddCbsExt.forEach(cb=>cb.addEventListener('change',updateExternalFilter));if(masterCbExt){masterCbExt.addEventListener('change',(e)=>{ddCbsExt.forEach(cb=>cb.checked=e.target.checked);updateExternalFilter();});}if(srchExt)srchExt.addEventListener('input',updateExternalFilter);d.querySelectorAll('#ddMExt .dd-o').forEach(btn=>{btn.addEventListener('click',(e)=>{e.stopPropagation();e.preventDefault();const cat=btn.getAttribute('data-cat');ddCbsExt.forEach(cb=>cb.checked=(cb.value===cat));updateExternalFilter();});});d.addEventListener('click',(e)=>{if(!e.target.closest('.dd')){d.querySelectorAll('.dd-m').forEach(m=>m.classList.remove('s'));}});const cleanCbs=d.querySelectorAll('.cbCleanUrls');const flatCbs=d.querySelectorAll('.cbFlattenRows');function refreshViews(){const isClean=Array.from(cleanCbs).some(cb=>cb.checked);const isFlat=Array.from(flatCbs).some(cb=>cb.checked);d.querySelector('#intTbl tbody').innerHTML=buildRows(intL,isClean,isFlat);d.querySelector('#extTbl tbody').innerHTML=buildRows(extL,isClean,isFlat);updateInternalFilter();updateExternalFilter();let allItems=intL.concat(extL);let urlsOut="";let ancsOut="";let csvOut="URL,Anchor Text,Position,Attributes\n";allItems.forEach(i=>{let u=isClean?i.url.split('?')[0].split('#')[0]:i.url;urlsOut+=u+"\n";let ts=Array.from(new Set(i.occs.map(o=>o.key.split('|||')[0]))).join(' | ');ancsOut+=ts+"\n";if(isFlat){i.occs.forEach(o=>{let p=o.key.split('|||');let anc=p[0];let pos=p[1]+(p[2]||'');let attr=p[3]&&p[3]!=='dofollow'?p[3]:'';csvOut+='"'+escCsv(u)+'","'+escCsv(anc)+'","'+escCsv(pos)+'","'+escCsv(attr)+'"\n';});}else{let ps=Array.from(new Set(i.occs.map(o=>o.key.split('|||')[1]+(o.key.split('|||')[2]||'')))).join(' | ');let fs=Array.from(new Set(i.occs.map(o=>o.key.split('|||')[3]))).filter(f=>f!=='dofollow').join(' | ');csvOut+='"'+escCsv(u)+'","'+escCsv(ts)+'","'+escCsv(ps)+'","'+escCsv(fs)+'"\n';}});const tU=d.getElementById('txUrls');if(tU)tU.value=urlsOut.trim();const tA=d.getElementById('txAncs');if(tA)tA.value=ancsOut.trim();const tC=d.getElementById('txCsv');if(tC)tC.value=csvOut.trim();}flatCbs.forEach(cb=>{cb.addEventListener('change',(e)=>{const isFlat=e.target.checked;flatCbs.forEach(c=>c.checked=isFlat);refreshViews();});});cleanCbs.forEach(cb=>{cb.addEventListener('change',(e)=>{const isClean=e.target.checked;cleanCbs.forEach(c=>c.checked=isClean);refreshViews();});});refreshViews();const copyTabHandler=(tblId,targetBtn)=>{let out="URL\tAnchor Text\tPosition\n";d.querySelectorAll('#'+tblId+' tbody tr:not(.h)').forEach(tr=>{const a=tr.querySelector('.link-url');if(!a)return;const url=a.href;const occs=Array.from(tr.querySelectorAll('.occ:not(.h)'));if(occs.length===0)return;const ancs=occs.map(o=>o.querySelector('.txt').innerText).join(' | ');const pos=occs.map(o=>Array.from(o.querySelectorAll('.badg')).map(b=>b.innerText).join(' ')).join(' | ');out+=url+"\t"+ancs+"\t"+pos+"\n";});copyText(out.trim(),targetBtn);};const copyMdHandler=(tblId,targetBtn)=>{let out="| URL | Anchor Text | Position |\n| --- | --- | --- |\n";d.querySelectorAll('#'+tblId+' tbody tr:not(.h)').forEach(tr=>{const a=tr.querySelector('.link-url');if(!a)return;const url=escMd(a.href);const occs=Array.from(tr.querySelectorAll('.occ:not(.h)'));if(occs.length===0)return;const ancs=occs.map(o=>escMd(o.querySelector('.txt').innerText)).join(' <br> ');const pos=occs.map(o=>Array.from(o.querySelectorAll('.badg')).map(b=>escMd(b.innerText)).join(' ')).join(' <br> ');out+="| "+url+" | "+ancs+" | "+pos+" |\n";});copyText(out.trim(),targetBtn);};const btnCopyAllInt=d.getElementById('btnCopyAllInt');if(btnCopyAllInt){btnCopyAllInt.addEventListener('click',(e)=>{let urls="";d.querySelectorAll('#intTbl tbody tr:not(.h)').forEach(tr=>{const a=tr.querySelector('.link-url');if(a)urls+=a.href+"\n";});copyText(urls.trim(),e.target);});}const btnCopyTabInt=d.getElementById('btnCopyTabInt');if(btnCopyTabInt){btnCopyTabInt.addEventListener('click',(e)=>copyTabHandler('intTbl',e.target));}const btnCopyMdInt=d.getElementById('btnCopyMdInt');if(btnCopyMdInt){btnCopyMdInt.addEventListener('click',(e)=>copyMdHandler('intTbl',e.target));}const btnCopyAllExt=d.getElementById('btnCopyAllExt');if(btnCopyAllExt){btnCopyAllExt.addEventListener('click',(e)=>{let urls="";d.querySelectorAll('#extTbl tbody tr:not(.h)').forEach(tr=>{const a=tr.querySelector('.link-url');if(a)urls+=a.href+"\n";});copyText(urls.trim(),e.target);});}const btnCopyTabExt=d.getElementById('btnCopyTabExt');if(btnCopyTabExt){btnCopyTabExt.addEventListener('click',(e)=>copyTabHandler('extTbl',e.target));}const btnCopyMdExt=d.getElementById('btnCopyMdExt');if(btnCopyMdExt){btnCopyMdExt.addEventListener('click',(e)=>copyMdHandler('extTbl',e.target));}d.addEventListener('click',(e)=>{if(e.target.classList.contains('row-copy-btn')){const tr=e.target.closest('tr');const a=tr.querySelector('.link-url');copyText(a.href,e.target);}});d.querySelectorAll('.tx-copy-btn').forEach(btn=>{btn.addEventListener('click',(e)=>{const targetId=btn.getAttribute('data-target');const tx=d.getElementById(targetId);if(tx){tx.select();copyText(tx.value,e.target);}});});d.querySelectorAll('.tx-wrap').forEach(tx=>{tx.addEventListener('click',()=>tx.select());});})();To inspect it elsewhere or self-host it, fetch it directly:curl https://www.stephan-reiss.de/tools/link-scraper/bookmarklet.js
Privacy: no data leaves your browser
Link Scraper makes no network requests. It reads only the current tab's DOM and sends, logs, or stores nothing outside your browser.
Security: read the code before you install
Any bookmarklet, this one included, runs with the same access as the page's own scripts, so a malicious one could read form fields, grab cookies, or fake a login overlay. Never install one you haven't read; the code above is exactly what runs when you click the button, unminified.