Python爬虫程序在Spyder中每爬取约600个网页后卡死,如何排查问题?

null
Python爬虫程序在Spyder中每爬取约600个网页后卡死,如何排查问题?

2 回复

这个问题很典型,通常和资源管理或网络行为有关。别慌,咱们一步步来。

首先,最可能的原因是连接没关闭或者内存泄漏。爬虫卡死,多半是资源耗尽了。我给你写个诊断和修复的模板,你对照着改。

import requests
import time
from contextlib import closing
import gc

def robust_crawler(url_list, delay=1, max_retries=3):
    """
    一个健壮的爬虫函数,重点处理连接和内存。
    """
    session = requests.Session()
    # 设置一个合理的超时,防止永远等待
    session.request = lambda method, url, **kwargs: requests.Session.request(
        session, method, url, timeout=(10, 30), **kwargs
    )

    for i, url in enumerate(url_list):
        print(f"Processing {i+1}/{len(url_list)}: {url}")
        html = None
        for attempt in range(max_retries):
            try:
                # 关键:使用 with closing 确保连接体被释放
                with closing(session.get(url, stream=True)) as response:
                    # 使用stream模式,避免大响应体一次性读入内存
                    response.raise_for_status()
                    html = response.content  # 或 .text
                    # 这里处理你的html数据,比如解析、保存
                    # process_html(html)

                # 成功抓取后跳出重试循环
                break

            except requests.exceptions.RequestException as e:
                print(f"  Attempt {attempt+1} failed for {url}: {e}")
                if attempt < max_retries - 1:
                    time.sleep(delay * 2 ** attempt)  # 指数退避
                else:
                    print(f"  Failed after {max_retries} attempts.")
                    html = None
            except Exception as e:
                print(f"  Unexpected error: {e}")
                html = None
                break  # 非网络错误,直接跳出

        # 每处理N个请求后,主动清理一次,并暂停。这是关键!
        if (i + 1) % 100 == 0:  # 你可以调整这个数字,比如200
            print(f"  --- 已处理 {i+1} 个,主动清理内存并休息 ---")
            # 1. 关闭session的适配器连接池(可选但有效)
            session.close()
            # 2. 强制垃圾回收
            gc.collect()
            # 3. 短暂休息,模拟人工操作,也释放系统资源
            time.sleep(5)
            # 4. 重新创建session(如果上面关闭了)
            session = requests.Session()
            session.request = lambda method, url, **kwargs: requests.Session.request(
                session, method, url, timeout=(10, 30), **kwargs
            )

        # 每个请求之间的基础间隔
        time.sleep(delay)

    # 最终关闭
    session.close()
    print("所有任务完成。")

# 使用示例
if __name__ == '__main__':
    # 假设你的url列表
    my_urls = [f"http://example.com/page/{i}" for i in range(1000)]
    robust_crawler(my_urls, delay=0.5)

核心排查点:

  1. 连接池耗尽:Spyder(或任何IDE)里长时间运行脚本,requests 的默认连接池可能被占满。上面的代码通过定期 session.close() 和重建来清空。
  2. 内存泄漏:如果解析库(如 BeautifulSoup)或你的代码持续创建未被释放的对象,内存会慢慢涨上去。gc.collect() 和定期重启“处理循环”能缓解。
  3. 网络不稳定/服务器限制:加入了重试机制和指数退避,避免因偶发失败卡住。
  4. Spyder 自身限制:在IDE里跑长进程有时不如命令行稳定。你可以在代码里把关键日志(如处理到第几个URL)打印出来,这样即使卡住,也能看到最后卡在哪里。

给你的直接建议:

先别管你原来的代码,用上面这个模板替换你的抓取循环,重点看 每处理N个请求后 那段逻辑。把 process_html(html) 换成你自己的处理函数。如果问题解决,那基本就是资源管理的问题;如果还卡,可能是你处理HTML的那部分代码有性能瓶颈。

一句话总结:用 with closing 管理连接,并定期休息、清理内存和重建会话。


多 print 一些信息, 看停留在什么地方了。。。

回到顶部