
前言链接是网络爬虫的核心抓取目标之一无论是全站遍历、定向内容采集、站点拓扑分析还是多级页面递归爬取都离不开对网页中超链接的批量提取。互联网页面普遍存在链接重复、无效链接、跳转链接、空链接、广告外链等问题若直接使用原始提取结果开展后续爬取工作不仅会造成重复请求、带宽资源浪费、爬取效率大幅下降还可能陷入循环爬取、访问恶意站点等风险。链接提取与去重作为爬虫链路中承上启下的关键环节承担着有效链接筛选、重复数据剔除、无效地址过滤、链接格式标准化等核心工作。本文聚焦爬虫场景下链接批量提取、分类筛选、高效去重、格式修正全流程技术实现结合主流解析工具、数据结构、算法逻辑与落地代码讲解从单页链接抓取、多页链接聚合到大批量链接去重、异常链接过滤、相对路径转绝对路径等实战技能。文中所使用开发库官方访问地址如下requests、BeautifulSoup4、lxml、urllib.parse、re。全文结合不同数据量级、不同页面结构、不同业务需求划分实战场景对比多种去重方案的性能差异与适用场景同时针对相对路径、锚点链接、动态伪链接、重复域名链接等行业高频问题给出标准化解决方案构建一套可直接落地、可二次扩展的链接处理技术体系。一、网页链接基础认知与业务分类1.1 网页链接基本形态网页中超链接主要依托 HTMLa标签实现核心属性为href根据地址书写形式可划分为绝对链接与相对链接两大类二者语法特征、使用场景、处理方式存在明显区别同时页面中还存在大量非标准伪链接也是链接处理过程中需要重点识别的内容。1.1.1 绝对链接绝对链接包含完整的协议、域名、路径、参数等信息具备全局可访问特性格式标准为协议://域名/路径?参数#锚点。常见协议包含http、https、ftp等互联网主流站点均采用 HTTPS 协议。示例如下https://www.example.com/article/1001?page1#content该类链接可直接用于网络请求无需二次拼接是爬虫最理想的抓取地址。1.1.2 相对链接相对链接不包含完整域名与协议仅依托当前页面地址进行路径跳转分为站点根目录相对路径、当前目录相对路径两种形式无法直接发起请求必须结合页面原始 URL 拼接为绝对链接后方可使用。典型示例根目录相对路径/news/2026/0611.html目录层级相对路径../detail/info.html同目录文件路径list.html1.1.3 伪链接伪链接不具备真实访问地址多用于前端交互、脚本跳转、占位展示无法作为爬虫目标地址必须提前过滤。常见类型包含javascript:脚本链接、#纯锚点链接、空href属性等示例javascript:openPage();、#、href。1.2 链接业务分类与筛选规则结合爬虫业务需求对提取到的链接进行业务分类并制定对应的保留、过滤规则分类及处理标准整理如下表表格链接分类特征描述处理规则典型应用场景站内有效链接域名与目标站点一致路径指向内容页面、列表页面保留纳入爬取队列全站爬虫、站内内容批量采集站外外链域名与目标站点不同多为合作站点、广告、友情链接按需过滤定向爬虫直接剔除站内数据采集、站点结构分析无效伪链接以javascript:、#、空地址为主直接过滤不进入后续流程全品类爬虫通用规则重复链接地址文本完全一致或参数不同但指向同一页面执行去重仅保留单条数据多页面聚合链接、批量爬取场景带参数链接基础路径一致仅 URL 参数、锚点不同可清除冗余参数、锚点后再去重分页采集、动态链接处理相对路径链接无完整域名与协议统一转换为绝对链接通用爬虫、中小型站点采集1.3 链接处理整体流程标准化的链接提取与去重流程遵循逐层处理、逐步筛选的逻辑通用执行链路为网页源码爬取 → 解析所有 a 标签并提取 href 属性 → 过滤伪链接与空链接 → 相对路径转绝对路径 → 区分站内 / 站外链接 → 链接格式标准化清理锚点、冗余参数→ 批量链接去重 → 有效链接结果输出。 该流程由粗筛到精筛逐层递进先剔除完全无效数据再完成格式统一最后执行去重操作能够最大程度降低后续去重环节的数据体量提升整体处理效率。二、环境依赖与核心库功能解析本章所用库分为 Python 标准库与第三方库标准库随解释器自带无需额外安装第三方库需通过 pip 命令完成部署各库在链接处理环节的核心作用如下。2.1 标准库说明2.1.1 urllib.parse 链接处理库官方文档https://docs.python.org/3/library/urllib.parse.html该模块是 Python 内置 URL 处理工具核心方法包含urljoin、urlparse、urlunparse主要实现相对路径拼接绝对路径、URL 拆解、URL 重组、参数解析等功能是链接格式标准化的核心依赖。2.1.2 re 正则表达式库官方文档https://docs.python.org/3/library/re.html用于正则匹配、筛选指定规则链接、清理 URL 末尾锚点、过滤脚本类伪链接适用于规则化批量匹配场景可作为标签解析的补充方案。2.2 第三方库说明2.2.1 requests 网络请求库官方地址https://pypi.org/project/requests/安装命令pip install requests负责模拟浏览器发起 HTTP 请求获取网页原始 HTML 源码为链接解析提供原始数据源支持请求头、超时、代理等爬虫基础配置。2.2.2 BeautifulSoup4 lxml 解析组合BeautifulSoup4 官方地址https://pypi.org/project/beautifulsoup4/安装命令pip install beautifulsoup4lxml 官方地址https://pypi.org/project/lxml/安装命令pip install lxml。二者组合是解析 HTML 标签的主流方案精准定位页面中所有a标签并提取href属性解析稳定性、容错性远高于纯正则方案是链接提取的首选技术栈。2.3 环境校验代码执行以下代码可验证所有依赖库是否正常导入与基础功能可用无报错即代表环境配置完成。python运行import requests from bs4 import BeautifulSoup import re from urllib.parse import urljoin, urlparse # 基础功能测试 base_url https://www.example.com relative_path /index.html abs_url urljoin(base_url, relative_path) print(路径拼接结果, abs_url) print(所有依赖库加载正常)代码原理代码依次导入全部核心库调用urljoin完成基础的相对路径与根路径拼接验证urllib.parse核心功能同时完成库文件有效性校验快速排查版本缺失、导入异常等基础问题。三、单页面链接批量提取实战单页面链接提取是多页面聚合、全站爬取的基础本节基于 BeautifulSoup 实现精准提取同时完成空链接、伪链接、站外链接的初步过滤搭配代码案例与原理拆解讲解。3.1 基础版提取页面全部链接该方案实现页面内所有a标签href属性的全量提取不做复杂筛选适用于小型站点、测试场景。python运行import requests from bs4 import BeautifulSoup def get_all_links(html): 提取页面中所有a标签的href属性 link_list [] soup BeautifulSoup(html, lxml) a_tags soup.find_all(a) for tag in a_tags: href tag.get(href) if href is not None: link_list.append(href) return link_list # 主程序测试 if __name__ __main__: headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } url https://www.baidu.com resp requests.get(url, headersheaders, timeout8) all_links get_all_links(resp.text) print(f页面原始链接总数{len(all_links)}) for link in all_links[:10]: print(link)代码原理BeautifulSoup加载网页源码并指定lxml解析器适配不规范 HTML 代码find_all(a)全局查找页面内所有超链接标签遍历标签并通过get(href)获取链接地址增加非空判断过滤href属性为空的无效标签避免空数据进入列表最终将所有链接存入列表并返回完成基础提取逻辑。3.2 进阶版过滤伪链接与空链接原始提取结果中包含大量#、javascript:等伪链接此类链接无爬取价值在提取阶段直接过滤减少后续数据处理量。python运行import requests from bs4 import BeautifulSoup def get_valid_links(html): 提取链接并过滤伪链接、空链接 valid_links [] soup BeautifulSoup(html, lxml) a_tags soup.find_all(a) # 定义伪链接过滤规则 filter_prefix (#, javascript:, mailto:, tel:) for tag in a_tags: href tag.get(href, ).strip() if not href: continue # 过滤指定前缀的伪链接 if href.startswith(filter_prefix): continue valid_links.append(href) return valid_links # 测试运行 if __name__ __main__: headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36} url https://www.baidu.com resp requests.get(url, headersheaders, timeout8) res_links get_valid_links(resp.text) print(f过滤后有效链接数量{len(res_links)})代码原理使用get(href, )设置默认空字符串结合strip()清除链接首尾空白字符统一数据格式定义伪链接前缀元组使用startswith匹配过滤锚点链接、脚本链接、电话、邮件链接采用continue跳过无效数据仅将符合要求的链接加入结果列表完成第一层数据筛选。3.3 核心功能相对路径转绝对路径网页中大量链接为相对路径无法直接发起请求借助urllib.parse.urljoin实现相对路径与页面基准 URL 的自动拼接是链接处理的核心功能。python运行from urllib.parse import urljoin import requests from bs4 import BeautifulSoup def parse_absolute_links(base_url, html): 提取链接并统一转换为绝对路径 abs_links [] soup BeautifulSoup(html, lxml) a_tags soup.find_all(a) filter_prefix (#, javascript:, mailto:, tel:) for tag in a_tags: href tag.get(href, ).strip() if not href or href.startswith(filter_prefix): continue # 拼接为绝对链接 full_link urljoin(base_url, href) abs_links.append(full_link) return abs_links # 测试运行 if __name__ __main__: headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36} base_page https://www.baidu.com resp requests.get(base_page, headersheaders, timeout8) final_links parse_absolute_links(base_page, resp.text) print(f转换为绝对路径后的链接数量{len(final_links)}) print(部分链接示例) for link in final_links[:8]: print(link)代码原理urljoin(base_url, href)会自动识别传入的href是绝对路径还是相对路径若为绝对路径则直接返回原链接若为相对路径则基于基准 URL 完成域名、协议、路径拼接该方法自动处理../、./、/等不同层级相对路径无需手动判断路径层级兼容性极强整合前置过滤逻辑在转换路径的同时剔除伪链接实现提取、过滤、路径转换一体化。3.4 定向爬虫区分站内链接与站外链接定向爬虫仅需要抓取目标站点内部链接需通过域名比对过滤站外外链实现精准筛选。python运行from urllib.parse import urljoin, urlparse import requests from bs4 import BeautifulSoup def split_inner_outer(base_url, html): 拆分站内链接与站外链接 inner_links [] outer_links [] # 解析基准URL的主域名 base_netloc urlparse(base_url).netloc soup BeautifulSoup(html, lxml) a_tags soup.find_all(a) filter_prefix (#, javascript:, mailto:, tel:) for tag in a_tags: href tag.get(href, ).strip() if not href or href.startswith(filter_prefix): continue full_link urljoin(base_url, href) # 解析当前链接域名 link_netloc urlparse(full_link).netloc if link_netloc base_netloc: inner_links.append(full_link) else: outer_links.append(full_link) return inner_links, outer_links # 测试运行 if __name__ __main__: headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36} target_url https://www.baidu.com resp requests.get(target_url, headersheaders, timeout8) inner, outer split_inner_outer(target_url, resp.text) print(f站内链接数量{len(inner)}) print(f站外链接数量{len(outer)})代码原理urlparse可将完整 URL 拆解为协议、域名、路径、参数、锚点等多个组成部分netloc属性对应网址主域名提前解析目标页面主域名遍历所有绝对链接并比对域名域名一致判定为站内链接反之判定为站外链接拆分两类链接并分别返回定向爬虫可直接使用站内链接作为后续爬取队列站外链接直接丢弃。四、链接格式标准化处理提取后的链接常携带锚点、多余请求参数、空白字符等冗余内容会造成同一页面生成多条不同链接加剧重复问题。本节实现锚点清除、冗余参数清理、链接格式化。4.1 清除 URL 锚点URL 中#后的内容为页面锚点仅用于前端页面定位不影响请求结果属于冗余内容统一清除。python运行from urllib.parse import urljoin, urlparse, urlunparse def remove_anchor(link): 清除链接中的锚点内容 parse_res urlparse(link) # 重组URL将锚点fragment置空 new_url urlunparse(( parse_res.scheme, parse_res.netloc, parse_res.path, parse_res.params, parse_res.query, )) return new_url # 测试代码 if __name__ __main__: test_link https://www.example.com/article.html#part1 clean_link remove_anchor(test_link) print(原链接, test_link) print(清除锚点后, clean_link)代码原理urlparse拆解 URL 六大组成部分fragment字段对应锚点内容urlunparse按照拆解后的结构重新拼接 URL手动将锚点字段设置为空实现锚点清除该方案基于官方库实现相比正则替换不会误删链接中其他正常字符稳定性更高。4.2 清理冗余请求参数部分链接仅参数不同但指向同一页面可根据业务需求保留核心参数、删除分页、统计、埋点等冗余参数。python运行from urllib.parse import urlparse, parse_qs, urlencode, urlunparse def clean_url_param(link, keep_paramNone): 清理URL冗余参数可指定保留参数 if keep_param is None: keep_param [] parse_res urlparse(link) # 解析查询参数为字典 param_dict parse_qs(parse_res.query) # 仅保留指定参数 new_param {k: v for k, v in param_dict.items() if k in keep_param} # 重新编码参数 new_query urlencode(new_param, doseqTrue) # 重组URL new_url urlunparse(( parse_res.scheme, parse_res.netloc, parse_res.path, parse_res.params, new_query, parse_res.fragment )) return new_url # 测试代码 if __name__ __main__: # 带多个参数的测试链接 test_link https://www.example.com/list?page1track123typenews # 仅保留page参数 res_link clean_url_param(test_link, keep_param[page]) print(原链接, test_link) print(清理参数后, res_link)代码原理parse_qs将 URL 请求参数字符串解析为字典格式便于筛选操作通过字典推导式过滤参数仅保留业务所需参数剔除统计、追踪类冗余参数urlencode将筛选后的字典重新编码为 URL 标准参数字符串doseqTrue适配多值参数场景最后重组完整 URL完成参数标准化大幅降低同源不同参链接造成的重复问题。五、大批量链接去重方案详解链接去重是本章核心模块根据链接数据量级、性能要求、使用场景分别介绍列表遍历去重、集合去重、字典去重、有序去重四种主流方案对比各自优缺点与适用场景并附完整代码与原理分析。5.1 各去重方案特性对比不同去重方式在执行效率、数据顺序、内存占用、代码复杂度上存在明显差异详细对比见下表表格去重方案实现原理执行效率是否保留原有顺序适用数据量级优缺点说明双层列表遍历循环比对每一条数据极低是百条以内小数据代码简单大数据量时间复杂度高集合 (set) 去重利用集合元素唯一性极高否万条、十万条级数据速度最快打乱原始链接顺序字典 (dict) 去重利用字典键唯一性高Python3.7 保留顺序十万至百万级数据兼顾效率与顺序内存占用适中有序列表遍历新增列表 单次判断中是千条至万条级数据平衡效率与顺序通用性强5.2 方案一双层列表遍历去重基础实现方案逻辑简单适合入门理解去重思路仅适用于极少量链接数据。python运行def deduplicate_by_origin(link_list): 双层循环遍历去重保留原始顺序 new_list [] for link in link_list: if link not in new_list: new_list.append(link) return new_list # 测试 if __name__ __main__: links [ https://a.com/1, https://a.com/2, https://a.com/1, https://a.com/3, https://a.com/2 ] res deduplicate_by_origin(links) print(去重后链接, res)代码原理初始化空列表存储去重结果遍历原始链接列表每取出一条链接判断是否已存在于结果列表不存在则加入列表时间复杂度为 O (n²)数据量增大后性能急剧下降仅用于测试与极小批量数据。5.3 方案二集合 Set 去重高性能首选利用 Python 集合元素不可重复的特性是大数据量链接去重速度最快的方案。python运行def deduplicate_by_set(link_list): 集合去重效率最高不保留顺序 # 集合自动剔除重复元素 temp_set set(link_list) # 转回列表 new_list list(temp_set) return new_list # 测试 if __name__ __main__: links [ https://a.com/1, https://a.com/2, https://a.com/1, https://a.com/3 ] res deduplicate_by_set(links) print(集合去重结果, res)代码原理集合底层基于哈希表实现元素查找、插入时间复杂度为 O (1)整体去重复杂度 O (n)列表转集合瞬间完成重复元素剔除再转回列表即可得到无重复数据缺点是集合无序会打乱链接原始排列顺序适合不关注顺序、追求极致效率的场景如全站爬虫链接池。5.4 方案三字典 Dict 去重兼顾效率与顺序Python 3.7 及以上版本字典默认保留键的插入顺序结合字典键唯一特性实现有序高效去重是工业级常用方案。python运行def deduplicate_by_dict(link_list): 字典去重保留原始顺序效率较高 # 以链接为键值置为空 temp_dict dict.fromkeys(link_list) # 提取字典键为最终列表 new_list list(temp_dict.keys()) return new_list # 测试 if __name__ __main__: links [ https://a.com/1, https://a.com/2, https://a.com/1, https://a.com/3 ] res deduplicate_by_dict(links) print(字典去重结果, res)代码原理dict.fromkeys(列表)会将列表元素全部作为字典的键字典键不允许重复自动完成去重Python3.7 字典严格保留元素插入顺序最终提取键组成的列表与原始链接顺序一致时间复杂度接近集合性能优异同时解决顺序丢失问题是大多数爬虫项目的首选去重方案。5.5 方案四有序遍历去重通用平衡方案在单层循环内完成判断兼顾顺序与中等数据量性能兼容性最强适配所有 Python 版本。python运行def deduplicate_simple(link_list): 有序遍历去重兼容所有版本 seen set() new_list [] for link in link_list: if link not in seen: seen.add(link) new_list.append(link) return new_list # 测试 if __name__ __main__: links [ https://a.com/1, https://a.com/2, https://a.com/1, https://a.com/3 ] res deduplicate_simple(links) print(有序去重结果, res)代码原理借助集合seen记录已经出现过的链接利用集合 O (1) 查询速度提升效率遍历原始列表链接未出现过则同时加入集合与结果列表保证顺序不变整体复杂度 O (n)性能优于双层遍历同时保留顺序是中小型爬虫项目的通用方案。六、综合实战链接提取、格式化、去重全链路整合整合前文所有功能模块实现网页爬取 → 链接提取 → 伪链接过滤 → 路径转换 → 格式标准化 → 批量去重端到端完整流程模拟真实项目运行逻辑。6.1 完整综合代码python运行import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse, urlunparse, parse_qs, urlencode # 1. 清除锚点 def remove_anchor(link): parse_res urlparse(link) new_url urlunparse(( parse_res.scheme, parse_res.netloc, parse_res.path, parse_res.params, parse_res.query, )) return new_url # 2. 清理冗余参数 def clean_param(link, keepNone): if keep is None: keep [] parse_res urlparse(link) param_dict parse_qs(parse_res.query) new_param {k: v for k, v in param_dict.items() if k in keep} new_query urlencode(new_param, doseqTrue) new_url urlunparse(( parse_res.scheme, parse_res.netloc, parse_res.path, parse_res.params, new_query, parse_res.fragment )) return new_url # 3. 有序去重 def link_deduplicate(link_list): seen set() res [] for item in link_list: if item not in seen: seen.add(item) res.append(item) return res # 4. 单页完整链接处理 def get_standard_links(base_url, html): link_list [] soup BeautifulSoup(html, lxml) filter_prefix (#, javascript:, mailto:, tel:) a_tags soup.find_all(a) for tag in a_tags: href tag.get(href, ).strip() if not href or href.startswith(filter_prefix): continue # 转绝对路径 full_link urljoin(base_url, href) # 清除锚点 full_link remove_anchor(full_link) # 清理冗余参数仅保留page参数 full_link clean_param(full_link, keep[page]) link_list.append(full_link) # 批量去重 final_links link_deduplicate(link_list) return final_links # 主程序入口 if __name__ __main__: headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } target_url https://www.baidu.com try: resp requests.get(target_url, headersheaders, timeout10) resp.raise_for_status() # 全流程处理 result_links get_standard_links(target_url, resp.text) print(f标准化去重后有效链接总数{len(result_links)}) print(最终链接列表) for link in result_links: print(link) except Exception as e: print(程序运行异常, str(e))6.2 全流程运行原理模块化拆分将锚点清理、参数清理、去重、链接提取拆分为独立函数低耦合设计便于单独维护与功能扩展执行顺序标签提取 → 伪链接过滤 → 相对路径转绝对路径 → 清除锚点 → 清理 URL 参数 → 批量去重逐层精简数据异常防护网络请求增加状态码判断、异常捕获、超时设置提升爬虫稳定性规则可配置保留参数列表、过滤前缀均可根据业务需求修改适配不同站点、不同爬取规则。七、多页面链接聚合与全局去重实际爬虫项目中往往需要采集多个页面的链接再统一全局去重本节实现多页面链接聚合逻辑。python运行import requests from bs4 import BeautifulSoup from urllib.parse import urljoin, urlparse, urlunparse, parse_qs, urlencode # 复用前文工具函数 def remove_anchor(link): parse_res urlparse(link) new_url urlunparse((parse_res.scheme, parse_res.netloc, parse_res.path, parse_res.params, parse_res.query, )) return new_url def clean_param(link, keepNone): if keep is None: keep [] parse_res urlparse(link) param_dict parse_qs(parse_res.query) new_param {k: v for k, v in param_dict.items() if k in keep} new_query urlencode(new_param, doseqTrue) new_url urlunparse((parse_res.scheme, parse_res.netloc, parse_res.path, parse_res.params, new_query, parse_res.fragment)) return new_url def single_page_links(base_url, html): temp [] soup BeautifulSoup(html, lxml) filter_prefix (#, javascript:, mailto:) for tag in soup.find_all(a): href tag.get(href, ).strip() if not href or href.startswith(filter_prefix): continue full_link urljoin(base_url, href) full_link remove_anchor(full_link) full_link clean_param(full_link) temp.append(full_link) return temp # 多页面聚合链接 def multi_page_links(page_url_list): all_links [] headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36} for page_url in page_url_list: try: resp requests.get(page_url, headersheaders, timeout8) page_links single_page_links(page_url, resp.text) all_links.extend(page_links) except: continue # 全局去重 unique_links list(dict.fromkeys(all_links)) return unique_links # 测试多页面采集 if __name__ __main__: pages [ https://www.baidu.com, https://www.baidu.com/s?wdpython ] total_links multi_page_links(pages) print(f多页面聚合去重后链接总数{len(total_links)})代码原理遍历多个页面地址逐个爬取页面并提取标准化链接使用extend将单页链接追加至全局列表所有页面数据采集完成后执行一次全局去重剔除跨页面重复链接该逻辑适用于列表页、分页页面等多页面场景是批量爬虫链接池构建的标准实现方式。八、常见问题排查与优化方案8.1 高频问题及解决办法相对路径拼接失败原因是基准 URL 书写不规范解决方案保证基准 URL 为完整可访问地址优先使用页面实际请求地址作为拼接基准。去重后仍存在重复链接原因是链接携带锚点、多余参数解决方案在去重前统一执行锚点清除、参数精简。误过滤有效链接原因是过滤前缀规则过于宽泛解决方案细化伪链接匹配规则精准限定过滤范围。海量链接处理卡顿原因是去重方案选择不当解决方案十万条以上数据优先使用集合去重百万级数据可分批次处理。8.2 项目级优化策略链接预校验对提取后的链接增加域名白名单、后缀白名单过滤图片、视频、附件等非页面链接增量去重搭建持久化链接库将已爬取链接存入文件或数据库实现增量判断避免全量去重消耗资源异步采集多页面链接聚合场景引入异步请求提升多页面爬取与链接提取速度。