数据采集与融合技术作业2
数据采集与融合技术作业2
各任务核心代码片段、实现思路与心得体会
任务一:中国气象网7日天气预报爬取与存储
要求:在中国气象网(http://www.weather.com.cn)给定城市集的7日天气预报,并保存在数据库。
核心代码片段(抓取与解析核心)
def crawl_weather(city_name, city_id): url = f"http://www.weather.com.cn/weather/{city_id}.shtml" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9" } try: response = requests.get(url, headers=headers, timeout=15) response.encoding = "utf-8" soup = BeautifulSoup(response.text, "html.parser") forecast_list = soup.select("ul.t.clearfix li") or soup.select("div#7d ul li") if not forecast_list: print(f"爬取{city_name}失败:未找到天气预报数据") return None weather_data = [] for day in forecast_list[:7]:
date_tag = day.select_one("h1") or day.select_one("div.date") if not date_tag: continue date = date_tag.text.strip() weather_tag = day.select_one("p.wea") if not weather_tag: continue weather = weather_tag.text.strip() # 提取温度(兼容格式,避免索引越界) temp = day.select_one("p.tem").text.strip() temp_parts = temp.replace("℃", "").split("/") # 处理温度拆分异常 if len(temp_parts) >= 2: temp_max = int(temp_parts[0].strip()) if temp_parts[0].strip().isdigit() else 0 temp_min = int(temp_parts[1].strip()) if temp_parts[1].strip().isdigit() else 0 else: temp_val = int(temp_parts[0].strip()) if temp_parts[0].strip().isdigit() else 0 temp_max = temp_val temp_min = temp_val - 4 weather_data.append({ "city": city_name, "date": date, "weather": weather, "temp_min": temp_min, "temp_max": temp_max }) return weather_data except Exception as e: print(f"爬取{city_name}失败:{str(e)}") return None |
实现思路
- URL规则分析:通过浏览不同城市天气页面,发现URL中包含唯一城市ID(如北京101010100),因此建立城市-ID映射表实现批量爬取。
- 页面结构定位:用F12查看元素,发现7日天气预报被包裹在ul.t.clearfix列表中,每个li对应一天数据。
- 字段提取逻辑:日期在h1标签、天气在p.wea、温度在p.tem,直接通过标签和类名定位。
- 存储设计:创建SQLite表时设置(city, date)唯一约束,避免重复存储同一天气数据。
心得体会
- 静态页面爬取关键:对于HTML结构稳定的网站(如气象网),直接解析标签比找API更简单,但需注意页面编码(此处utf-8解决了中文乱码)。
- 反爬基础防御:仅设置User-Agent就可爬取气象网,说明其反爬策略较宽松,但仍需控制爬取频率(加入time.sleep(2))避免给服务器压力。
- 数据去重教训:初期未设置唯一约束导致重复存储,后来用INSERT OR IGNORE解决,这让我意识到数据库约束在爬虫中的重要性。
- 可扩展性思考:城市ID映射表可以从文件读取,未来可扩展到全国城市,只需维护ID列表即可。
地址:https://gitee.com/oozyt/datacollecting/tree/master/%E4%BD%9C%E4%B8%9A2/%E4%BB%BB%E5%8A%A11
任务二:东方财富网股票信息爬取与存储
要求:用requests和BeautifulSoup库方法定向爬取股票相关信息,并存储在数据库中。
候选网站:东方财富网:https://www.eastmoney.com/
技巧:在谷歌浏览器中进入F12调试模式进行抓包,查找股票列表加载使用的url,并分析api返回的值,并根据所要求的参数可适当更改api的请求参数。根据URL可观察请求的参数f1、f2可获取不同的数值,根据情况可删减请求的参数。
核心代码片段(API解析核心)
def crawl_stock_data(page): # 抓包找到的股票列表API url = f"https://push2.eastmoney.com/api/qt/clist/get?pn={page}&pz=20&..." headers = {"User-Agent": "Mozilla/5.0 ..."}
response = requests.get(url, headers=headers) # 处理JSONP格式(API返回的是jQuery包裹的JSON,需剥离函数名) json_str = response.text.split("(")[1].rsplit(")", 1)[0] data = json.loads(json_str)
stock_list = [] # 解析API返回的字段(通过分析response确定f12=代码、f14=名称等映射关系) for item in data["data"]["diff"]: stock_list.append({ "code": item["f12"], # 股票代码 "name": item["f14"], # 股票名称 "price": item["f2"] / 100, # 最新价(API返回值需除以100还原真实值) "change_rate": item["f3"] / 100 # 涨跌幅(百分比,同样需除以100) }) return stock_list |
实现思路
- API发现过程:通过F12的Network面板,在页面滚动加载股票列表时,捕获到push2.eastmoney.com的API请求,其返回包含完整股票数据。
- 参数分析:发现pn是页码、pz是每页条数,修改这两个参数可控制爬取范围。
- 字段映射:API返回的是f12、f14等缩写字段,通过对比页面显示值,确定f12=代码、f2=最新价等映射关系,且数值需除以100还原。
- JSONP处理:由于API返回的是jQuery(...)包裹的JSONP格式,需先切割字符串提取纯JSON。
心得体会
- 动态页面爬取技巧:对于滚动加载的内容(如股票列表),直接解析HTML很难获取完整数据,通过F12抓包找API是更高效的方案。
- API字段解读:初期因不清楚f12、f3等字段含义,通过对比页面显示值和API返回值,才确定映射关系,这个过程锻炼了数据匹配能力。
- 数值处理细节:API返回的价格是乘以100的整数(如1567代表15.67元),忽略这一点会导致数据错误,说明爬虫必须重视数据格式验证。
- 反爬升级应对:连续爬取多页后出现过403,通过添加随机User-Agent和爬取间隔解决,意识到动态页面的反爬策略通常比静态页面严格。
地址:https://gitee.com/oozyt/datacollecting/tree/master/%E4%BD%9C%E4%B8%9A2/%E4%BB%BB%E5%8A%A12
任务三:中国大学2021排名爬取与存取
要求:爬取中国大学2021主榜(https://www.shanghairanking.cn/rankings/bcur/2021)所有院校信息,并存储在数据库中,同时将浏览器F12调试分析的过程录制Gif加入至博客中。
技巧:分析该网站的发包情况,分析获取数据的api
网站静态Payload: /_nuxt/static/.../payload.js 这类文件是典型的例子。这些文件是构建时预生成的,数据就藏在其中
核心代码片段(API请求与存储核心)
使用api常用格式可以使出网页api
def crawl_university_rank(): url = "https://www.shanghairanking.cn/api/pub/v1/bcur?bcur_type=11&year=2021" headers = { "User-Agent": "Mozilla/5.0 ...", "Referer": "https://www.shanghairanking.cn/rankings/bcur/2021" # 关键:添加Referer绕过防盗链 }
response = requests.get(url, headers=headers) data = response.json() # 核心:提取排名数据(API返回结构中rankings数组包含所有院校信息 return data["data"]["rankings"] def save_to_db(rankings): conn = sqlite3.connect("university_rank.db") cursor = conn.cursor() # 核心:存储关键字段(排名、学校名、省市、类型、总分) for item in rankings: cursor.execute(''' INSERT INTO university_2021 (rank, name, province, type, score) VALUES (?, ?, ?, ?, ?) ''', (item["ranking"], item["univNameCn"], item["province"], item["univCategory"], item["score"])) conn.commit() conn.close() |
实现思路
- API定位:在上海软科排名页面,通过F12的XHR过滤器,找到加载排名数据的API(/api/pub/v1/bcur),其参数year=2021指定了年份。
- 防盗链处理:初期请求API返回403,分析请求头发现需要Referer字段(必须包含官网域名),添加后成功获取数据。
- 数据结构解析:API返回的JSON中,data.rankings数组包含所有院校信息,直接提取ranking(排名)、univNameCn(学校名)等字段即可。
- 存储设计:创建表时包含排名、学校、省市、类型、总分5个核心字段,与页面展示的信息一一对应。
心得体会
- Referer防盗链应对:这是首次遇到需要验证Referer的网站,通过对比浏览器正常请求和代码请求的 headers,才发现缺失的关键字段,让我明白抓包时需完整复制请求头。
- API稳定性优势:相比解析HTML,直接调用API获取JSON数据更高效,且字段结构清晰(如univNameCn明确是中文校名),减少了解析错误。
- 数据可视化配合:按要求录制了F12分析过程的GIF,这个过程让我意识到:爬虫不仅要写代码,还要能清晰展示数据来源分析过程,这是技术文档的重要部分。
- 异常处理补充:后期发现部分学校的score字段为空,添加了try-except处理(float(item["score"]) if item["score"] else 0),使程序更健壮。
地址:https://gitee.com/oozyt/datacollecting/tree/master/%E4%BD%9C%E4%B8%9A2/%E4%BB%BB%E5%8A%A13

浙公网安备 33010602011771号