数据采集第四次作业

作业1

股票数据爬取实验

(1)要求:使用 Selenium 框架+ MySQL 数据库存储技术路线爬取“沪深 A 股”、“上证 A 股”、“深证 A 股”3 个板块的股票数据信息。

HTML信息:

屏幕截图 2025-12-06 205050

核心代码:

点击查看代码
def crawl_board(self, hash_code, board_name, max_pages):
        """
        爬取指定板块的股票数据
        :param hash_code: 板块的hash编码
        :param board_name: 板块名称
        :param max_pages: 最大爬取页数
        :return: 爬取的所有数据行
        """
        all_data = []
        # 构造完整的URL
        url = self.base_url + hash_code
        print(f"正在访问: {url}")
        # 访问目标网页
        self.driver.get(url)
        # 等待页面加载
        time.sleep(5)
        # 循环爬取多页数据
        for page in range(1, max_pages + 1):
            # 如果不是第一页,则需要点击下一页按钮
            if page > 1:
                # 查找下一页按钮
                next_btn = self.driver.find_element(By.XPATH, '//a[@title="下一页"]')
                # 检查按钮是否可用
                if next_btn.is_enabled():
                    # 滚动到按钮位置
                    self.driver.execute_script("arguments[0].scrollIntoView(true);", next_btn)
                    time.sleep(1)
                    # 点击下一页按钮
                    self.driver.execute_script("arguments[0].click();", next_btn)
                    # 等待新页面加载
                    time.sleep(5)
                else:
                    print(" 无更多页面,提前结束。")
                    break

            # 等待表格元素加载完成
            self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "table tbody tr")))
            time.sleep(2)

            # 获取当前页面所有股票行数据
            rows = self.driver.find_elements(By.CSS_SELECTOR, "table tbody tr")

            # 遍历每一行数据
            for row in rows:
                # 获取行内所有单元格
                tds = row.find_elements(By.TAG_NAME, "td")
                # 如果单元格数量不足14个,则跳过该行
                if len(tds) < 14:
                    continue

                # 提取各个字段的数据
                data_row = [
                    tds[0].text.strip(),     # 序号
                    tds[1].text.strip(),     # 代码
                    tds[2].text.strip(),     # 名称
                    tds[4].text.strip(),     # 最新价
                    tds[5].text.strip(),     # 涨跌幅
                    tds[6].text.strip(),     # 涨跌额
                    tds[7].text.strip(),     # 成交量(手)
                    tds[8].text.strip(),     # 成交额
                    tds[9].text.strip(),     # 振幅
                    tds[10].text.strip(),    # 最高
                    tds[11].text.strip(),    # 最低
                    tds[12].text.strip(),    # 今开
                    tds[13].text.strip()     # 昨收
                ]
                # 将数据行添加到总数据列表中
                all_data.append(data_row)

            print(f" 第 {page} 页数据已采集")

        return all_data

结果:

屏幕截图 2025-11-26 184040
屏幕截图 2025-11-26 184028

完整代码:

https://gitee.com/kjmfzu/fzu_-kjm/blob/master/实验4/practice-4-2.py

(2)心得体会

新的股票信息新增“相关链接”一列,爬取数据时要略过这一列,并且通过这个实验,我掌握了使用Selenium爬取动态网页数据的技巧,学会了处理金融数据的特殊格式,并实现了数据的自动化采集和存储。这让我对网络爬虫在金融领域的应用有了更深理解。

作业2

慕课课程数据爬取实验

(1)要求:使用 Selenium 框架+MySQL 爬取中国 mooc 网课程资源信息(课程号、课程名称、学校名称、主讲教师、团队成员、参加人数、课程进度、课程简介)

核心代码:

点击查看代码
def get_text(driver, selectors, attr=None, use_innerText=False):
    """
    从页面元素中获取文本内容的通用函数
    :param driver: WebDriver实例
    :param selectors: 选择器元组列表,格式为[(By.XXX, 'selector'), ...]
    :param attr: 要获取的属性名(可选)
    :param use_innerText: 是否使用innerText获取文本(可选)
    :return: 获取到的文本内容或"未知"
    """
    # 遍历所有选择器尝试获取元素
    for by, val in selectors:
        try:
            # 根据选择器查找元素
            el = driver.find_element(by, val)
            # 如果指定了属性名,则返回属性值
            if attr:
                return el.get_attribute(attr).strip()
            # 使用 innerText 获取包含格式的完整文本,解决 .text 获取不全的问题
            if use_innerText:
                return el.get_attribute("innerText").strip()
            # 默认返回元素的text属性
            return el.text.strip()
        except: continue
    # 所有选择器都找不到元素时返回默认值
    return "未知"

def crawl_details(driver):
    """提取详情页数据"""
    # 创建存储课程信息的字典
    d = {}
    
    # 1. 课程名 & 学校
    # 获取课程标题
    d['cCourse'] = get_text(driver, [(By.CSS_SELECTOR, ".course-title"), (By.CSS_SELECTOR, "h1")])
    # 获取学校名称(通过图片的alt属性)
    d['cCollege'] = get_text(driver, [(By.CSS_SELECTOR, ".m-teachers img.u-img")], "alt")
    # 如果通过图片无法获取学校名称,则尝试其他方式
    if d['cCollege'] == "未知":
        d['cCollege'] = get_text(driver, [(By.CSS_SELECTOR, ".school-name")])

    # 2. 人数 & 进度
    # 获取选课人数
    raw_cnt = get_text(driver, [(By.CSS_SELECTOR, "span.count"), (By.CLASS_NAME, "course-enroll-count")])
    # 使用正则表达式提取数字部分
    d['cCount'] = (re.findall(r'\d+', raw_cnt) or ["0"])[0]
    
    try: # SPOC版时间
        # 尝试获取SPOC版本的开课时间
        d['cProcess'] = driver.find_element(By.XPATH, "//span[contains(text(),'开课时间')]/following-sibling::span").text
    except: # 普通版时间
        # 获取普通版本的开课时间
        d['cProcess'] = get_text(driver, [(By.CLASS_NAME, "course-enroll-time")])
    # 替换时间范围符号
    d['cProcess'] = d['cProcess'].replace("~", "至")
    
    # 3. 课程简介 (关键修改:放宽限制,使用 innerText)
    # 获取课程简介内容
    d['cBrief'] = get_text(driver, [
        (By.CSS_SELECTOR, ".f-richEditorText"),         # SPOC版
        (By.CSS_SELECTOR, ".m-course-intro .content"),  # 普通版
        (By.CSS_SELECTOR, ".u-content")
    ], use_innerText=True)
    # 限制 50 字,便于查看
    d['cBrief'] = d['cBrief'][:50]

    # 4. 老师 (自动翻页)
    # 使用集合存储教师姓名,避免重复
    teachers = set()
    # (A) 抓普通版静态老师
    # 获取普通版本的教师姓名
    for t in driver.find_elements(By.CSS_SELECTOR, ".u-teacher-name"): teachers.add(t.text.strip())
    # (B) 抓 SPOC 版轮播图老师 (循环翻页)
    # 获取SPOC版本的教师信息(可能需要翻页)
    for _ in range(5):
        try:
            # 获取轮播图中的教师姓名
            for t in driver.find_elements(By.CSS_SELECTOR, ".m-teachers h3.f-fc3"):
                # 强制获取隐藏文本
                name = t.get_attribute("textContent").strip() # 强制获取隐藏文本
                if name: teachers.add(name)
            
            # 查找并点击下一页按钮
            btn = driver.find_element(By.CSS_SELECTOR, ".um-list-slider_next")
            if btn.is_displayed(): 
                btn.click()
                time.sleep(0.5)
            else: break
        except: break
    
    # 5. 整理团队数据 
    # 将教师集合转换为列表
    t_list = list(teachers)
    if t_list:
        # 主讲人 (取第一个)
        d['cTeacher'] = t_list[0]         
        # 团队成员 (包含主讲人 + 其他人)
        d['cTeam'] = ",".join(t_list)     
    else:
        # 如果没有找到教师信息,则设置默认值
        d['cTeacher'] = "未知"; d['cTeam'] = "无"

    return d
点击查看代码
try:
        # 访问中国大学MOOC首页
        driver.get("https://www.icourse163.org/")
        
        # 尝试点首页登录按钮
        try: driver.find_element(By.XPATH, "//div[contains(text(),'登录/注册')]").click()
        except: pass
        
        # 唯一的人工操作环节 
        print("\n" + "="*50)
        print(">>> 1. 请在浏览器登录")
        print(">>> 2. 请点击同意【隐私政策】")
        print(">>> 3. 完成后,请在这里按【回车键】启动全自动爬取...")
        print("="*50 + "\n")
        input(">>> 等待按回车: ")

        # 2. 自动跳转课程列表
        print(">>> 正在跳转课程列表...")
        try: 
            # 尝试点击"我的课程"链接
            driver.find_element(By.XPATH, "//a[contains(text(),'我的课程')]").click()
        except: 
            # 如果找不到"我的课程"链接,则直接访问课程列表页面
            driver.get("https://www.icourse163.org/home.htm#/home/course")
        
        # 等待列表加载完成
        time.sleep(5) 

点击查看代码
# 3. 循环爬取
        # 获取所有课程卡片元素
        cards = driver.find_elements(By.CSS_SELECTOR, ".course-card-wrapper, .u-course-card, .u-clist .u-course-card")
        print(f">>> 发现 {len(cards)} 门课程,开始处理...")
        
        # 遍历所有课程卡片
        for i in range(len(cards)):
            try:
                # 重新定位防失效(防止页面元素变化导致的错误)
                current_cards = driver.find_elements(By.CSS_SELECTOR, ".course-card-wrapper, .u-course-card, .u-clist .u-course-card")
                if i >= len(current_cards): break
                card = current_cards[i]
                
                # 进入详情页 
                try:
                    # 尝试通过更多按钮进入课程详情
                    ActionChains(driver).move_to_element(card.find_element(By.CSS_SELECTOR, ".u-icon-dots-more")).perform()
                    time.sleep(0.5)
                    driver.find_element(By.LINK_TEXT, "查看课程介绍").click()
                except:
                    # 备选方案:直接点击卡片
                    card.click() 
                
                # 等待页面加载
                time.sleep(2)
                # 切换到新打开的标签页
                driver.switch_to.window(driver.window_handles[-1])
                
                # 抓取并存库
                # 爬取课程详情信息
                data = crawl_details(driver)
                # 将数据保存到数据库
                save_to_mysql(data)
                
                # 关闭当前标签页并切换回主页面
                driver.close()
                driver.switch_to.window(driver.window_handles[0])
                
            except Exception as e:
                # 处理异常情况
                print(f"   [跳过] 课程 {i+1} 异常: {e}")
                # 如果打开了多个标签页但出现异常,则关闭当前标签页并切换回主页面
                if len(driver.window_handles) > 1:
                    driver.close(); driver.switch_to.window(driver.window_handles[0])
完整代码:

https://gitee.com/kjmfzu/fzu_-kjm/blob/master/实验4/practice-4-2.py

结果:

image
屏幕截图 2025-12-06 203235

我的思路是选择手动登录+点掉隐私政策来方便获取课程详细HTML信息,其他操作让程序全自动进行。并且我还设置了课程简绍限制字数为50来方便MYSQL界面查看,以下是我个人账号的课程部分HTML信息
其中值得注意是个人课程的查看课程介绍和老师界面的翻页按钮

查看课程简绍

屏幕截图 2025-12-06 183502

老师界面翻页按钮

屏幕截图 2025-12-06 185358

登录键

屏幕截图 2025-12-06 183154

我的课程

屏幕截图 2025-12-06 183354

课程名

屏幕截图 2025-12-06 183617

开课时间

屏幕截图 2025-12-06 184037

进度,人数

屏幕截图 2025-12-06 184144

学校名

屏幕截图 2025-12-06 184255

课程概述

屏幕截图 2025-12-06 184205

(2)心得体会:

这个项目让我学会了如何爬取教育平台数据,处理复杂的页面交互和教师信息展示。特别是解决了iframe和动态加载内容的抓取难题,提升了我对复杂网页结构的分析能力。

作业3

华为云平台实验

(1)要求:掌握大数据相关服务,熟悉 Xshell 的使用

环境搭建:

任务一:开通 MapReduce 服务

1.申请弹性公网IP
屏幕截图 2025-11-04 221904
2.开通MapReduce服务
屏幕截图 2025-11-04 221936
安全组
屏幕截图 2025-11-04 175257
3.开通云数据库服务RDS
屏幕截图 2025-11-04 222004
屏幕截图 2025-11-04 222011
4.开通数据湖探索服务(DLI)
屏幕截图 2025-11-04 222053
屏幕截图 2025-11-04 175956
屏幕截图 2025-11-04 222147(1)

实时分析开发实战:

任务一:Python脚本生成测试数据

屏幕截图 2025-11-04 180942(1)
屏幕截图 2025-11-04 180919
屏幕截图 2025-11-04 180933

任务二:配置Kafka

kafka-topics.sh --create --topic fludesc --partitions 1 --replication-factor 1 --bootstrap-server 192.168.0.135 9092
屏幕截图 2025-11-04 181524

任务三:安装Flume客户端

屏幕截图 2025-11-04 182127
d2eabdac1c3badd724c55c57ddcacb75

任务四:配置Flume采集数据

屏幕截图 2025-11-04 192213
屏幕截图 2025-11-04 192230

华为云平台相关结果:

使用DLI中的Flink作业进行数据分析:

屏幕截图 2025-11-04 205457
屏幕截图 2025-11-04 215241

数据库结果:

屏幕截图 2025-11-04 215045

(2)心得体会:

本实验让我首次完整体验了“数据生成 → 实时采集 → 流式处理”的实时数仓链路。通过编写Python脚本模拟日志数据,利用Kafka作为消息队列缓冲,再通过Flume将数据采集至MRS集群,最后借助DLI中的Flink作业进行实时计算,整个流程环环相扣。我在配置Kafka时因路径问题和Flume版本不匹配遇到了小挫折,但通过查阅文档及时修正(如使用1.9.0版本Flume、指定绝对路径),提升了排错能力。

posted @ 2025-12-07 19:57  KKLMMML  阅读(4)  评论(0)    收藏  举报