数据采集第二次作业
数据采集与融合技术-第二次作业
| 项目 | 内容 |
|---|---|
| 课程 | 2026数据采集与融合技术实践 |
| 作业要求 | 作业二 |
| Git 仓库 | aarctic |
| 学号姓名 | 102402110 姜思源 |
一、作业1代码与图片:
要求:在中国气象网( http://www.weather.com.cn )给定城市集【3-5个城市】的7日天气预报,并保存在数据库。
Gitee文件夹链接:作业1
1.1 作业代码与运行结果
(1)实现思路
- 中国天气网每个城市对应一个静态页面,url 形如
https://www.weather.com.cn/weather/城市代码.shtml(北京 101010100、南京 101190101、徐州 101190801、福州 101230101)。 - 用 F12 → 元素检查分析页面结构:7 天预报数据在
<ul class="t clearfix">标签下的 7 个<li>中,其中<h1>是日期、<p class="wea">是天气现象、<p class="tem">里是温度、<p class="win">里是风向风力。 - 用 requests 携带 User-Agent 请求页面,BeautifulSoup 解析出 7 天数据(日期、天气、温度、风向风力),逐条插入 SQLite 数据库 weather.db。
- 表结构设置了
UNIQUE(city, date)联合唯一约束,配合INSERT OR IGNORE插入:同一城市同一天只会存一条记录,脚本重复运行也不会产生重复数据。 - 注意点:
resp.encoding = "utf-8"防止中文乱码;请求头必须携带 User-Agent 伪装浏览器;每个城市爬取后time.sleep(2)降低请求频率防反爬。
(2)完整代码
import requests
from bs4 import BeautifulSoup
import sqlite3
import time
# 1. 请求头:伪装成浏览器,天气网会拒绝没有 User-Agent 的请求
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
}
# 2. 要爬取的城市
CITY_LIST = [
{"name": "北京", "code": "101010100"},
{"name": "南京", "code": "101190101"},
{"name": "徐州", "code": "101190801"},
{"name": "福州", "code": "101230101"},
]
def init_db():
#初始化数据库
conn = sqlite3.connect("weather.db")
cur = conn.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS weather_forecast (
id INTEGER PRIMARY KEY AUTOINCREMENT,
city TEXT,
date TEXT,
weather TEXT,
temperature TEXT,
wind TEXT,
crawl_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(city, date)
)
''')
conn.commit()
conn.close()
def crawl_city_weather(city_name, city_code):
"""爬取单个城市的 7 天预报并入库"""
url = f"https://www.weather.com.cn/weather/{city_code}.shtml"
resp = requests.get(url, headers=HEADERS, timeout=10)
resp.encoding = "utf-8"
soup = BeautifulSoup(resp.text, "html.parser")
forecast_ul = soup.find("ul", class_="t clearfix")
if not forecast_ul:
print(f"{city_name} 页面数据获取失败!")
return
li_list = forecast_ul.find_all("li")[:7] # 取前 7 天
conn = sqlite3.connect("weather.db")
cur = conn.cursor()
for li in li_list:
date = li.find("h1").get_text(strip=True)
weather = li.find("p", class_="wea").get_text(strip=True)
temp = li.find("p", class_="tem").get_text(strip=True).replace("\n", "")
wind = li.find("p", class_="win").get_text(strip=True).replace("\n", "")
# 存在同城市同日期记录时自动跳过,重复运行不会重复入库
cur.execute('''
INSERT OR IGNORE INTO weather_forecast (city, date, weather, temperature, wind)
VALUES (?, ?, ?, ?, ?)
''', (city_name, date, weather, temp, wind))
conn.commit()
conn.close()
print(f"{city_name} 7天天气入库完成")
if __name__ == "__main__":
init_db()
for city in CITY_LIST:
crawl_city_weather(city["name"], city["code"])
time.sleep(2) # 防反爬延时
print("全部城市天气爬取结束\n")
# 3. 从数据库读出全部记录打印
conn = sqlite3.connect("weather.db")
cur = conn.cursor()
cur.execute("SELECT city, date, weather, temperature, wind FROM weather_forecast")
rows = cur.fetchall()
cur.close()
conn.close()
print("{:<6}{:<14}{:<14}{:<10}{}".format("城市", "日期", "天气", "温度", "风向风力"))
for row in rows:
print("{:<6}{:<14}{:<14}{:<10}{}".format(*row))
print(f"\n共 {len(rows)} 条记录,已保存到 weather.db")
(3)运行结果
在vscode中的运行截图:

用 DB Browser 打开 weather.db 的数据表:

1.2 作业心得
这是三个作业里唯一需要解析 HTML 的。最开始直接用 http 请求拿到了 301 重定向的空页面,换成 https 才正常;如果不设置 resp.encoding="utf-8",中文会变成乱码。通过 F12 的元素检查定位到 7 天数据所在的 <ul class="t clearfix">,再用 BeautifulSoup 逐层取值,我体会到"先分析页面结构、再写解析代码"比一上来就写正则高效得多。建表时我给 (city, date) 加了联合唯一约束并用 INSERT OR IGNORE 插入,这样脚本反复调试运行时数据库里也始终只有 28 条记录,不会重复堆积;最后用 DB Browser for SQLite 打开 weather.db 核对,确认 4 个城市 × 7 天数据完整无误。
二、作业2代码与图片:
要求:用requests和json解析方法定向爬取股票相关信息,并存储在数据库中。
候选网站:东方财富网:https://www.eastmoney.com/
新浪股票:http://finance.sina.com.cn/stock/
技巧:在谷歌浏览器中进入F12调试模式进行抓包,查找股票列表加载使用的url,并分析api返回的值,并根据所要求的参数可适当更改api的请求参数。根据URL可观察请求的参数f1、f2可获取不同的数值,根据情况可删减请求的参数。
参考链接:https://zhuanlan.zhihu.com/p/50099084
Gitee文件夹链接:作业2
2.1 作业代码与运行结果
(1)实现思路
- 打开东方财富行情中心行业板块页
https://quote.eastmoney.com/center/boardlist.html#industry_board,按 F12 → Network(网络) → Fetch/XHR 过滤,在页面上点击"银行"板块。 - 在请求列表中找到返回股票列表的接口
push2.eastmoney.com/api/qt/clist/get,查看其 url 和参数,Preview 面板可见返回为 json,data.diff数组里就是每只股票的数据。 - 分析各请求参数含义:
fs=b:BK0475:板块代码,BK0475 即"银行"板块(改成其他 BK 代码可抓别的板块)pn:页码;pz:每页条数(银行板块共 42 家,pz=100 一次抓完)po:排序方向(1 降序 / 0 升序);fid:排序字段(f3=涨跌幅,改为 f6=成交额、f2=最新价可获取不同排序结果)fields:返回字段,f12 代码、f14 名称、f2 最新价、f3 涨跌幅、f4 涨跌额、f5 成交量、f6 成交额、f7 振幅、f8 换手率、f9 市盈率、f10 量比
- 用 requests 携带 User-Agent 和
Referer: https://quote.eastmoney.com/(东财有防盗链,不带 Referer 拿不到数据)请求接口,resp.json()解析后遍历入库。 - 建表时给 code 加了 UNIQUE 约束,配合
INSERT OR REPLACE插入:重复运行时同一只股票只保留最新行情,不会堆积重复数据;入库后再从数据库读出打印,验证保存结果。
(2)完整代码
import requests
import sqlite3
# 1. F12 中找到的接口地址
URL = "https://push2.eastmoney.com/api/qt/clist/get"
# 2. 请求参数:每一个都对应页面上的一项可调设置,改它们就能"获取不同的值"
PARAMS = {
"pn": 1, # page number
"pz": 100, # page size
"po": 1, # 排序方向:1 降序 / 0 升序
"np": 1, # 返回格式固定值
"fltt": 2, # 浮点数格式固定值
"invt": 2, # 固定值
"fid": "f3", # 排序字段
"fs": "b:BK0475+f:!50", # 板块筛选:BK0475
"fields": "f12,f14,f2,f3,f4,f5,f6,f7,f8,f9,f10",
}
# fields 对应含义:f12 代码 f14 名称 f2 最新价 f3 涨跌幅 f4 涨跌额
# f5 成交量 f6 成交额 f7 振幅 f8 换手率 f9 市盈率 f10 量比
# 3. 请求头
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36",
"Referer": "https://quote.eastmoney.com/",
}
def init_db():
conn = sqlite3.connect("bank_stocks.db")
cur = conn.cursor()
cur.execute('''
CREATE TABLE IF NOT EXISTS bank_stock (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE,
name TEXT,
price TEXT,
change_pct TEXT,
volume TEXT,
amount TEXT
)
''')
conn.commit()
conn.close()
def crawl_bank_stocks():
"""请求东方财富银行板块接口,解析 json 并入库"""
resp = requests.get(URL, params=PARAMS, headers=HEADERS, timeout=10)
result = resp.json()
data = result["data"]
stocks = data["diff"] # diff 里是股票列表
# 兼容两种返回格式:diff 为列表,或 diff 为 {序号: 股票} 的字典
if isinstance(stocks, dict):
stocks = list(stocks.values())
print("银行板块共 {} 家上市公司\n".format(data["total"]))
conn = sqlite3.connect("bank_stocks.db")
cur = conn.cursor()
for s in stocks:
cur.execute('''
INSERT OR REPLACE INTO bank_stock (code, name, price, change_pct, volume, amount)
VALUES (?, ?, ?, ?, ?, ?)
''', (s.get("f12"), s.get("f14"), s.get("f2"),
s.get("f3"), s.get("f5"), s.get("f6")))
conn.commit()
conn.close()
def print_from_db():
conn = sqlite3.connect("bank_stocks.db")
cur = conn.cursor()
cur.execute(
"SELECT code, name, price, change_pct, volume, amount FROM bank_stock "
"ORDER BY CAST(change_pct AS REAL) DESC"
)
rows = cur.fetchall()
cur.close()
conn.close()
print("{:<8}{:<10}{:<8}{:<8}{:<12}{}".format(
"代码", "名称", "最新价", "涨跌幅", "成交量", "成交额"))
for row in rows:
print("{:<8}{:<10}{:<8}{:<8}{:<12}{}".format(*row))
print(f"\n共 {len(rows)} 条记录,已保存到 bank_stocks.db")
if __name__ == "__main__":
init_db()
crawl_bank_stocks()
print_from_db()
(3)运行结果
在vscode中的运行截图:

用 DB Browser 打开 bank_stocks.db 的数据表:

2.2 作业心得
这个作业让我真正理解了"F12 抓包找接口"的套路——页面上的表格不是写在 HTML 里的,而是前端通过 ajax 请求 push2.eastmoney.com/api/qt/clist/get 拿到 json 后渲染的。逐个分析参数后发现:fs=b:BK0475 决定抓哪个板块,fid 和 po 决定排序方式,改 fid=f6 就能按成交额排序,删减 fields 参数则可以控制返回字段,这就是老师说的"根据 URL 可获取不同的值"。中间踩的坑是:不带 Referer 请求头会被东财的防盗链拦截,返回空数据;另外返回的 diff 有时是字典有时是列表,代码里做了兼容处理。
三、作业3代码与图片:
要求:爬取中国大学2021主榜( https://www.shanghairanking.cn/rankings/bcur/2021 )所有院校信息,并存储在数据库中,同时将浏览器F12调试分析的过程录制Gif加入至博客中。
技巧:分析该网站的发包情况,分析获取数据的api
Gitee文件夹链接:
3.1 作业代码与运行结果
(1)实现思路
(2)完整代码
(3)运行结果

浙公网安备 33010602011771号