【Python】如何监控Python爬虫进程,实现异常重启或异常通知?

1.项目环境

基于 Python3.7 ,使用 Scrapy框架,正常抓取某网站搜索指数排名。

2.需求背景

由于网络时好时坏,有时候会遇到timeout这种延时错误,出错之后爬虫便会停止,如果没有第一时间重启,或者是排错,抓取效率便会十分低下。所以便想着有没有解决方法(爬虫项目是同事写的,跑在我电脑上,实习生打杂就完事了

3.解决思路

  • 1.利用另外一个Python程序,监控该爬虫进程是否正常进行,同理也可以扩展到监控其他的程序是否正常运行。

    • 2.利用Scrapy框架自身提供的中间件进行错误处理

3.1 进程监控

实现进程监控需要解决以下问题

  1. 如何用python获取电脑中运行的进程?
  2. 如何知道要监控的进程是否存活?

    第一个问题使用python提供的os库可以解决

1
os.popen('tasklist')

tasklist : 输出电脑上当前所执行的程序名已经进程编号PID

该方法类似与在cmd命令行下所执行的效果一样,会将在cmd下执行的结果输出到缓冲流,因此我们也需要从缓冲流中把结果读出来。

1
os.popen('tasklist').read()

第二个问题,我们可以使用新建子进程来解决这个问题

1
2
3
4
5
6
7
8
9
10
from multiprocessing import Process
from scrapy import cmdline

def execute_spider()
cmdline.execute('scrapy crawl my_spider'.split())

my_pro = Process(target=executeSpider)
my_pro.start()
print("pid:%s =" % my_pro.getpid())
# 在这里可以获得pid,可以使用进程通信方式把pid传递过去,我比较简单,使用的文件存储pid,监控程序读文件就好了

在这里使用getpid()便可以获得该爬虫的进程号,结合之前的命令,便可以知道当前程序是否正常运行。

1
2
# 从tasklist的输出中,统计该pid的数量,从而判定该进程是否存在
isAlive = os.popen('tasklist').read().count(pid) != 0

3.2 Scrapy中间件之异常处理

在Scrapy中存在两种中间件,一种是SpiderMiddleWare(蜘蛛中间件),一种是DownloaderMiddlerWare(下载器中间件)。

  • 蜘蛛中间件: Scrapy中Engine组件与Spider交互经过的中间件,如果蜘蛛发生的异常,就在这个里面处理,比如在解析数据的时候发生的错误。对应的方法如下:
1
2
3
4
5
6
7
8
 """
response: 异常被抛出时被处理的response对象
exception: 抛出的异常
spider: 抛出该异常的spider对象
"""
def process_spider_exception(self, response, exception, spider):
# 相应处理
pass
  • 下载器中间件:Scrapy中Engine组件与Downloader交互经过的中间件,这个处理的错误一般都是网络问题,或者服务器问题。与自己敲的蜘蛛代码关系不大。(我选的这个
    1
    2
    3
    4
    5
    6
    7
    8
    """ 
    request: 产生异常的request对象
    exception: 抛出的异常对象
    spider: 产生异常的request对象的spider对象
    """
    def process_exception(self, request, exception, spider):
    # 相应处理
    pass

如果使用中间件,别忘记在setting.py文件中开启中间件

1
2
3
DOWNLOADER_MIDDLEWARES = {
'MySpider.middlewares.MyDownloaderMiddleware': 543,
}

or 蜘蛛中间件

1
2
3
SPIDER_MIDDLEWARES = {
'MySpider.middlewares.MySpiderMiddleware': 543,
}

4.相应处理

现在已经知道进程是否正常、爬虫出错在哪处理。那么如何处理呢?

4.1 继续运行

第一种继续运行的方式,可以使用死循环,也可以使用调度任务。
死循环实现比较简单,在同一个代码中,开启一个进程执行Scrapy爬虫,一个进程实现监控。(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
#偷懒使用死循环的方式
while True:
p = os.popen('tasklist')
pid = getPid() # 写个读文件的方法
isAlive = p.read().count(pid) != 0
count = count +1
print( "%s %s 次是 %s" %(datetime.datetime.now(),count,isAlive) )
if(isAlive==False):
my_pro = Process(target=executeSpider)
my_pro.start()
with open("pid.txt","w") as f:
f.write(my_pro.getpid())
time.sleep(60)

第二种利用中间件返回Request让爬虫继续执行:

1
2
3
4
5
def process_exception(self, request, exception, spider): 
with open("exceptionLog.txt", "a") as f:
msg = "%s : %s\n" %(datetime.datetime.now(),exception)
f.write(msg) #手动记录日志
return request

4.2 通知

第一种使用窗口弹出,适合在电脑工作时跑的爬虫,这个窗口提醒会置顶,达到提醒的目的,同时程序也会阻塞,需要确认之后才会继续,当然如果有异常的话,也不需要继续了(不适合打游戏时开
需要安装win32库: pip install pywin32

1
win32api.MessageBox(0, "爬虫进程异常,请查看", "提醒", win32con.MB_SYSTEMMODAL)

第二种使用邮件通知,适合不在电脑目前时跑的爬虫。
(这个参考下一篇博文,封装一个发邮件的类,调用一下便ok。

5. 总结

使用一个工具需要慢慢的熟悉,第一个监控进程的方法是在没有弄清有两个中间件导致一直没找到解决方法所想的(囫囵吞枣学了一下Scapy。使用弹窗提醒还可以,同时也可以使用在其他可以在命令行所运行的程序上,见参考博客

借鉴博客如下
https://www.jb51.net/article/163273.htm
https://blog.csdn.net/weixin_41990342/article/details/81907263