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

一,作业一
要求:熟练掌握 Selenium 查找HTML元素、爬取Ajax网页数据、等待HTML元素等内容。使用Selenium框架+ MySQL数据库存储技术路线爬取“沪深A股”、“上证A股”、“深证A股”3个板块的股票数据信息。
候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board
输出信息:MYSQL数据库存储
1.核心代码和运行结果
创建数据库

点击查看代码
    def _init_table(self):
        """初始化数据表"""
        self.cursor.execute("DROP TABLE IF EXISTS a_stock_data;")
        self.cursor.execute("""
        CREATE TABLE a_stock_data (
            id INT AUTO_INCREMENT PRIMARY KEY,
            bStockNo VARCHAR(20) NOT NULL,
            bStockName VARCHAR(50) NOT NULL,
            fLatestPrice DECIMAL(10,2) NOT NULL,
            fChangePercent VARCHAR(10),
            fChangePrice DECIMAL(10,2),
            fVolume VARCHAR(20),
            fTurnover VARCHAR(20),
            fAmplitude VARCHAR(10),
            fHighestPrice DECIMAL(10,2),
            fLowestPrice DECIMAL(10,2),
            fOpenPrice DECIMAL(10,2),
            fPreClosePrice DECIMAL(10,2),
            bMarketType VARCHAR(20) NOT NULL,
            UNIQUE KEY uk_stock_market (bStockNo, bMarketType)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
        """)
        self.db.commit()
        print("数据表创建完成")
爬取数据和插入数据到数据库
点击查看代码
    def crawl(self, url, market, limit=20):
        """爬取指定板块数据"""
        print(f"\n===== 开始爬取{market}({limit}条数据)=====")
        self.driver.get(url)
        # 等待表格加载
        table = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".quotetable tbody")))
        time.sleep(3)
        rows = table.find_elements(By.TAG_NAME, "tr")
        print(f"页面找到{len(rows)}条原始数据行")
        data = []
        count = 0
        for row in rows:
            if count >= limit: break
            text = row.text.strip()
            if not text or any(k in text for k in ['加自选', '相关链接', '资讯']) or not re.search(r'\b\d{6}\b',text):
                continue
            cols = row.find_elements(By.TAG_NAME, "td")
            if len(cols) < 15: continue
            # 提取股票代码和名称
            code, name = cols[1].text.strip(), cols[2].text.strip()
            # 获取价格
            price = None
            for i in [3, 4, 5]:
                if i < len(cols):
                    price = self._parse_num(cols[i].text.strip())
                    if price: break
            if not price: continue
            # 确定价格列索引
            idx = next(i for i in [3, 4, 5] if i < len(cols) and self._parse_num(cols[i].text.strip()) == price)
            # 提取其他数据
            cp = re.sub(r'[^\d\.\-\+\%]', '', cols[idx + 1].text.strip()) if (idx + 1) < len(cols) else None
            data.append((
                code, name, price, cp,
                self._parse_num(cols[idx + 2].text.strip()) if (idx + 2) < len(cols) else None,
                cols[idx + 3].text.strip() if (idx + 3) < len(cols) else None,
                cols[idx + 4].text.strip() if (idx + 4) < len(cols) else None,
                cols[idx + 5].text.strip() if (idx + 5) < len(cols) else None,
                self._parse_num(cols[idx + 6].text.strip()) if (idx + 6) < len(cols) else None,
                self._parse_num(cols[idx + 7].text.strip()) if (idx + 7) < len(cols) else None,
                self._parse_num(cols[idx + 8].text.strip()) if (idx + 8) < len(cols) else None,
                self._parse_num(cols[idx + 9].text.strip()) if (idx + 9) < len(cols) else None,
                market
            ))
            count += 1
            print(f"已提取第{count}条: {code} {name},价格: {price}")
        # 批量插入
        if data:
            self.cursor.executemany("""
            INSERT INTO a_stock_data 
            (bStockNo, bStockName, fLatestPrice, fChangePercent, fChangePrice,
            fVolume, fTurnover, fAmplitude, fHighestPrice, fLowestPrice,
            fOpenPrice, fPreClosePrice, bMarketType)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
            fLatestPrice = VALUES(fLatestPrice),
            fChangePercent = VALUES(fChangePercent),
            fChangePrice = VALUES(fChangePrice)
            """, data)
            self.db.commit()
            print(f"{market}数据爬取完成,共{len(data)}条有效数据")
        else:
            print(f"{market}未获取到有效数据")

4

5

6

2.心得:
在爬取时进入该网站查看结构,可以看出数据都在table里
通过table = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".quotetable tbody")))来进行爬取
微信图片_20251202103627
同样也可以看出每一行是一个tr列表,每个tr下都有td来存储信息
就用rows = table.find_elements(By.TAG_NAME, "tr")和cols = row.find_elements(By.TAG_NAME, "td")来爬取信息
微信图片_20251202103641
综上,本次股票数据爬取实训,让我将 Selenium 理论知识真正落地实践。课堂上学的driver.get()、显式等待和元素定位等方法,在爬取动态页面时发挥了关键作用。实训中遇到表格数据缺失的问题,通过分析页面结构、过滤无效数据(如含 “加自选” 的行),最终成功提取有效信息。这个过程让我深刻理解到爬虫开发不仅需要技术,更要注重数据校验和异常处理。这次实践不仅巩固了 Selenium 技能,更培养了我解决实际问题的能力,收获满满。

Gitee文件夹链接:https://gitee.com/njs5/bgyuhnji/tree/homework4/gupiao

二,作业二
要求:熟练掌握 Selenium 查找HTML元素、实现用户模拟登录、爬取Ajax网页数据、等待HTML元素等内容。使用Selenium框架+MySQL爬取中国mooc网课程资源信息(课程号、课程名称、学校名称、主讲教师、团队成员、参加人数、课程进度、课程简介)
候选网站:中国mooc网:https://www.icourse163.org
输出信息:MYSQL数据库存储
1.核心代码和运行结果
创建数据库

点击查看代码
    def create_table(self):
        """创建课程信息表"""
        self.db_cursor.execute("DROP TABLE IF EXISTS mooc_courses")
        create_table_sql = """
        CREATE TABLE IF NOT EXISTS mooc_courses (
            id INT AUTO_INCREMENT PRIMARY KEY,
            cCourse VARCHAR(255) NOT NULL,
            cCollege VARCHAR(255),
            cTeacher VARCHAR(255),
            cTeam TEXT,
            cCount INT,
            cProcess VARCHAR(255),
            cBrief TEXT
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
        """
        self.db_cursor.execute(create_table_sql)
        self.db_conn.commit()
手动登录和进入个人中心
点击查看代码
    def manual_login(self):
        """手动登录流程"""
        self.driver.get("https://www.icourse163.org")
        try:
            self.wait.until(
                EC.element_to_be_clickable((By.XPATH, '//*[@id="web-nav-container"]/div/div/div/div[3]/div[2]/div/span/a')))
            print("登录成功!开始进入个人中心...")
            return True
        except TimeoutException:
            print("登录超时!请检查是否在规定时间内完成登录")
            return False

    def navigate_to_personal_center(self):
        """导航到个人中心"""
        personal_center_btn = self.wait.until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="web-nav-container"]/div/div/div/div[3]/div[2]/div/span/a')))
        personal_center_btn.click()
        print("已进入个人中心")
        time.sleep(3)
        self.original_window = self.driver.current_window_handle
        return True
提取课程信息和插入数据到数据库
点击查看代码
    def extract_course_info(self, course_url):
        """提取课程信息"""
        self.driver.get(course_url)
        course_info = {}
        # 等待页面加载
        self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".course-title")))
        # 课程名称
        course_info['cCourse'] = self.driver.find_element(By.CSS_SELECTOR, ".course-title").text.strip()
        # 学校名称
        course_info['cCollege'] = "未知"
        school_link = self.wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="j-teacher"]/div/a')))
        data_label = school_link.get_attribute("data-label")
        if data_label:
            course_info['cCollege'] = data_label.split(".")[-1].strip() if "." in data_label else \
            data_label.split("-")[-1].strip()
        # 主讲教师和团队
        teacher_elem = self.driver.find_element(By.XPATH,'//*[@id="j-teacher"]/div/div/div[2]/div/div/div/div/div/h3[1]')
        course_info['cTeacher'] = teacher_elem.text.strip()
        team_elems = self.driver.find_elements(By.XPATH,'//*[@id="j-teacher"]/div/div/div[2]/div/div/div/div/div/h3')
        course_info['cTeam'] = ",".join([t.text.strip() for t in team_elems]) if team_elems else course_info['cTeacher']
        # 参加人数
        count_element = self.wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="course-enroll-info"]/div/div[1]/div[4]/span[2]')))
        count_text = count_element.text.strip().replace('万', '0000').replace(',', '')
        course_info['cCount'] = int(re.findall(r'(\d+)', count_text)[0]) if re.findall(r'(\d+)',count_text) else 0
        # 课程进度
        time_container = self.driver.find_element(By.CSS_SELECTOR,".course-enroll-info_course-info_term-info_term-time")
        course_info['cProcess'] = "".join([span.text.strip() for span in time_container.find_elements(By.TAG_NAME, "span")])
        # 课程简介
        brief_element = self.driver.find_element(By.CSS_SELECTOR, "#j-course-heading-intro")
        course_info['cBrief'] = brief_element.text.strip()
        print(f"成功提取课程信息: {course_info['cCourse']}")
        return course_info

    def save_to_db(self, course_info):
        """保存到数据库"""
        if not course_info:
            return
        insert_sql = """
        INSERT INTO mooc_courses (cCourse, cCollege, cTeacher, cTeam, cCount, cProcess, cBrief)
        VALUES (%s, %s, %s, %s, %s, %s, %s)
        """
        self.db_cursor.execute(insert_sql, (
            course_info['cCourse'], course_info['cCollege'],
            course_info['cTeacher'], course_info['cTeam'], course_info['cCount'],
            course_info['cProcess'], course_info['cBrief']
        ))
        self.db_conn.commit()
        print(f"成功保存课程到数据库: {course_info['cCourse']}")

7

8
2.心得
个人中心的查找方式是通过xpath://*[@id="web-nav-container"]/div/div/div/div[3]/div[2]/div/span/a
微信图片_20251202104935
课程名称用 course_info['cCourse'] = self.driver.find_element(By.CSS_SELECTOR, ".course-title").text.strip()来提取信息
微信图片_20251202104941
学校名称,主讲教师和团队,课程进度等基本上用的都是xpath来进行爬取。
综上,开发 MOOC 爬虫时,我先通过分析网页结构获取 XPath 定位元素,初期直接爬取指定的两个课程链接,完成基础数据提取。但后续对照作业要求发现遗漏了模拟登录环节,于是调整代码逻辑,新增手动登录流程 —— 等待用户完成登录验证后,导航至个人中心,再爬取个人账户中的课程数据。这个过程让我深刻体会到需求理解的重要性,也掌握了从 "固定链接爬取" 到 "个人账户数据抓取" 的思路转变:登录状态的保持、个人中心页面的元素定位,都需要结合实际交互流程调整代码。通过 XPath 精准定位登录入口和个人中心按钮,确保了从公开数据爬取到个人账户数据获取的完整实现,也让我对动态页面交互和用户态数据抓取有了更具体的实践认知。

Gitee文件夹链接:https://gitee.com/njs5/bgyuhnji/tree/homework4/mooc

三,作业三
要求:掌握大数据相关服务,熟悉Xshell的使用。完成文档 华为云_大数据实时分析处理实验手册-Flume日志采集实验(部分)v2.docx 中的任务,即为下面5个任务,具体操作见文档。
环境搭建:任务一:开通MapReduce服务
实时分析开发实战:任务一:Python脚本生成测试数据。任务二:配置Kafka。任务三: 安装Flume客户端。任务四:配置Flume采集数据
输出:实验关键步骤或结果截图。
1.关键步骤和结果截图
(1)开通MapReduce服务
9
安全组都已经配置了
(2)Python脚本生成测试数据(用的PUYY)
10
(3)配置Kafka
下载Flume客户端
11
校验下载的客户端文件包
12
安装Kafka客户端
13
在kafka中创建topic
14
(4)安装Flume客户端
下载Flume客户端
15
校验下载的客户端文件包
16
安装Flume运行环境
17
18
安装Flume客户端
19
重启Flume服务
20
(5)配置Flume采集数据
修改配置文件
21
创建消费者消费kafka中的数据
22
后续跟着ppt把该释放的都释放了
2.心得
之前华为云实验包含 3 个任务,其中有这部分正好是我负责的,因此对这一环节印象格外深刻。从搭建 MRS 集群、配置 Kafka 到编写 Flume 采集规则,每一步操作都需精准把控 —— 曾因 Broker IP 填写错误导致数据传输失败,排查后才深刻体会到配置细节的重要性。实验中,我更加理解了 Flume “源 - 通道 - 下沉” 的架构逻辑,也更熟练了 Linux 命令和云资源管理。这次实践不仅提升了动手能力,更让我明白大数据组件协同需兼顾兼容性与准确性,为后续学习打下了基础。

posted @ 2025-12-02 10:58  bd2  阅读(0)  评论(0)    收藏  举报