8

我编写了一个网络爬虫,希望能够通过键盘停止。我不希望程序在我中断时死掉;它需要先将其数据刷新到磁盘。我也不想 catch KeyboardInterruptedException,因为持久数据可能处于不一致的状态。

我目前的解决方案是定义一个捕获SIGINT和设置标志的信号处理程序;主循环的每次迭代都会在处理下一个 url 之前检查这个标志。

但是,我发现如果socket.recv()在我发送中断时系统恰好正在执行,我会得到这个:

^C
Interrupted; stopping...  // indicates my interrupt handler ran
Traceback (most recent call last):
  File "crawler_test.py", line 154, in <module>
    main()
  ...
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/socket.py", line 397, in readline
    data = recv(1)
socket.error: [Errno 4] Interrupted system call

并且该过程完全退出。为什么会这样?有没有办法可以防止中断影响系统调用?

4

2 回答 2

9

socket.recv()调用 C 层中的底层 POSIX 兼容函数,该函数反过来会在进程接收到等待传入数据时recv返回错误代码。此错误代码可用于 C 端(如果您使用 C 编程)来检测返回的不是因为套接字上有更多可用数据,而是因为进程收到了. 不管怎样,这个错误代码被 Python 变成了一个异常,因为它永远不会被捕获,它会用你看到的回溯终止你的应用程序。解决方案是简单地捕获,检查错误代码,如果它等于,则静默忽略异常。像这样的东西:EINTRSIGINTrecv()recv()SIGINTsocket.errorerrno.EINTR

import errno

try:
    # do something
    result = conn.recv(bufsize)
except socket.error as (code, msg):
    if code != errno.EINTR:
        raise
于 2010-06-10T17:28:43.983 回答
3

如果您不希望套接字调用被中断,请在设置信号处理程序后禁用中断行为。

signal.signal(<your signal here>, <your signal handler function here>)
signal.siginterrupt(<your signal here>, False)

在信号处理函数中设置一些标志,例如 threading.Event(),然后在您的主处理函数中检查该标志并优雅地终止您的爬虫。

背景信息在这里:

于 2015-06-18T15:49:01.757 回答