如何修复Python递归提取网站href链接时重复且不全的问题?
网站递归爬取href链接的问题修复方案
原程序核心问题
- 正则匹配局限性:仅适配
href="格式的链接,未兼容单引号、无引号的href写法,且直接对bytes类型内容做正则匹配,易出现编码异常。 - 已爬链接无全局记录:
links_list是crawl函数的局部变量,每次递归都会重新初始化,无法追踪已爬取内容,导致重复爬取或遗漏链接。 - 域名判断逻辑过严:
if url in link的判断会漏掉同一域名下、但路径不包含完整target_url的子页面链接。 - 无异常处理机制:请求失败时直接抛出异常,中断整个爬取流程。
修复后代码实现
import requests import urllib.parse as urlparse import re from urllib.parse import urlsplit # 全局集合存储已爬链接,查询效率优于列表 crawled_links = set() def extract_links_from(url): try: response = requests.get(url, timeout=10) response.raise_for_status() # 抛出HTTP错误状态码异常 content = response.text # 匹配双引号/单引号包裹的href值,兼容常见写法 links = re.findall(r'href=(["\'])(.*?)\1', content, re.IGNORECASE) # 提取链接部分,过滤引号 return [link[1] for link in links] except requests.exceptions.RequestException as e: print(f"请求{url}失败: {str(e)}") return [] def crawl(url): target_domain = urlsplit(target_url).netloc if url in crawled_links: return crawled_links.add(url) print(url) href_links = extract_links_from(url) for link in href_links: full_link = urlparse.urljoin(url, link) # 移除锚点部分 if "#" in full_link: full_link = full_link.split("#")[0] # 判断是否为目标域名下的链接 link_domain = urlsplit(full_link).netloc if link_domain == target_domain and full_link not in crawled_links: crawl(full_link) target_url = "https://www.free-power-point-templates.com/" crawl(target_url)
关键修改说明
- 正则与编码优化:改用
r'href=(["\'])(.*?)\1'兼容双/单引号的href写法,同时用response.text获取解码后的文本内容,避免编码错误。 - 全局已爬集合:用
crawled_links全局集合记录已爬链接,爬取前先做存在性判断,避免重复递归。 - 域名判断优化:通过
urlsplit解析链接域名,确保仅爬取目标域名下的内容,解决原判断逻辑的局限性。 - 异常处理:添加
try-except捕获请求异常,打印错误后继续爬取其他链接,提升程序稳定性。
内容的提问来源于stack exchange,提问作者Mohammad dehghani
相关产品推荐
相关产品推荐

