一、基本架构

1、URL管理器:爬虫的调度中枢
核心职责
功能 | 说明 |
---|
URL去重 | 防止重复抓取 |
URL优先级管理 | 控制抓取顺序(广度优先/深度优先) |
断点续爬支持 | 持久化存储抓取状态 |
分布式协同 | 多节点共享URL队列 |
2、网页下载器:数据获取的引擎
功能 | 实现方式 |
---|
请求模拟 | 随机User-Agent/Cookie管理 |
代理管理 | 代理IP池+自动检测可用性 |
流量控制 | 自适应QPS限制 |
异常处理 | 自动重试+熔断机制 |
反反爬策略 | TLS指纹伪装+请求随机延时 |
3、网页解析器:信息提取的核心
技术 | 适用场景 | 示例 |
---|
XPath | 结构化页面 | //div[@class='content'] |
CSS选择器 | 简单页面 | div.content > p.text |
正则表达式 | 非结构化文本 | \d{4}-\d{2}-\d{2} |
OCR识别 | 验证码/图片文字 | Tesseract引擎 |
深度学习 | 复杂语义抽取 | BERT模型 |
二、代码展示
数据展示

代码展示
"""
烟台市历史天气数据爬取及可视化
功能:从天气网站爬取2011-2012年数据,生成Excel表格并绘制气温折线图
"""# ==================== 库导入 ====================
import requests # 网络请求库
from bs4 import BeautifulSoup # HTML解析库
import pandas as pd # 数据处理库
import time # 时间模块(用于延迟)
import random # 随机数模块(用于随机延迟)
import matplotlib.pyplot as plt # 绘图库
import matplotlib.dates as mdates # 日期格式化模块# ==================== 数据爬取函数 ====================
def save_a_month_data(url):"""获取单个月份的天气数据:param url: 目标页面URL (格式: http://lishi.tianqi.com/yantai/YYYYMM.html):return: 二维列表,包含该月每天的天气数据"""a_month = []headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"}try:# 发送带超时设置的GET请求(连接超时10秒,读取超时30秒)r = requests.get(url, headers=headers, timeout=(10, 30))r.raise_for_status() # 检查HTTP状态码,非200会抛出异常except requests.exceptions.RequestException as e:print(f"请求失败:{url},错误:{e}")return a_month # 返回空列表# 使用BeautifulSoup解析HTMLsoup = BeautifulSoup(r.text, 'html.parser')tianqi_zone = soup.find(class_='tian_three') # 定位天气数据区域try:tianqi_data = tianqi_zone.find(class_='thrui') # 定位数据列表except AttributeError:print(f"页面结构异常:{url}")return a_month# 提取每日数据(每个<li>标签代表一天)tianqi_data_a_month = tianqi_data.find_all('li')# 数据清洗处理for tianqi_data_a_day in tianqi_data_a_month:data = tianqi_data_a_day.text.split() # 按空白字符分割文本# 处理异常格式:当某天有空气质量数据时字段数为7if len(data) == 7:data.pop(1) # 移除第二个元素(原始数据中的冗余字段)a_month.append(data)return a_month# ==================== 主程序:数据爬取 ====================
all_data = [] # 存储所有月份的数据# 注意:range的结束值不包含,2011-2012年应写range(2011, 2013)
for year in range(2011, 2013): # 修正后的年份范围for month in range(1, 13):month_str = f"{year}{month:02d}" # 格式化月份为两位数(如201101)url = f"http://lishi.tianqi.com/yantai/{month_str}.html"print(f"正在抓取:{url}")# 调用爬取函数并合并数据a_month_data = save_a_month_data(url)all_data += a_month_data# 随机延迟(1~3秒)防止触发反爬机制time.sleep(random.uniform(1, 3))# ==================== 数据保存 ====================
# 注意:如果桌面已存在weather_data.xlsx文件,需先关闭该文件
df = pd.DataFrame(all_data, columns=["日期", "最高温", "最低温", "天气", "风力风向", "空气质量"])
df.to_excel("C:/Users/lenovo/Desktop/weather_data.xlsx", index=False)
print("数据已保存至桌面")# ==================== 数据可视化 ====================
# 重新读取数据(避免修改原始数据影响后续操作)
df = pd.read_excel("C:/Users/lenovo/Desktop/weather_data.xlsx")# ---------- 数据预处理 ----------
# 1. 转换日期格式(将字符串转为datetime类型)
df['日期'] = pd.to_datetime(df['日期'])
# 2. 清洗温度数据(去除℃符号并转为浮点数)
df['最高温'] = df['最高温'].str.replace('℃', '').astype(float)
df['最低温'] = df['最低温'].str.replace('℃', '').astype(float)# 筛选指定时间段数据
mask = (df['日期'] >= '2011-01-01') & (df['日期'] <= '2012-12-31')
df_filtered = df.loc[mask]# ---------- 图表配置 ----------
# 设置中文字体(Windows系统使用SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题# 创建画布(单位:英寸,15x6英寸≈38x15厘米)
plt.figure(figsize=(15, 6))# ---------- 绘制折线图 ----------
# 绘制最高温曲线(颜色代码:橙红色)
plt.plot(df_filtered['日期'], df_filtered['最高温'],label='最高温', color='#FF4500', linewidth=1.2)
# 绘制最低温曲线(颜色代码:道奇蓝)
plt.plot(df_filtered['日期'], df_filtered['最低温'],label='最低温', color='#1E90FF', linewidth=1.2)# ---------- 图表美化 ----------
plt.title("烟台市气温变化 (2011-2012)", fontsize=14, pad=20) # pad标题间距
plt.xlabel("日期", fontsize=12, labelpad=10) # labelpad标签间距
plt.ylabel("温度 (℃)", fontsize=12, labelpad=10)
plt.grid(linestyle=':', color='gray', alpha=0.7) # 虚线网格# 设置x轴日期格式
ax = plt.gca() # 获取当前坐标轴
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) # 显示年月
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=2)) # 每2个月一个主刻度
plt.xticks(rotation=45, ha='right') # 标签旋转45度,右端对齐# 添加图例(无边框,右上角位置)
plt.legend(loc='upper right', frameon=False)# 自动调整子图参数(避免标签被截断)
plt.tight_layout()# ---------- 保存与显示 ----------
# 保存高清图片(dpi=300为印刷级清晰度)
plt.savefig("C:/Users/lenovo/Desktop/2011-2012_temperature.png",dpi=300, bbox_inches='tight') # bbox_inches='tight'去除白边
plt.show() # 显示图表窗口
三、结果展示
