使用request_html爬取天气数据时程序随机终止的原因咨询
问题:使用request_html爬取Weather Underground时程序随机终止
问题描述
我尝试用request_html爬取Weather Underground的天气数据,编写了如下代码,但运行时程序会随机终止——有时在获取2016-1-20的数据后停止,有时在2016-3-20时停止。不清楚是否是r.html.render中的timeout或sleep参数导致的问题?
session = HTMLSession() flag = 0 leap_year =[2016,2020] month_30 = [4,6,9,11] for year in range(2015,2020): date =[] times = [] temp = [] dew = [] hum =[] wind=[] wind_s =[] wind_g =[] press = [] precip = [] cond = [] for month in range(1,13): for day in range(1,32): if(year in leap_year): if(month == 2 and day >29): break else: if (month == 2 and day >28): break if(month in month_30 and day >30): break d = "{}-{}-{}".format(year,month,day) start2 = time.time() url = "https://www.wunderground.com/history/daily/ca/richmond/CYVR/date/{}".format(d) r = session.get(url) r.html.render(sleep =5) soup = BeautifulSoup(r.html.html,'html.parser') table = soup.find("tbody",{'role':'rowgroup'}) row = table.find_all('tr') for r in row : n = 0 date.append(d) for cell in r.find_all('td'): if(n == 0): times.append(cell.text) elif(n == 1): temp.append(cell.text) elif(n == 2): dew.append(cell.text) elif(n == 3): hum.append(cell.text) elif(n == 4): wind.append(cell.text) elif(n == 5): wind_s.append(cell.text) elif(n == 6): wind_g.append(cell.text) elif(n == 7): press.append(cell.text) elif(n == 8): precip.append(cell.text) elif(n == 9): cond.append(cell.text) n+=1 end2 = time.time() print("it took : {} for one day ".format(end2-start2)) print(d) end = time.time() print(end-start)
可能的原因分析
程序随机终止大概率不是单纯的sleep参数问题,而是以下几个因素共同导致的:
缺少异常处理机制
你的代码没有任何错误捕获:如果某天的请求失败(比如网络波动)、页面结构变化(找不到<tbody role="rowgroup">),或者渲染过程中Chrome进程崩溃,程序会直接抛出AttributeError、ConnectionError等异常并终止运行。这是最常见的终止原因。render参数配置不足r.html.render()默认的timeout是30秒,但如果页面加载缓慢(比如反爬机制导致加载延迟),可能会超时;另外,频繁渲染页面可能导致Chrome子进程崩溃,没有设置重启机制的话,后续渲染会直接失败。反爬机制触发
Weather Underground会检测频繁的自动化请求,当你的请求频率过高时,可能会返回空白页面、验证码页面或者直接拒绝连接,导致后续解析失败触发异常。
解决方案
针对这些问题,我给你调整了代码,加入了关键改进:
from requests_html import HTMLSession from bs4 import BeautifulSoup import time import random session = HTMLSession() leap_year = [2016, 2020] month_30 = [4, 6, 9, 11] # 初始化所有数据列表在循环外,避免每次年份重置(如果需要按年份拆分可以调整) all_data = { "date": [], "times": [], "temp": [], "dew": [], "hum": [], "wind": [], "wind_s": [], "wind_g": [], "press": [], "precip": [], "cond": [] } start = time.time() for year in range(2015, 2020): for month in range(1, 13): for day in range(1, 32): # 日期合法性判断 if year in leap_year: if month == 2 and day > 29: break else: if month == 2 and day > 28: break if month in month_30 and day > 30: break d = f"{year}-{month}-{day}" url = f"https://www.wunderground.com/history/daily/ca/richmond/CYVR/date/{d}" start2 = time.time() try: # 发送请求,设置超时并检查状态码 r = session.get(url, timeout=15) r.raise_for_status() # 触发HTTP错误(如403/500) # 渲染页面:增加超时时间,添加--no-sandbox避免进程崩溃,用随机sleep降低反爬风险 r.html.render( sleep=random.uniform(4, 6), timeout=40, options={"args": ["--no-sandbox", "--disable-dev-shm-usage"]} ) # 解析页面 soup = BeautifulSoup(r.html.html, 'html.parser') table = soup.find("tbody", {'role': 'rowgroup'}) if not table: print(f"警告:日期{d}未找到目标表格,跳过") continue rows = table.find_all('tr') for row in rows: cells = row.find_all('td') if len(cells) < 10: print(f"警告:日期{d}的行数据不完整,跳过该行") continue all_data["date"].append(d) all_data["times"].append(cells[0].text.strip()) all_data["temp"].append(cells[1].text.strip()) all_data["dew"].append(cells[2].text.strip()) all_data["hum"].append(cells[3].text.strip()) all_data["wind"].append(cells[4].text.strip()) all_data["wind_s"].append(cells[5].text.strip()) all_data["wind_g"].append(cells[6].text.strip()) all_data["press"].append(cells[7].text.strip()) all_data["precip"].append(cells[8].text.strip()) all_data["cond"].append(cells[9].text.strip()) end2 = time.time() print(f"完成日期{d},耗时: {end2-start2:.2f}秒") # 增加随机延迟,避免被反爬识别 time.sleep(random.uniform(2, 4)) except Exception as e: print(f"日期{d}处理失败,错误信息: {str(e)}") # 出错后延长延迟,降低被拉黑风险 time.sleep(random.uniform(5, 8)) continue end = time.time() print(f"总耗时: {end-start:.2f}秒")
关键改进点说明
- 全流程异常捕获:用
try-except包裹请求、渲染、解析的核心逻辑,出错时仅打印信息并继续执行,不会终止整个程序。 - 优化渲染配置:延长
timeout到40秒,添加--no-sandbox和--disable-dev-shm-usage参数避免Chrome进程崩溃(尤其在Linux服务器环境),同时用随机sleep代替固定值,更难被反爬检测。 - 请求合法性校验:用
r.raise_for_status()提前捕获HTTP错误状态码,避免无效页面进入解析环节。 - 数据完整性检查:验证表格和行数据是否存在,避免因页面结构变化导致的
AttributeError。 - 动态延迟策略:正常请求后用短随机延迟,出错后用长延迟,降低被反爬机制拉黑的概率。
内容的提问来源于stack exchange,提问作者Lucian
相关产品推荐
相关产品推荐

