数据采集第二次作业

数据采集与融合技术-第二次作业

项目 内容
课程 2026数据采集与融合技术实践
作业要求 作业二
Git 仓库 aarctic
学号姓名 102402110 姜思源

一、作业1代码与图片:

要求:在中国气象网( http://www.weather.com.cn )给定城市集【3-5个城市】的7日天气预报,并保存在数据库。
Gitee文件夹链接:作业1

1.1 作业代码与运行结果

(1)实现思路

  1. 中国天气网每个城市对应一个静态页面,url 形如 https://www.weather.com.cn/weather/城市代码.shtml(北京 101010100、南京 101190101、徐州 101190801、福州 101230101)。
  2. 用 F12 → 元素检查分析页面结构:7 天预报数据在 <ul class="t clearfix"> 标签下的 7 个 <li> 中,其中 <h1> 是日期、<p class="wea"> 是天气现象、<p class="tem"> 里是温度、<p class="win"> 里是风向风力。
  3. 用 requests 携带 User-Agent 请求页面,BeautifulSoup 解析出 7 天数据(日期、天气、温度、风向风力),逐条插入 SQLite 数据库 weather.db。
  4. 表结构设置了 UNIQUE(city, date) 联合唯一约束,配合 INSERT OR IGNORE 插入:同一城市同一天只会存一条记录,脚本重复运行也不会产生重复数据。
  5. 注意点: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中的运行截图:

image

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

image

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)实现思路

  1. 打开东方财富行情中心行业板块页 https://quote.eastmoney.com/center/boardlist.html#industry_board,按 F12 → Network(网络) → Fetch/XHR 过滤,在页面上点击"银行"板块。
  2. 在请求列表中找到返回股票列表的接口 push2.eastmoney.com/api/qt/clist/get,查看其 url 和参数,Preview 面板可见返回为 json,data.diff 数组里就是每只股票的数据。
  3. 分析各请求参数含义:
    • 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 量比
  4. 用 requests 携带 User-Agent 和 Referer: https://quote.eastmoney.com/(东财有防盗链,不带 Referer 拿不到数据)请求接口,resp.json() 解析后遍历入库。
  5. 建表时给 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中的运行截图:

image

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

ca66e5c3a7d9b5501be6a21345f571e5

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)运行结果

3.2 作业心得

四、作业总结

posted @ 2026-11-07 17:19  Cactus061  阅读(11)  评论(0)    收藏  举报