龙空技术网

结合 Linux 定时任务实现程序失败续借问题

埋头苦干的小码农 103

前言:

眼前各位老铁们对“anacondapython36”大体比较珍视,兄弟们都想要知道一些“anacondapython36”的相关知识。那么小编同时在网络上汇集了一些有关“anacondapython36””的相关内容,希望我们能喜欢,咱们快快来学习一下吧!

背景

有目前一个需求,需要持续获取某个网站的界面数据,将数据存储进入数据库,用来绘出界面变化曲线。

网站特点及需要解决的问题网站各个接口仅支持h2协议的请求。令牌信息则存在一个动态的js文件中,js文件每隔一段时间名字就会变动,且令牌信息在每隔一段时间后也会变动,故需要动态获取js文件名与令牌信息。每一次请求需要携带上一次请求的时间,来获取在上一次请求到此次请求时间段内,界面的数据变化情况;如果某一次请求未获取数据,则下一次请求继续携带上一次请求的时间来获取数据。网站存在一个未知问题,当一次死循环请求持续在三小时左右时,则后续请求均会变为 400系列 或者 500系列,不管是清空cookie或者重新获取令牌均无法解决掉该问题。开发环境

Python3.6
问题解决方案因为网站仅支持h2协议的请求故在使用 requests 模块则无法正常发起请求,故改为使用 httpx 模块发起请求因为动态度的令牌存在于一个动态的js文件中,故需要先获取此动态的js文件,再考虑在每隔一段时间发起一次请求来获取这个动态的js文件。
# 示例代码# 每位每隔一段时间就要调用一下以下逻辑故将其封装为一个函数,这样调用起来方便import h2import httpximport regexdef get_dynamic_js_url():    req_get_client = httpx.Client(http2=True, timeout=None)    home_page_url = ";  # 目标网站主页    home_page_data = req_get_client.get(home_page_url, headers=headers)  # 在这里后续请求可能会出现失败或者 400 500 的错误代码    # 获取到 请求首页返回的原始数据后 根据正则获取动态js的名字    keepCommons = regex.compile('(\/_xxx.{40}dynamic_js.*?\.js)')  # 取出来的名字大概 为 _xxx/xxxx/xxx/dynamic_js2312312j31k2j3jk1.js    # 再获取到 动态js路径后拼接后进行请求来获取动态js的原始数据,使用正则取出令牌信息并返回    return tokenid  # 返回令牌信息3.
因为每次请求都需要携带上一次请求时间故需要一个开关来控制是否重新获取时间
# 示例代码# 因每隔几秒钟获取一次数据这里是一个持续的可以认为是死循环的一段逻辑def post_query_spider(tokenid, current_time):    # 接收 在动态js中解析出来的tokenid 并将其放入 headers头中    headers["tokenid"] = tokenid    # 数据目标地址    target_url = ";    req_post_client = httpx.Client(http2=True, timeout=None)    if current_time:	eventTimestamp_GtKEY = 0    else:	eventTimestamp_GtKEY = 1  # 标识用以判断定时获取最新的utc时间    while True:	if eventTimestamp_GtKEY:		current_time = str(datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc))  # 获取当前UTC时间	data = {}  # 需要携带的请求参数	target_data = req_post_client.post(url=target_url, headers=headers, data=data)	# 判定一下数据是否是自己所需	if not target_data: # 如果数据返回为空或者不是自己所需的则时间戳不变            eventTimestamp_GtKEY = 0	# 对返回的数据进行结构化处理并存入数据库	eventTimestamp_GtKEY = 1  # 如果数据正常则重新获取时间
因为会在某一次请求的时候会出现400或者500系列的报错亦或者直接无数据返回,再暂停一段时间重新请求也无法解决

解决办法

a)对上面的每一个请求进行 try

try:    target_data = req_post_client.post(url=target_url, headers=headers, data=data)except Exception as e:    return None  # 不管发生什么问题都直接跳出回到主函数

b:在主函数进行判断如果发现有返回值为None的,则记录当前时间

def time_stamp_parse(nexttime):    nexttime = int(nexttime)    month = DT.utcfromtimestamp(nexttime).month    day = DT.utcfromtimestamp(nexttime).day    hour = DT.utcfromtimestamp(nexttime).hour    minute = DT.utcfromtimestamp(nexttime).minute    crontabStr = "{} {} {} {} *".format(minute, hour, day, month)    return crontabStrdef corn_spider(nexttime, current_time):    """    :param nexttime: 下一次执行的时间    :param current_time: 上一次请求携带的时间    :return:    """    cornStr = time_stamp_parse(nexttime)    shStr = '(crontab -l 2>/dev/null; echo "{} cd /home/xxx/xxxxx; /home/ubuntu/anaconda3/envs/python36/bin/python  当前任务脚本.py --current_time "{}" >> /home/xxx/xxxx/logs/当前任务脚本.log") | crontab -'.format(cornStr, current_time)    status = subprocess.call(shStr, shell=True)def main(current_time)  # 如果是定时任务启动会携带上一次失败时所携带的id    while True:	tokenid = get_dynamic_js_url() # 获取tokenid逻辑	if current_time:	    isNone, current_time = post_query_spider(tokenid, current_time)  # 这个函数中有个死循环来持续获取数据 出问题则	if not isNone: # 如果是None的话则进行设置定时任务以便下一次继续执行	    startTime = int(time.time()) + 360  # 获取下次执行时间戳	    corn_spider(startTime, current_time)  # 设置定时任务
因为长时间下会存在大量的定时任务,故需要有一个定时任务来定时清理失效的任务
import datetimeimport subprocesscrontab_str = subprocess.check_output("crontab -l", shell=True)crontab_str = crontabStr.decode('utf-8')crontab_list = crontabStr.split("\n")# 当前时间new_utc_time = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)task_year = datetime.datetime.now().yearfor crontab in crontab_list:    if "目标函数.py" in crontab:        TaskMinute, TaskHour, TaskDate, TaskMoon = crontab.split(" ")[:4]        # 任务执行时间        TaskExecutionTime = datetime.datetime(int(TaskYear), int(TaskMoon), int(TaskDate), int(TaskHour), int(TaskMinute), tzinfo=datetime.timezone.utc)        if (TaskExecutionTime-newUtcTime).seconds < 0:            crontabSed = "{} {} {} {}".format(TaskMinute, TaskHour, TaskDate, TaskMoon)            sedSh = "sudo sed -i '/{}/d' /var/spool/cron/crontabs/ubuntu".format(crontabSed)            subprocess.call(sedSh,  shell=True)
结尾

在实际开发中,会遇到各种各样的奇葩问题,如果问题走直线无法解决,那么就要想其它的办法进行解决,就像这个小程序一样,遇到了未知问题,即使在重置了所有状态后还是无法获取数据,只有在重启之后才可以重新获取数据,故想到了动态定时任务与程序相结合,因为这样随之时间的推移会设置大量的定时任务,故会考虑如何定时删除一些定时任务,这一整套流程下来就是解决问题的方法,有了思路便可以根据思路来实现这个想法,便可以解决目前所遇到的问题。

所以写代码解决问题思路是最重要的,思考好一二三步,并找到每一步所需的技术和知识,学习知识来解决自己遇到的问题。

以下是我在学习时看过的书还算不错,网上都有pdf版的,如果喜欢纸质版可以考虑买一本来看,不过建议pdf版足够用。

标签: #anacondapython36