数据采集第一次作业

第一次作业


作业①:大学排名数据爬取

1)实验代码与结果

实验要求: 用 requests 和 BeautifulSoup 库方法定向爬取给定网址( http://www.shanghairanking.cn/rankings/bcur/2020 )的数据,屏幕打印爬取的大学排名信息。

实验代码:

# -*- coding: utf-8 -*-
"""
作业①:大学排名数据爬取
使用 requests + BeautifulSoup 库定向爬取上海排名网2020中国大学排名数据
"""

import requests
from bs4 import BeautifulSoup


def get_html_text(url):
    """
    向给定网址发送请求,返回网页HTML文本内容
    :param url: 目标网址字符串
    :return: 网页HTML文本;请求失败时返回空字符串
    """
    # 设置请求头,模拟浏览器访问,避免被网站拒绝
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                       'AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/120.0.0.0 Safari/537.36'
    }
    try:
        # 发送GET请求,设置超时时间为20秒
        resp = requests.get(url, headers=headers, timeout=20)
        # 根据网页内容自动推断编码方式,防止中文乱码
        resp.encoding = resp.apparent_encoding
        # 判断响应状态码,200表示请求成功
        if resp.status_code == 200:
            return resp.text
        else:
            print(f"请求失败,状态码:{resp.status_code}")
            return ""
    except Exception as e:
        print(f"请求发生异常:{e}")
        return ""


def parse_university_ranking(html):
    """
    使用BeautifulSoup解析HTML,提取大学排名信息
    :param html: 网页HTML文本
    :return: 包含各大学排名信息的列表,每个元素为字典
    """
    # 创建BeautifulSoup对象,使用html.parser解析器
    soup = BeautifulSoup(html, "html.parser")
    # 查找表格中所有行标签tr
    rows = soup.find_all("tr")
    # 用于存储所有大学的排名信息
    university_list = []

    # 遍历每一行,跳过表头行(第一行)
    for row in rows[1:]:
        # 获取当前行中所有单元格td
        cols = row.find_all("td")
        # 确保该行有足够的数据列(至少5列)
        if len(cols) >= 5:
            # 排名:第1列
            rank = cols[0].get_text(strip=True)
            # 学校名称:第2列,优先查找class为name-cn的span标签(中文校名)
            name_tag = cols[1].find("span", class_="name-cn")
            if name_tag:
                name = name_tag.get_text(strip=True)
            else:
                # 备选:取img标签的alt属性(通常为中文校名)
                img_tag = cols[1].find("img")
                if img_tag and img_tag.get("alt"):
                    name = img_tag.get("alt")
                else:
                    name = cols[1].get_text(strip=True).split("\n")[0].strip()
            # 省市:第3列
            province = cols[2].get_text(strip=True)
            # 学校类型:第4列
            school_type = cols[3].get_text(strip=True)
            # 总分:第5列
            score = cols[4].get_text(strip=True)

            # 将提取的信息存入字典
            university_list.append({
                "rank": rank,
                "name": name,
                "province": province,
                "type": school_type,
                "score": score
            })
    return university_list


def print_university_list(university_list):
    """
    格式化打印大学排名信息表
    :param university_list: 大学排名信息列表
    """
    # 打印表头
    print(f"{'排名':<6}{'学校名称':<12}{'省市':<8}{'学校类型':<8}{'总分':<8}")
    print("-" * 50)
    # 遍历列表,逐行打印每所大学的信息
    for uni in university_list:
        print(f"{uni['rank']:<6}{uni['name']:<12}{uni['province']:<8}"
              f"{uni['type']:<8}{uni['score']:<8}")


def main():
    """
    主函数:协调调用各功能函数,完成爬取和输出
    """
    # 目标网址:上海排名网2020中国大学排名
    url = "https://www.shanghairanking.cn/rankings/bcur/2020"
    print("正在爬取大学排名数据,请稍候...\n")
    # 获取网页HTML
    html = get_html_text(url)
    if html:
        # 解析HTML,提取排名信息
        university_list = parse_university_ranking(html)
        # 打印结果
        print_university_list(university_list)
        print(f"\n共爬取到 {len(university_list)} 所大学排名信息")
    else:
        print("未获取到网页数据")


if __name__ == "__main__":
    main()

运行结果:

image

运行程序后,终端输出如下排名信息(部分展示):

排名 学校名称 省市 学校类型 总分
1 清华大学 北京 综合 852.5
2 北京大学 北京 综合 746.7
3 浙江大学 浙江 综合 649.2
4 上海交通大学 上海 综合 625.9
5 南京大学 江苏 综合 566.1
... ... ... ... ...
30 中国农业大学 北京 农业 351.5

共爬取到 30 所大学排名信息。

2)心得体会

本次实验使用 requests 库发送 HTTP 请求获取网页源代码,再用 BeautifulSoup 库解析 HTML 表格结构,提取大学排名数据。实验过程中遇到了以下关键点:

  1. 编码问题处理: 网页内容可能存在非 UTF-8 编码,通过 resp.apparent_encoding 自动推断编码方式,有效避免了中文乱码问题。

  2. 请求头设置: 部分网站会拒绝没有 User-Agent 的请求,通过模拟浏览器请求头成功获取了数据。

  3. 精确提取校名: 学校名称列中同时包含中文名、英文名和"双一流/985/211"标签,直接使用 get_text() 会混入多余文本。通过分析 HTML 结构,发现中文校名位于 <span class="name-cn"> 标签中,因此使用 find("span", class_="name-cn") 精确提取,保证了数据的整洁性。

  4. 表格解析思路: 使用 find_all("tr") 获取所有行,跳过表头行后逐行解析 td 单元格,将每所大学的信息存入字典列表,结构清晰、易于扩展。


作业②:商城商品比价定向爬虫

1)实验代码与结果

实验要求: 用 requests 和 re 库方法设计某个商城商品比价定向爬虫,爬取该商城以关键词"书包"搜索页面的数据,爬取商品名称和价格。(本作业选择当当网作为目标商城)

实验代码:

# -*- coding: utf-8 -*-
"""
作业②:商城商品比价定向爬虫
使用 requests + re 库方法爬取当当网以关键词"书包"搜索的商品名称和价格
"""

import requests
import re


def get_html_text(url):
    """
    向给定网址发送请求,返回网页HTML文本内容
    :param url: 目标网址字符串
    :return: 网页HTML文本;请求失败时返回空字符串
    """
    # 设置请求头,模拟浏览器访问
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                       'AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/120.0.0.0 Safari/537.36'
    }
    try:
        # 发送GET请求,设置超时时间为20秒
        resp = requests.get(url, headers=headers, timeout=20)
        # 自动推断编码,防止中文乱码
        resp.encoding = resp.apparent_encoding
        if resp.status_code == 200:
            return resp.text
        else:
            print(f"请求失败,状态码:{resp.status_code}")
            return ""
    except Exception as e:
        print(f"请求发生异常:{e}")
        return ""


def parse_products(html):
    """
    使用正则表达式解析HTML,提取商品名称和价格
    :param html: 网页HTML文本
    :return: 商品信息列表,每个元素为(价格, 商品名)元组
    """
    # 定义商品信息列表
    products = []

    # 正则表达式匹配每个商品列表项li(class以line开头的li标签)
    # re.S 使 . 能匹配换行符
    li_pattern = re.compile(r'<li[^>]*class="line[^"]*"[^>]*>(.*?)</li>', re.S)
    li_blocks = li_pattern.findall(html)

    # 正则表达式匹配价格:class="price_n"标签中的 &yen; 后的数字
    price_pattern = re.compile(r'class="price_n"[^>]*>&yen;([\d.]+)')
    # 正则表达式匹配商品名称:title属性值
    name_pattern = re.compile(r'title="([^"]+)"')

    # 遍历每个商品块,提取名称和价格
    for block in li_blocks:
        # 提取价格
        price_match = price_pattern.search(block)
        # 提取商品名(取第一个title属性)
        name_match = name_pattern.search(block)
        # 确保价格和名称都存在
        if price_match and name_match:
            price = price_match.group(1)
            # 去除商品名首尾空白字符
            name = name_match.group(1).strip()
            products.append((price, name))

    return products


def print_products(products):
    """
    格式化打印商品比价信息表
    :param products: 商品信息列表,元素为(价格, 商品名)元组
    """
    # 打印表头
    print(f"{'序号':<6}{'价格':<12}{'商品名'}")
    print("-" * 70)
    # 遍历商品列表,逐行打印
    for index, (price, name) in enumerate(products, 1):
        print(f"{index:<6}{price:<12}{name}")


def main():
    """
    主函数:协调调用各功能函数,完成爬取和输出
    """
    # 当当网搜索URL,关键词为"书包"(URL编码为%E4%B9%A6%E5%8C%85)
    url = "http://search.dangdang.com/?key=%E4%B9%A6%E5%8C%85"
    print('正在爬取当当网"书包"商品搜索数据,请稍候...\n')
    # 获取网页HTML
    html = get_html_text(url)
    if html:
        # 解析HTML,提取商品信息
        products = parse_products(html)
        # 打印结果
        print_products(products)
        print(f"\n共爬取到 {len(products)} 个商品信息")
    else:
        print("未获取到网页数据")


if __name__ == "__main__":
    main()

运行结果:

image

运行程序后,终端输出如下商品比价信息(部分展示):

序号 价格 商品名
1 89.99 书包女新款初中生高中学生大容量轻便减负护脊简约学院风双肩包
2 224.00 儿童书包护脊减负大容量2026新款男生一二三到六年级男孩超轻书包
3 39.99 书包幼儿园女孩 潮小兔子书包3-6岁大中小班男童儿童卡通可爱韩版
4 99.80 书包男女生 1-6年级大容量新款小学生耐磨透气书包儿童双肩包
5 75.80 软书包女孩小学生一二三到六年级可爱卡通超轻便减负儿童双肩背包
... ... ...
60 54.80 儿童双肩包女孩三到六年级上课书包小学生超轻防水补课旅游背包女

共爬取到 60 个商品信息。

2)心得体会

本次实验使用 requests 获取当当网搜索页面 HTML,再用 re 正则表达式库提取商品名称和价格。实验过程中的关键体会如下:

  1. 正则表达式设计: 当当网每个商品信息包裹在 <li class="line..."> 标签中,使用 r'<li[^>]*class="line[^"]*"[^>]*>(.*?)</li>' 配合 re.S 标志匹配跨行内容,成功提取出每个商品块的 HTML 片段。

  2. 价格提取技巧: 当当网价格使用 HTML 实体 &yen; 表示人民币符号 ¥,通过正则 r'class="price_n"[^>]*>&yen;([\d.]+)' 精确匹配价格数字,避免了实体编码的干扰。

  3. re 与 BeautifulSoup 的对比: 相比 BeautifulSoup 的结构化解析,正则表达式更适合处理非结构化或半结构化的数据提取,灵活性更高但可读性稍低。对于商品列表这种重复结构,正则表达式的匹配效率很高。

  4. URL 编码处理: 中文关键词"书包"在 URL 中需要编码为 %E4%B9%A6%E5%8C%85,这是 UTF-8 编码的百分号编码形式,直接拼接到搜索 URL 中即可正确访问搜索结果页面。


作业③:网页图片爬取

1)实验代码与结果

实验要求: 爬取给定网页( https://news.fzu.edu.cn/yxfd.htm )的所有 JPEG、JPG 或 PNG 格式图片文件,将图片保存在一个文件夹中。

实验代码:

# -*- coding: utf-8 -*-
"""
作业③:网页图片爬取
爬取给定网页( https://news.fzu.edu.cn/yxfd.htm )中的所有JPEG、JPG或PNG格式图片
并将图片文件保存到本地文件夹中
"""

import requests
from bs4 import BeautifulSoup
import os
import re
from urllib.parse import urljoin, urlparse


def get_html_text(url):
    """
    向给定网址发送请求,返回网页HTML文本内容
    :param url: 目标网址字符串
    :return: 网页HTML文本;请求失败时返回空字符串
    """
    # 设置请求头,模拟浏览器访问
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                       'AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/120.0.0.0 Safari/537.36'
    }
    try:
        # 发送GET请求,设置超时时间为20秒
        resp = requests.get(url, headers=headers, timeout=20)
        # 自动推断编码,防止中文乱码
        resp.encoding = resp.apparent_encoding
        if resp.status_code == 200:
            return resp.text
        else:
            print(f"请求失败,状态码:{resp.status_code}")
            return ""
    except Exception as e:
        print(f"请求发生异常:{e}")
        return ""


def parse_image_urls(html, base_url):
    """
    使用BeautifulSoup解析HTML,提取所有JPEG、JPG、PNG格式图片的URL
    :param html: 网页HTML文本
    :param base_url: 基础网址,用于将相对路径转为绝对路径
    :return: 图片URL列表
    """
    # 创建BeautifulSoup对象
    soup = BeautifulSoup(html, "html.parser")
    # 查找所有img标签
    img_tags = soup.find_all("img")
    # 用于存储符合条件的图片URL
    image_urls = []
    # 定义允许的图片扩展名(JPEG、JPG、PNG)
    valid_extensions = ('.jpeg', '.jpg', '.png')

    # 遍历所有img标签
    for img in img_tags:
        # 获取src属性
        src = img.get("src")
        if src:
            # 将相对路径转为绝对路径
            img_url = urljoin(base_url, src)
            # 获取URL路径部分,转为小写判断扩展名
            path_lower = urlparse(img_url).path.lower()
            # 判断是否为指定格式的图片
            if path_lower.endswith(valid_extensions):
                image_urls.append(img_url)

    return image_urls


def download_images(image_urls, save_dir):
    """
    下载图片并保存到指定文件夹
    :param image_urls: 图片URL列表
    :param save_dir: 保存图片的文件夹路径
    :return: 成功下载的图片数量
    """
    # 如果文件夹不存在,则创建
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)

    # 设置请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                       'AppleWebKit/537.36 (KHTML, like Gecko) '
                       'Chrome/120.0.0.0 Safari/537.36'
    }
    # 成功下载计数
    success_count = 0

    # 遍历图片URL列表,逐个下载
    for index, img_url in enumerate(image_urls, 1):
        try:
            # 发送GET请求获取图片二进制数据
            resp = requests.get(img_url, headers=headers, timeout=15)
            if resp.status_code == 200:
                # 从URL中提取文件名
                # 取URL路径的最后一部分作为文件名
                file_name = os.path.basename(urlparse(img_url).path)
                # 若文件名为空,则自动生成文件名
                if not file_name:
                    file_name = f"image_{index}.png"
                # 构建完整的保存路径
                file_path = os.path.join(save_dir, file_name)
                # 以二进制写入方式保存图片
                with open(file_path, "wb") as f:
                    f.write(resp.content)
                print(f"  [{index}] 下载成功: {file_name} "
                      f"({len(resp.content)} 字节)")
                success_count += 1
            else:
                print(f"  [{index}] 下载失败: {img_url} "
                      f"(状态码: {resp.status_code})")
        except Exception as e:
            print(f"  [{index}] 下载异常: {img_url} ({e})")

    return success_count


def main():
    """
    主函数:协调调用各功能函数,完成图片爬取和保存
    """
    # 目标网址:福州大学新闻网院系风采页面
    url = "https://news.fzu.edu.cn/yxfd.htm"
    # 图片保存文件夹
    save_dir = "images"
    print("正在爬取网页图片,请稍候...\n")
    # 获取网页HTML
    html = get_html_text(url)
    if html:
        # 解析HTML,提取图片URL
        image_urls = parse_image_urls(html, url)
        print(f"共发现 {len(image_urls)} 张JPEG/JPG/PNG格式图片\n")
        # 下载并保存图片
        print(f"开始下载图片到 '{save_dir}' 文件夹...")
        success_count = download_images(image_urls, save_dir)
        print(f"\n下载完成!成功保存 {success_count}/{len(image_urls)} 张图片"
              f"到 '{save_dir}' 文件夹")
    else:
        print("未获取到网页数据")


if __name__ == "__main__":
    main()

运行结果:

image

运行程序后,终端输出如下下载日志:

正在爬取网页图片,请稍候...

共发现 20 张JPEG/JPG/PNG格式图片

开始下载图片到 'images' 文件夹...
  [1] 下载成功: top_search.png (473 字节)
  [2] 下载成功: logo.png (27193 字节)
  [3] 下载成功: footer_logo.png (25402 字节)
  ...
  [20] 下载成功: 9D5EF45533E69056CF06F1A0115_9201CE3A_198.png (408 字节)

下载完成!成功保存 20/20 张图片到 'images' 文件夹

0B2EC59649CA13BAC85941A8B12_D497950A_19B
1F3D77933484E42FBCAF0681489_B9E72BFA_288DAE
4EF56A35EF9583D950FE3F8A85C_8FCE4F24_2C08E
5B42E35E337E9167CA9E61E1DC4_A87D02A0_D13E9
9D5EF45533E69056CF06F1A0115_9201CE3A_198
10F0677E72A0B0DCCF5F798C6DB_865A6721_1F7
72D850F90F27AD5F420C5659716_ED088B7C_141463
38496C338E7D3F26F02248A99ED_6621F65A_8B82F1
BD5C51D961F315A61C47FD0AEC9_70BA2916_3A880
bnt_sub_nav2
EE9C07C6486DEFAA9EAF683C37C_48D55A5D_F84E4
FD18078151A13ACF11663A5200C_DEA986BA_635B33
footer_logo
icon_bnt_back2
logo
n_left_ico1
n_left_ico2
top_search
A81DCC70A190C301F32A2CD9710_57503CF4_12A9BA
A81DCC70A190C301F32A2CD9710_57503CF4_12A9BA

成功爬取并保存了 20 张图片,包括网页 Logo、导航图标以及新闻配图等,部分图片展示如下:

1F3D77933484E42FBCAF0681489_B9E72BFA_288DAE
logo
BD5C51D961F315A61C47FD0AEC9_70BA2916_3A880
EE9C07C6486DEFAA9EAF683C37C_48D55A5D_F84E4

2)心得体会

本次实验爬取福州大学新闻网页面的所有 JPEG、JPG、PNG 格式图片并保存到本地文件夹。实验过程中的关键体会如下:

  1. 相对路径处理: 网页中的图片 src 属性使用的是相对路径(如 images/logo.png),直接请求会失败。通过 urllib.parse.urljoin() 将相对路径与基础 URL 拼接为完整绝对路径,成功解决了路径问题。

  2. 图片格式过滤: 网页中可能包含 SVG 等非目标格式的图片,通过 urlparse 解析 URL 路径并检查扩展名是否为 .jpeg、.jpg、.png,精确过滤出目标格式图片。

  3. 二进制文件保存: 图片属于二进制文件,不能使用文本模式保存。通过 resp.content 获取二进制数据,以 "wb" 模式写入文件,确保图片数据完整无误。

  4. 文件名提取: 使用 os.path.basename(urlparse(img_url).path) 从 URL 中提取文件名,并处理了文件名为空的边界情况,保证了程序的健壮性。

  5. 文件夹自动创建: 使用 os.makedirs() 在文件夹不存在时自动创建,避免了手动创建文件夹的麻烦,使程序更加自动化。


总结

本次作业完成了三个爬虫实验:

  • 作业① 使用 requests + BeautifulSoup 爬取大学排名表格数据,掌握了 HTML 表格解析和精确元素定位的方法;
  • 作业② 使用 requests + re 爬取当当网商品比价数据,掌握了正则表达式在非结构化数据提取中的应用;
  • 作业③ 爬取网页图片并保存到本地,掌握了相对路径处理、格式过滤和二进制文件下载的方法。

三个实验循序渐进,覆盖了爬虫开发中常用的技术栈,加深了对 Web 数据采集流程的理解。

posted @ 2026-10-18 15:27  很具想  阅读(2)  评论(0)    收藏  举报