Python 异常处理在爬虫项目中的应用

爬虫中常见的网络请求异常包括连接错误、超时和HTTP状态码异常,需通过try-except分层捕获并针对性处理。

python 异常处理在爬虫项目中的应用

在爬虫项目中,Python的异常处理机制绝不是可有可无的装饰品,它简直就是保障爬虫生命力与稳定性的核心骨架。没有它,你的爬虫就像在薄冰上跳舞,任何一点风吹草动——网络波动、目标网站结构微调、IP被封——都可能让它瞬间崩塌,功亏一篑。真正有效的异常处理,能让爬虫从容应对这些“不确定性”,哪怕遭遇挫折也能优雅地恢复,继续它的使命,确保数据收集的连续性和完整性。

解决方案

要让爬虫变得“皮实”起来,我们得系统地运用

try-except-finally-else

结构。这不仅仅是捕获错误那么简单,它更像是一种风险管理策略。我的经验是,先预判那些最常出现的“雷区”,比如网络连接中断、请求超时、HTTP状态码异常,以及数据解析时的各种意外。针对这些预判,我们用特定的

except

块去精准拦截。

比如,当发起一个网络请求时,可能会遇到服务器无响应、DNS解析失败或者代理挂掉。这些都属于

requests.exceptions.RequestException

的范畴。如果直接用一个大而全的

except Exception as e:

去捕获,虽然能防止程序崩溃,但你丢失了错误发生时的具体上下文,也就难以对症下药。因此,我们应该先捕获更具体的异常,再逐步放宽到更通用的异常。

立即学习“Python免费学习笔记(深入)”;

一个健壮的爬虫,其异常处理逻辑应该包含:

请求层面的异常:针对

requests

库可能抛出的各种错误,如

ConnectionError

Timeout

HTTPError

等。解析层面的异常:当使用BeautifulSoup、lxml或json库解析数据时,可能出现选择器失效、键不存在、JSON格式错误等问题。业务逻辑异常:比如在数据校验时发现数据不符合预期,或者某些字段缺失。重试机制:对于瞬时性的网络问题,简单的重试往往就能解决。但要注意,重试不能是无限次的,并且最好配合指数退避(exponential backoff),给服务器一点喘息的时间。日志记录:每一次异常的发生,都应该被详细记录下来,包括发生时间、URL、异常类型、堆信息等,这对于后续的调试和问题排查至关重要。

import requestsimport timeimport randomimport logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')def fetch_url_with_retry(url, retries=3, backoff_factor=0.5):    for i in range(retries):        try:            response = requests.get(url, timeout=10)            response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)            return response        except requests.exceptions.Timeout:            logging.warning(f"请求超时,URL: {url},尝试重试 {i+1}/{retries}...")        except requests.exceptions.ConnectionError:            logging.warning(f"连接错误,URL: {url},尝试重试 {i+1}/{retries}...")        except requests.exceptions.HTTPError as e:            if e.response.status_code == 404:                logging.error(f"页面未找到 (404),URL: {url}")                return None # 404通常不需要重试            logging.warning(f"HTTP错误 {e.response.status_code},URL: {url},尝试重试 {i+1}/{retries}...")        except requests.exceptions.RequestException as e:            logging.error(f"未知请求异常,URL: {url},错误: {e},尝试重试 {i+1}/{retries}...")        if i < retries - 1:            sleep_time = backoff_factor * (2 ** i) + random.uniform(0, 1) # 指数退避加随机抖动            logging.info(f"等待 {sleep_time:.2f} 秒后重试...")            time.sleep(sleep_time)    logging.error(f"多次重试失败,URL: {url} 无法获取。")    return None# 示例使用# response = fetch_url_with_retry("http://www.example.com/nonexistent")# if response:#     print(response.text[:100])

爬虫中常见的网络请求异常有哪些,以及如何针对性地捕获和处理?

在爬虫的世界里,网络请求异常简直是家常便饭。我的经验是,大部分爬虫的“崩溃”都始于此。最常见的几种,无非就是连接不上、请求超时、以及HTTP状态码不正常。

首先是

requests.exceptions.ConnectionError

。这通常意味着你的程序无法建立到目标服务器的连接。可能是目标网站宕机了,也可能是你的网络有问题,或者DNS解析失败。这种错误,第一时间想到的应该是重试。但别傻乎乎地立刻重试,给它一点时间,比如等个几秒钟,再尝试。如果还是不行,那可能就不是瞬时问题了,得考虑是不是IP被封了,或者目标网站真的挂了。

接着是

requests.exceptions.Timeout

。当你发送请求后,在指定的时间内没有收到服务器的响应,就会抛出这个异常。超时可能是因为服务器处理请求太慢,也可能是网络延迟高。对于这种,重试同样是有效手段,但可能需要调整超时时间,或者尝试更换代理。我通常会设置一个合理的超时时间,比如5-10秒,而不是无限等待。

然后是

requests.exceptions.HTTPError

。这发生在服务器返回了非200的HTTP状态码时,比如404(页面未找到)、403(禁止访问)、500(服务器内部错误)等等。

requests

库的

response.raise_for_status()

方法就是为此而生。对于404,通常意味着这个URL是无效的,不需要重试,直接记录并跳过就好。403往往是反爬机制在作祟,此时你需要考虑更换User-Agent、使用代理IP,甚至模拟登录。而500错误,可能是服务器暂时性故障,重试几次往往能解决。

处理这些异常,关键在于“针对性”。我们应该利用Python的异常继承链,先捕获最具体的异常,再捕获更通用的。这就像你生病了,医生会先诊断是感冒还是肺炎,而不是直接给你开个“万能药”。

import requestsimport timeimport randomimport logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')def robust_get(url, retries=3, delay_base=1):    for attempt in range(retries):        try:            # 模拟代理切换或User-Agent轮换            headers = {'User-Agent': f'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{random.randint(80, 100)}.0.0.0 Safari/537.36'}            response = requests.get(url, timeout=15, headers=headers)            response.raise_for_status() # 检查HTTP状态码            return response        except requests.exceptions.Timeout:            logging.warning(f"请求超时,URL: {url} (尝试 {attempt + 1}/{retries})")        except requests.exceptions.ConnectionError:            logging.warning(f"连接错误,URL: {url} (尝试 {attempt + 1}/{retries})")        except requests.exceptions.HTTPError as e:            status_code = e.response.status_code            if status_code == 404:                logging.error(f"资源未找到 (404),URL: {url}。跳过。")                return None            elif status_code == 403:                logging.warning(f"访问被拒绝 (403),URL: {url}。可能需要更换IP或User-Agent。")            elif status_code >= 500:                logging.warning(f"服务器内部错误 ({status_code}),URL: {url}。")            else:                logging.warning(f"HTTP错误 ({status_code}),URL: {url}。")        except requests.exceptions.RequestException as e:            logging.error(f"发生未知请求错误: {e},URL: {url}")        if attempt < retries - 1:            sleep_time = delay_base * (2 ** attempt) + random.uniform(0, 1)            logging.info(f"等待 {sleep_time:.2f} 秒后重试...")            time.sleep(sleep_time)    logging.error(f"多次重试失败,无法获取 URL: {url}")    return None# 示例:# resp = robust_get("https://httpbin.org/status/403")# if resp:#     print(resp.text)

通过这种分层、精细化的处理,我们能让爬虫在面对网络世界的各种“恶意”时,表现得更加从容和专业。

数据解析阶段的异常处理,如何避免因数据结构变化导致爬虫崩溃?

爬虫最脆弱的环节之一,就是数据解析。我见过太多爬虫,前一秒还在欢快地抓取数据,后一秒就因为目标网站HTML结构或者API响应格式的微小变动,直接“猝死”。这种感觉就像你精心搭建的乐高城堡,被一阵突如其来的风吹散了。

常见的解析异常,主要集中在以下几类:

IndexError

KeyError

:当你试图访问一个不存在的列表索引或者字典键时。比如,你期望某个HTML元素下有第三个子元素,结果只有两个;或者某个JSON字段突然不见了。

AttributeError

:在使用BeautifulSoup或lxml时,如果你尝试访问一个不存在的标签属性或者解析结果对象上没有的方法。

TypeError

:数据类型不匹配,比如你期望一个字符串,结果却是个

None

,然后你尝试对

None

调用字符串方法。

json.JSONDecodeError

:当API返回的响应体不是一个合法的JSON字符串时。

避免这些问题,核心思路是“防御性编程”:永远不要假设数据结构是完美的、不变的。

安全访问字典和列表:对于字典,使用

.get(key, default_value)

方法,而不是直接

dict[key]

。这样即使键不存在,也不会抛出

KeyError

,而是返回你设定的默认值(通常是

None

)。对于列表,在访问索引前,先检查列表的长度,或者使用

try-except IndexError

检查

None

:在对解析结果进行操作前,务必检查它是否为

None

。比如,

if element is not None: element.text

使用健壮的选择器:CSS选择器或XPath表达式应该尽可能地具有鲁棒性,避免过度依赖层级关系。比如,优先使用

id

class

或者

data-*

属性,而不是

div > div > span

这种脆弱的结构。捕获特定解析异常:将解析代码包裹在

try-except

块中,捕获

IndexError

KeyError

AttributeError

json.JSONDecodeError

等。当这些异常发生时,记录下错误信息和对应的URL,然后跳过当前项,而不是让整个爬虫停下来。数据校验:在数据入库前,进行一次最终的校验。比如,确保某个字段是数字类型,某个字符串长度符合要求等。

from bs4 import BeautifulSoupimport jsonimport logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')def parse_html_data(html_content, url):    data = {}    try:        soup = BeautifulSoup(html_content, 'lxml')        # 示例1: 安全访问元素及其文本        title_element = soup.select_one('h1.product-title') # 使用更具体的选择器        data['title'] = title_element.text.strip() if title_element else None        # 示例2: 安全访问属性        image_element = soup.select_one('img.product-image')        data['image_url'] = image_element.get('src') if image_element else None        # 示例3: 处理可能缺失的列表项        price_list = soup.select('span.price-item')        try:            data['main_price'] = price_list[0].text.strip() if price_list else None            data['discount_price'] = price_list[1].text.strip() if len(price_list) > 1 else None        except IndexError:            logging.warning(f"解析价格列表时索引越界,URL: {url}")            data['main_price'] = None            data['discount_price'] = None    except AttributeError as e:        logging.error(f"解析HTML时属性错误,URL: {url},错误: {e}")        return None    except Exception as e: # 捕获其他未预料的解析错误        logging.error(f"解析HTML时发生未知错误,URL: {url},错误: {e}")        return None    return datadef parse_json_data(json_string, url):    try:        data = json.loads(json_string)        # 安全访问字典键        product_name = data.get('product', {}).get('name')        product_price = data.get('product', {}).get('details', {}).get('price')        if product_name is None:            logging.warning(f"JSON数据中缺少 'product.name' 字段,URL: {url}")        return {'name': product_name, 'price': product_price}    except json.JSONDecodeError as e:        logging.error(f"JSON解析错误,URL: {url},错误: {e}")        return None    except Exception as e:        logging.error(f"解析JSON时发生未知错误,URL: {url},错误: {e}")        return None# 示例使用# html_example = "

Test Product

@@##@@$100"# parsed_html = parse_html_data(html_example, "http://example.com/product/1")# print(parsed_html)# json_example = '{"product": {"name": "Laptop", "details": {"price": 1200}}}'# parsed_json = parse_json_data(json_example, "http://example.com/api/product/1")# print(parsed_json)

通过这些手段,我们能够大幅提升爬虫在面对目标网站结构变化时的韧性,让它不至于因为一点小变动就“罢工”。

构建健壮爬虫时,除了捕获异常,还有哪些策略可以提升系统的容错性和稳定性?

单纯地捕获异常,只是“治标不治本”。一个真正健壮的爬虫系统,需要一系列组合拳来提升其容错性和稳定性。这就像建造一座大楼,地基要稳固,结构要合理,还得有消防系统和应急通道。

完善的日志系统:这不仅仅是记录异常,而是记录爬虫运行的方方面面。请求URL、响应状态码、解析结果、入库情况,甚至每次重试的详情。使用

logging

模块,设置不同的日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL),将日志输出到文件,并定期归档。这样,当问题发生时,你才能有迹可循,快速定位问题。

智能的重试机制与指数退避:前面已经提到,对于瞬时性的网络错误,重试是有效的。但关键在于“智能”。不要立即重试,而是等待一段时间,并且每次重试的等待时间逐渐增加(指数退避),同时加入随机抖动,避免“死循环”或给目标网站造成更大压力。设置最大重试次数,超过后放弃当前任务。

代理IP池与User-Agent轮换:这是应对反爬机制的利器。当IP被封禁或某个User-Agent被识别时,系统能自动切换到下一个可用的代理或User-Agent。一个健康的代理池需要有检测机制,定期清理失效代理。

限速与请求间隔:对目标网站的访问频率进行控制,模拟人类的浏览行为。设置一个随机的请求间隔(例如2到5秒),可以有效降低被封禁的风险,也体现了对目标网站的“尊重”。

任务队列与持久化:对于大规模爬虫,使用消息队列(如Redis、RabbitMQ)来管理待抓取URL,并将已抓取和待抓取的任务状态进行持久化。这样,即使爬虫程序意外中断,也能从上次中断的地方恢复,避免重复抓取或数据丢失。

监控与告警:这是最容易被忽视,但却至关重要的一环。实时监控爬虫的运行状态,比如抓取速度、错误率、代理IP可用率、数据入库量等。当某个指标超出预设阈值时,通过邮件、短信或即时通讯工具发送告警,让你能第一时间介入处理。

数据校验与清洗:在数据入库前,对抓取到的数据进行严格的校验和清洗。例如,检查字段是否缺失、数据类型是否正确、是否存在异常值。不符合要求的数据,可以记录下来进行人工复查,而不是直接丢弃或入库。

模块化与解耦:将爬虫的不同功能(请求、解析、存储、调度)模块化,降低耦合度。这样,当某个模块出现问题时,更容易隔离和修复,而不影响整个系统的运行。

这些策略的引入,能让爬虫从一个简单的脚本,升级为一个能够自我修复、稳定运行的系统。它不再只是被动地捕获错误,而是主动地预防错误,并具备从错误中恢复的能力,这才是真正意义上的“健壮”。

Python 异常处理在爬虫项目中的应用

以上就是Python 异常处理在爬虫项目中的应用的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1373022.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Python 实战:简易 Flask 博客项目
上一篇 2025年12月14日 12:49:46
使用Python subprocess模块运行带参数和输入重定向的外部命令
下一篇 2025年12月14日 12:49:58

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • 修复Django电商项目中AJAX过滤产品列表图片不显示问题

    在Django电商项目中,当使用AJAX动态加载过滤后的产品列表时,常遇到图片无法正常显示的问题。这通常是由于前端模板中图片加载方式(如data-setbg属性结合JavaScript库)与AJAX动态内容更新机制不兼容所致。解决方案是直接在AJAX返回的HTML中使用标准的标签来渲染图片,确保浏览…

    2026年5月10日
    700
  • 开源免费PHP工具 PHP开发效率提升利器

    推荐开源免费PHP开发工具以提升效率:VS Code、Sublime Text轻量高效,PhpStorm专业强大;调试用Xdebug、Kint、Ray;依赖管理选Composer;代码质量工具包括PHPStan、Psalm、PHP_CodeSniffer;数据库管理可用%ignore_a_1%MyA…

    2026年5月10日
    000
  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    900
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    300
  • 利用海象运算符简化条件赋值:Python教程与最佳实践

    本文旨在探讨Python中海象运算符(:=)在条件赋值场景下的应用。通过对比传统if/else语句与海象运算符,以及条件表达式,分析海象运算符在简化代码、提高可读性方面的优势与局限性。并通过具体示例,展示如何在列表推导式等场景下合理使用海象运算符,同时强调其潜在的复杂性及替代方案,帮助开发者更好地掌…

    2026年5月10日
    300
  • Debian syslog性能优化技巧有哪些

    提升Debian系统syslog (通常基于rsyslog)性能,关键在于精简配置和高效处理日志。以下策略能有效优化日志管理,提升系统整体性能: 精简配置,高效加载: 在rsyslog配置文件中,仅加载必要的输入、输出和解析模块。 使用全局指令设置日志级别和格式,避免不必要的处理。 自定义模板: 创…

    2026年5月10日
    000
  • 怎么在PHP代码中实现图片上传功能_PHP图片上传功能实现与安全处理教程

    首先创建含enctype的HTML表单,再用PHP接收文件,检查目录、移动临时文件,验证类型与大小,生成唯一文件名,并调整php.ini限制以确保上传成功。 如果您尝试在PHP项目中添加图片上传功能,但服务器无法正确接收或保存文件,则可能是由于表单配置、文件处理逻辑或安全限制的问题。以下是实现该功能…

    2026年5月10日
    300
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • HTML如何隐藏滚动条或去除滚动条

    滚动条可以存在也可以不存在,本文主要介绍了html 隐藏滚动条和去除滚动条的方法的相关资料,大家一起来学习一下html隐藏滚动条或去除滚动条的方法吧。 1. html 标签加属性 XML/HTML Code复制内容到剪贴板 2.body中加入以下代码 立即学习“前端免费学习笔记(深入)”; html…

    用户投稿 2026年5月10日
    100
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    100
  • vscode上怎么运行html_vscode上运行html步骤【指南】

    首先保存文件为.html格式,再通过浏览器或Live Server插件打开预览;推荐安装Live Server实现本地服务器运行与实时刷新,提升开发体验。 在 VS Code 上运行 HTML 文件并不需要复杂的配置,只需几个简单步骤即可预览页面效果。VS Code 本身是一个代码编辑器,不直接运行…

    2026年5月10日
    100
  • css max-height属性怎么用

    max-height 属性设置元素的最大高度。 说明 该属性值会对元素的高度设置一个最高限制。因此,元素可以比指定值矮,但不能比其高。不允许指定负值。 注意:max-height 属性不包括外边距、边框和内边距。 立即学习“前端免费学习笔记(深入)”; 值描述none 默认。定义对元素被允许的最大高…

    2026年5月10日
    100
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    300
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    100
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 页面中文本域的值怎么设置

    标签定义多行的文本输入控件。 文本区中可容纳无限数量的文本,其中的文本的默认字体是等宽字体(通常是 Courier)。 可以通过 cols 和 rows 属性来规定 textarea 的尺寸,不过更好的办法是使用 CSS 的 height 和 width 属性。 注释:在文本输入区内的文本行间,用 …

    2026年5月10日
    000
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    200
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信