102302155张怡旋数据采集第二次作业

作业一
要求:在中国气象网(http://www.weather.com.cn)给定城市集的7日天气预报,并保存在数据库。
1.核心代码和运行结果

  # 目标城市与对应天气网编码的映射
    self.city_code = {
        "北京": "101010100",
        "上海": "101020100",
        "广州": "101280101",
        "深圳": "101280601"
    }
    # 构建目标城市的天气页面URL
    weather_url = f"http://www.weather.com.cn/weather/{self.city_code[city]}.shtml"
    try:
        # 发送HTTP请求获取页面内容
        req = urllib.request.Request(url=weather_url, headers=self.headers)
        with urllib.request.urlopen(req, timeout=10) as response:
            page_data = response.read()

        # 自动检测编码并转换为Unicode(处理中文乱码)
        dammit = UnicodeDammit(page_data, ["utf-8", "gbk"])
        page_html = dammit.unicode_markup

        soup = BeautifulSoup(page_html, "lxml")
        # 定位天气数据所在的列表项(ul.t clearfix 下的li)
        weather_items = soup.select("ul.t.clearfix li")

        # 遍历每个列表项,提取日期、天气、温度
        for item in weather_items:
            try:
                date = item.select_one("h1").text  # 日期(h1标签)
                weather = item.select_one("p.wea").text  # 天气状况(p.wea标签)
                # 温度(最高温span标签 + 最低温i标签,用"/"连接)
                temp_high = item.select_one("p.tem span").text
                temp_low = item.select_one("p.tem i").text
                temp = f"{temp_high}/{temp_low}"

                # 打印当前解析的天气数据
                print(f"{city} | {date} | {weather} | {temp}")
                # 插入数据到数据库
                db.insert_data(city, date, weather, temp)
            except Exception as e:
                print(f"解析单条天气数据出错:{e}")
    except Exception as e:
        print(f"获取 {city} 天气页面失败:{e}")
def run_forecast(self, target_cities):
    """批量处理多个城市的天气爬取:初始化数据库→爬取数据→关闭数据库"""
    # 初始化数据库连接
    weather_db = WeatherDB()
    weather_db.open_db()

    # 遍历目标城市,逐个爬取天气数据
    for city in target_cities:
        print(f"\n开始爬取 {city} 天气数据...")
        self.get_city_forecast(city, weather_db)

    # 关闭数据库连接
    weather_db.close_db()

微信图片_20251030163912

微信图片_20251030163923
2.心得:

微信图片_20251030164736
根据该网页中的html格式,来遍历每个列表项,提取日期、天气、温度,并打印出来,一共爬取了四个城市的数据,提取的数据格式都是一样的。
这个任务的主要难点就是对html格式的解析,要充分了解结构,才能成功的爬取出来。
作业二
要求:用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
1.核心代码和结果
我选择的是东方财富网这个网站

def get_stock_info():
"""获取股票数据(使用有效完整URL)"""
# 有效URL
url = "https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=1&invt=2&cb=jQuery3710510125268950266_1761719095870&fs=m%3A0%2Bt%3A6%2Bf%3A!2%2Cm%3A0%2Bt%3A80%2Bf%3A!2%2Cm%3A1%2Bt%3A2%2Bf%3A!2%2Cm%3A1%2Bt%3A23%2Bf%3A!2%2Cm%3A0%2Bt%3A81%2Bs%3A262144%2Bf%3A!2&fields=f12%2Cf13%2Cf14%2Cf1%2Cf2%2Cf4%2Cf3%2Cf152%2Cf5%2Cf6%2Cf7%2Cf15%2Cf18%2Cf16%2Cf17%2Cf10%2Cf8%2Cf9%2Cf23&fid=f3&pn=1&pz=20&po=1&dect=1&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1761719095870"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
    "Referer": "https://www.eastmoney.com/",
    "Accept": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01"
}
try:
    response = requests.get(url, headers=headers, timeout=15)
    response.raise_for_status()
    response_text = response.text
    # 用正则提取JSONP中的核心数据
    json_match = re.search(r'\((\{.*\})\)', response_text)
    if not json_match:
        print("无法解析JSONP格式数据")
        return []
    # 解析JSON数据
    data = json.loads(json_match.group(1))
    # 验证数据有效性
    if data.get("rc") == 0 and data.get("data") and data["data"].get("diff"):
        return data["data"]["diff"]
    else:
        print(f"接口返回无效数据: {data.get('rc')}")
        return []
except Exception as e:
    print(f"请求失败: {e}")
    return []
def print_stock_info(stock_list):
"""打印股票数据(优化对齐)"""
print(
    f"{'序号':<5}{'代码':<8}{'名称':<8}{'最新价':<8}{'涨跌幅':<8}{'涨跌额':<8}"
    f"{'成交量(手)':<12}{'成交额':<10}{'振幅':<8}{'最高':<8}{'最低':<8}{'今开':<8}"
    f"{'昨收':<8}{'量比':<6}{'换手率':<8}{'市盈率(动态)':<12}"
)
for i, stock in enumerate(stock_list, 1):
    code = stock.get("f12", "--")
    name = stock.get("f14", "--")
    latest_price = stock.get("f2", 0)
    change_percent = stock.get("f3", 0)
    change_amount = stock.get("f4", 0)
    volume = stock.get("f5", 0)
    turnover = stock.get("f6", 0)
    amplitude = stock.get("f7", 0)
    high = stock.get("f15", 0)
    low = stock.get("f16", 0)
    open_price = stock.get("f17", 0)
    prev_close = stock.get("f18", 0)
    volume_ratio = stock.get("f8", 0)
    turnover_rate = stock.get("f9", 0)
    pe = stock.get("f10", 0)

    # 格式化显示
    latest_price = f"{latest_price / 100:.2f}" if latest_price != 0 else "--"
    change_percent = f"{change_percent / 100:.2f}%" if change_percent != 0 else "--"
    change_amount = f"{change_amount / 100:.2f}" if change_amount != 0 else "--"
    volume = f"{volume}" if volume != 0 else "--"
    turnover = f"{turnover / 100000000:.2f}亿" if turnover != 0 else "--"
    amplitude = f"{amplitude / 100:.2f}%" if amplitude != 0 else "--"
    high = f"{high / 100:.2f}" if high != 0 else "--"
    low = f"{low / 100:.2f}" if low != 0 else "--"
    open_price = f"{open_price / 100:.2f}" if open_price != 0 else "--"
    prev_close = f"{prev_close / 100:.2f}" if prev_close != 0 else "--"
    volume_ratio = f"{volume_ratio / 100:.2f}" if volume_ratio != 0 else "--"
    turnover_rate = f"{turnover_rate / 100:.2f}%" if turnover_rate != 0 else "--"
    pe = f"{pe / 100:.2f}" if pe != 0 else "--"

    # 打印行
    print(
        f"{i:<5}{code:<8}{name:<8}{latest_price:<8}{change_percent:<8}{change_amount:<8}"
        f"{volume:<12}{turnover:<10}{amplitude:<8}{high:<8}{low:<8}{open_price:<8}"
        f"{prev_close:<8}{volume_ratio:<6}{turnover_rate:<8}{pe:<12}"
    )

微信图片_20251030170256
2.心得:
这个任务使用的是动态爬取,需要找到数据对应的url,通过url来查看json,并用正则表达式解析出json的数据,从而将数据提取出来

微信图片_20251030170306

微信图片_20251030170313

微信图片_20251030170310
这次任务老师进行了讲解也给了参考文献,但是自己实际操作的时候还是遇到了很多困难,一直爬取不出来数据,代码中运用的url放在浏览器里头就变成了空的,一直在调整正则表达式,要不就是数字的数据不正确,对数字的数据进行了限制并更改,最终才得以实现。
作业三
要求:爬取中国大学2021主榜https://www.shanghairanking.cn/rankings/bcur/2021 所有院校信息,并存储在数据库中,同时将浏览器F12调试分析的过程录制Gif加入至博客中。
技巧:分析该网站的发包情况,分析获取数据的api
1.核心代码和运行结果

def download_payload_js(url="payload.js"):
"""下载payload.js(仅首次或文件不存在时下载)"""
if os.path.exists(url):
    print(f"ℹ️ 本地已存在{url},跳过下载")
    return True
try:
    response = requests.get(url, headers=get_headers(), timeout=15)
    response.raise_for_status()
    response.encoding = response.apparent_encoding
    with open(url, "w", encoding="utf-8") as f:
        f.write(response.text)
    print(f"✅ 成功下载{url}")
    return True
except Exception as e:
    print(f"❌ 下载失败: {str(e)}")
    return False
# 2. 解析第一页数据(从本地payload.js提取)
def parse_first_page(file_path="payload.js", page_size=20):
"""解析本地文件,仅提取第一页(前20条)数据"""
if not os.path.exists(file_path):
    print(f"❌ {file_path}不存在,无法解析")
    return []
# 编码映射表(保留核心映射,去除冗余)
province_map = {
    "q": "北京", "D": "上海", "x": "浙江", "k": "江苏", "v": "湖北",
    "y": "安徽", "u": "广东", "B": "黑龙江", "C": "吉林", "n": "山东",
    "o": "河南", "p": "河北", "N": "天津", "G": "山西", "F": "福建"
}
category_map = {"f": "综合类", "e": "理工类", "h": "师范类", "m": "农业类", "S": "林业类"}
tag_map = {"i": "双一流", "l": "985", "j": "211"}
try:
    with open(file_path, "r", encoding="utf-8") as f:
        content = f.read()
    # 提取univData核心区域
    univ_data_match = re.search(r'univData:\s*\[(.*?)\],\s*indList:', content, re.S)
    if not univ_data_match:
        print("❌ 未找到核心数据区域")
        return []
    # 提取单个大学对象,取前page_size条(第一页)
    univ_items = re.findall(r'\{[^}]*univNameCn:"[^"]+"[^}]*\}', univ_data_match.group(1), re.S)[:page_size]
    first_page_data = []
    for idx, item in enumerate(univ_items, 1):
        # 基础信息提取(带默认值)
        name_cn = re.search(r'univNameCn:"(.*?)"', item).group(1) if re.search(r'univNameCn:"(.*?)"',
                                                                               item) else f"未知大学_{idx}"
        name_en = re.search(r'univNameEn:"(.*?)"', item).group(1) if re.search(r'univNameEn:"(.*?)"',
                                                                               item) else "Unknown University"
        rank = int(re.search(r'ranking:([^,]+)', item).group(1).strip('"')) if (
                    re.search(r'ranking:([^,]+)', item) and re.search(r'ranking:([^,]+)', item).group(1).strip(
                '"').isdigit()) else idx
        province = province_map.get(
            re.search(r'province:([^,]+)', item).group(1).strip('"') if re.search(r'province:([^,]+)',
                                                                                  item) else "", "未知省份")
        category = category_map.get(
            re.search(r'univCategory:([^,]+)', item).group(1) if re.search(r'univCategory:([^,]+)', item) else "",
            "未知类型")
        score = float(re.search(r'score:([^,]+)', item).group(1).strip('"')) if (
                    re.search(r'score:([^,]+)', item) and re.search(r'score:([^,]+)', item).group(1).strip(
                '"').replace('.', '').isdigit()) else None
        # 标签处理
        tags_match = re.search(r'univTags:\[([^\]]*)\]', item)
        tags = ",".join([tag_map.get(t.strip(), t.strip()) for t in tags_match.group(1).split(',') if
                         t.strip()]) if tags_match else "无"
        # 指标得分提取(保留核心指标)
        ind_data = {}
        if re.search(r'indData:(\{[^}]+\})', item):
            ind_str = re.search(r'indData:(\{[^}]+\})', item).group(1)
            ind_name_map = {"159": "办学层次", "160": "学科水平", "163": "人才培养"}
            for code, name in ind_name_map.items():
                val_match = re.search(f'"{code}":"?([^,"]+)"?', ind_str)
                if val_match:
                    val = val_match.group(1).strip()
                    ind_data[name] = float(val) if val.replace('.', '').isdigit() else val
                else:
                    ind_data[name] = None
        # 组装第一页数据
        first_page_data.append({
            "rank": rank, "name_cn": name_cn, "name_en": name_en,
            "province": province, "category": category, "score": score,
            "tags": tags, "school_level": ind_data.get("办学层次"),
            "discipline_level": ind_data.get("学科水平"), "talent_train": ind_data.get("人才培养"),
            "crawl_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })
    print(f"✅ 成功解析第一页 {len(first_page_data)} 条数据")
    return first_page_data
except Exception as e:
    print(f"❌ 解析失败: {str(e)}")
    return []
# 5. 主程序(流程简化)
if __name__ == "__main__":
# 核心参数(集中配置)
PAYLOAD_URL = "https://www.shanghairanking.cn/_nuxt/static/1761118404/rankings/bcur/2021/payload.js"
DB_FILE = "university_rank_2021_firstpage.db"
FIRST_PAGE_SIZE = 20  # 第一页固定20条数据
try:
    # 步骤1:下载payload.js(如需更新,手动删除本地文件后重新运行)
    download_payload_js()
    # 步骤2:解析第一页数据
    first_page_data = parse_first_page(page_size=FIRST_PAGE_SIZE)
    # 步骤3:数据处理(打印+保存)
    if first_page_data:
        print_first_page_summary(first_page_data)
        # 初始化数据库并保存
        db_conn = create_db(DB_FILE)
        if db_conn:
            save_to_db(db_conn, first_page_data)
            db_conn.close()
            print(f"\n📌 操作完成!数据库文件路径:{os.path.abspath(DB_FILE)}")
    else:
        print("❌ 未获取到第一页数据")
except Exception as e:
    print(f"❌ 程序异常: {str(e)}")

微信图片_20251030173901

QQ20251030-18167
2.心得
同样也是动态爬取,找到数据对应的url,通过url来查看json,并用正则表达式解析出json的数据,从而将数据提取出来

微信图片_20251030182903

微信图片_20251030182907

微信图片_20251030182911
这个任务有很大的难度,用了跟之前一样的方法但是一直爬取不出来任何数据,然后问了班里的同学,说json有问题,通过同学的指点说是要把json下载下来,然后解析这个json得出正确的正则表达式,但是我还是不太理解,又问了问老师,老师说解析这个json有一定的难度,就跟一个密码本一样需要找到配对的数据,然后我就知道如何解决了,就借助大模型,让大模型根据这个json生成正确的正则表达式,然后修改了代码,最后终于爬取出来。这个任务让我知道了处理问题需要找到问题所在,对症下药,才能真正的解决问题。

代码地址: https://gitee.com/njs5/bgyuhnji/tree/homework1/

posted @ 2025-10-30 19:32  bd2  阅读(3)  评论(0)    收藏  举报