利用python自动化爬取的各大城市的天气数据,有如下字段:日期、星期、天气、最高温度、最低温度、风向、风级、PM2.5和AQI等等。
城市包含:徐州、苏州、北京、上海、深圳、广州、杭州、南京、西安、成都、重庆、武汉、天津。
时间范围为:2016年1月1日~2025年7月1日。
如有需要可以厚台私信我我呀!
如有具体要求的城市、时间也可以私聊我帮忙爬取。
需要项目源码也可以!
def safe_request(method, url, headers=None, **kwargs):
for _ in range(10):
proxy = next(proxy_cycle)
proxies = {'http': f'http://{proxy}', 'https': f'http://{proxy}'}
try:
if method == 'GET':
resp = requests.get(url, headers=headers, proxies=proxies, timeout=10, **kwargs)
else:
resp = requests.post(url, headers=headers, proxies=proxies, timeout=10, **kwargs)
resp.raise_for_status()
return resp
except Exception as e:
print(f"⚠️ 代理请求失败,切换中... {proxy} -> {e}")
time.sleep(random.uniform(1, 2))
raise RuntimeError("❌ 多次请求失败")
# ========== 城市和年月 ==========
cities = ['xuzhou', 'suzhou', 'beijin', 'shanghai', 'shenzhen', 'guangzhou', 'hangzhou', 'nanjin', 'xian', 'chengdu', 'chonqing', 'wuhan', 'tianjin']
years = range(2016, 2026)
months = range(1, 13)
os.makedirs('debug_html', exist_ok=True)
for city in cities:
for year in years:
for month in months:
ym = f"{year}{month:02d}"
print(f"\n📦 正在爬取:{city} {ym} ...")
referer_url = f'https://lishi.tianqi.com/{city}/{ym}.html'
headers['Referer'] = referer_url
# -------- 前半月 --------
try:
resp = safe_request('GET', referer_url, headers=headers)
selector = parsel.Selector(resp.text)
lis = selector.css('.inleft .tian_three .thrui li')
if not lis:
print(f"⚠️ 前半月无数据:{city} {ym}")
for li in lis:
try:
date_info = li.css('.th200::text').get().split(' ')
w_info = li.css('.th140::text').getall()
dit = {
'城市': city,
'日期': date_info[0],
'星期': date_info[1],
'天气': w_info[2] if len(w_info) > 2 else '',
'白天天气': '',
'夜间天气': '',
'最高温度': w_info[0].replace('℃', ''),
'最低温度': w_info[1].replace('℃', ''),
'风向': w_info[3].split()[0] if len(w_info) > 3 else '',
'风级': w_info[3].split()[1] if len(w_info) > 3 and len(w_info[3].split()) > 1 else '',
'湿度': '', '空气质量指数 (AQI)': '', 'PM2.5': '', '气压 (hPa)': '',
'日出时间': '', '日落时间': '', '紫外线指数': '', '降水概率 (%)': '',
'降水强度 (mm)': '', '能见度 (km)': '', '平均云量': '', '全天辐射 (W/m²)': '',
'图片编码(白天)': '', '图片编码(夜间)': '',
}
csv_writer.writerow(dit)
except Exception as e:
print(f"⚠️ 前半月单条失败:{city} {ym} -> {e}")
except Exception as e:
print(f"❌ 前半月抓取失败:{city} {ym} -> {e}")
continue
time.sleep(random.uniform(1, 2))
# -------- 后半月 --------
for attempt in range(3):
try:
crypte = sign_func.call('GetSign', int(ym), city)
link = f'https://lishi.tianqi.com/monthdata/{city}/{ym}'
post_resp = safe_request('POST', link, headers=headers, data={'crypte': crypte})
if not post_resp.text.strip().startswith('['):
raise ValueError("非 JSON 返回")
json_data = post_resp.json()
for item in json_data:
dit = {
'城市': city,
'日期': item.get('date_str', ''),
'星期': item.get('week', ''),
'天气': item.get('weather', ''),
'白天天气': item.get('weather_day', ''),
'夜间天气': item.get('weather_night', ''),
'最高温度': item.get('htemp', ''),
'最低温度': item.get('ltemp', ''),
'风向': item.get('WD', ''),
'风级': item.get('WS', ''),
'湿度': item.get('SD', ''),
'空气质量指数 (AQI)': item.get('aqi', ''),
'PM2.5': item.get('pm25', ''),
'气压 (hPa)': item.get('pres', ''),
'日出时间': item.get('sunup', ''),
'日落时间': item.get('sundown', ''),
'紫外线指数': item.get('ultraviolet', {}).get('desc', ''),
'降水概率 (%)': item.get('precipitation', {}).get('probability', ''),
'降水强度 (mm)': item.get('precipitation', {}).get('max', ''),
'能见度 (km)': item.get('visibility', {}).get('avg', ''),
'平均云量': item.get('cloudrate', {}).get('avg', ''),
'全天辐射 (W/m²)': item.get('dswrf', {}).get('max', ''),
'图片编码(白天)': item.get('img_num_day', ''),
'图片编码(夜间)': item.get('img_num_night', ''),
}
csv_writer.writerow(dit)
break
except Exception as e:
print(f"⚠️ 后半月尝试失败:{city} {ym} -> {e}")
time.sleep(random.uniform(1.5, 3.0))
f.close()
print("\n✅ 所有城市数据爬取完成!")