python爬取各大城市的天气数据【附代码分享】

利用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✅ 所有城市数据爬取完成!")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值