数据采集第四次作业-102302128吴建良

作业1:基于 Selenium 和 MySQL 的股票数据爬取

码云仓库:https://gitee.com/wujianliang9/2025-data-collection/tree/master/第四次作业/作业1

一、核心代码与方法 (Code & Methodology)

核心代码

以下是实现爬取、数据清洗、翻页和入库功能的关键代码片段(StockScraper 类)。

A. 数据库配置与连接

配置 MySQL 连接信息,并设置了 stock_info 表格的 stock_codeboard_type 联合唯一键,以避免重复插入。

# ================= 配置区域 =================
DB_CONFIG = {
    'host': 'localhost',
    'user': 'root',
    'password': 'Wu258039',  # 您的密码
    'database': 'stock_data',
    'charset': 'utf8mb4'
}
# ...
class StockScraper:
    def __init__(self):
        try:
            self.db = pymysql.connect(**DB_CONFIG)
            self.cursor = self.db.cursor()
        except Exception as e:
            print(f"❌ 数据库连接失败: {e}")
            exit()
        
        # ... (浏览器启动代码)

B. 数据解析 (parse_page)

主要通过定位表格的 tbody trtd 元素,使用修正后的索引进行数据提取,并清洗空值。

    def parse_page(self):
        """解析当前页表格,并返回清洗后的数据列表"""
        WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, 'tbody tr:nth-child(1)'))
        )
        rows = self.driver.find_elements(By.CSS_SELECTOR, 'tbody tr')
        page_data = []
        
        for row in rows:
            cols = row.find_elements(By.TAG_NAME, 'td')
            if len(cols) < 14: continue 
            
            try:
                # 提取数据 (使用修正后的列索引)
                data_row = (
                    cols[0].text, cols[1].text, cols[2].text, cols[4].text,
                    cols[5].text, cols[6].text, cols[7].text, cols[8].text,
                    cols[9].text, cols[10].text, cols[11].text, cols[12].text,
                    cols[13].text
                )
                # 清洗 '-' 或 '--' 等空值为 '0'
                cleaned_row = tuple(x.replace('--', '0').replace('-', '0') for x in data_row)
                page_data.append(cleaned_row)
            except Exception:
                continue
        return page_data

C. 翻页与稳定机制 (run)

通过定位表格的 ID 来实现翻页,并使用了 EC.staleness_of 等待机制,确保页面完全刷新后再进行下一次爬取。

    def run(self):
        # ... (循环板块和页数)
        # 1. 处理弹窗 (省略)
        # 2. 爬取数据 (省略)

        # 3. 翻页逻辑
        if page < MAX_PAGES:
            try:
                # 记录当前第一行元素(用于判断页面刷新)
                first_row = self.driver.find_element(By.CSS_SELECTOR, 'tbody tr:nth-child(1)')
                
                # 等待翻页按钮出现并可点击 (使用 By.ID 定位,最稳定)
                next_btn = WebDriverWait(self.driver, 10).until(
                    EC.element_to_be_clickable((By.ID, "table_wrapper-table_next"))
                )
                
                # 强制点击翻页 (使用 JS 解决交互性问题)
                self.driver.execute_script("arguments[0].click();", next_btn)
                
                # 关键:等待旧数据失效,证明新页面已加载
                WebDriverWait(self.driver, 10).until(EC.staleness_of(first_row))
                time.sleep(1) # 增加缓冲时间
                
            except TimeoutException:
                print("⚠️ 翻页按钮加载超时,停止翻页。")
            except Exception as e:
                print(f"❌ 翻页时发生未知错误: {e}")

## 二、运行结果 (Results)

image

1. 终端日志 (Output Log)

image

三、心得体会 (Reflections)

1.这次作业最头疼的就是那个 chromedriver。一开始以为代码逻辑错了,结果发现是驱动自己在崩溃(那个 Stacktrace 错误简直噩梦)。最后发现,用 Python 代码去修一个驱动程序的底层 bug 几乎是不可能的。学到的教训就是:遇到底层崩溃,别瞎折腾代码了,赶紧换个稳定版本的驱动才是王道。

2.数据库终于搞定了 虽然一开始用 MySQL 费了点劲,但看到最后 60 条数据成功入库,而且代码还能自动处理重复数据,那种成就感还是很棒的。比起之前存文件,数据库存取数据真的是规范又可靠。

3.爬虫和反爬就是一场战争 整个过程就是不停地和网页斗智斗勇。它异步加载数据,我就用 WebDriverWait 守着;它突然弹广告,我就写代码秒关。虽然代码修修改改很多次,但每解决一个 bug,都感觉自己离数据更近了一步,这种感觉还挺酷的。

作业②:Selenium 爬取中国 mooc 网课程资源

码云仓库:https://gitee.com/wujianliang9/2025-data-collection/tree/master/第四次作业/作业2

一、核心思路与代码 (Core Logic and Code)

1. 数据库初始化与存储

由于本地环境限制,将作业要求的 MySQL 替换为 Python 内置的 SQLite。

# --- 1. 数据库初始化 ---
def course_db():
    conn = sqlite3.connect('course.db')
    # 创建 course_info 表,包含所有要求的字段
    conn.execute('''
                 CREATE TABLE IF NOT EXISTS course_info
                 (
                     id INTEGER PRIMARY KEY AUTOINCREMENT,
                     课程名称 VARCHAR(100) NOT NULL,
                     学校名称 VARCHAR(100),
                     主讲老师 VARCHAR(50),
                     团队成员 TEXT,
                     参加人数 VARCHAR(20),
                     课程进度 VARCHAR(50),
                     课程简介 TEXT
                     )
                 ''')
    conn.commit()
    conn.close()

# --- 2. 保存数据 ---
def save_to_db(data):
    conn = sqlite3.connect('course.db')
    try:
        conn.execute(
            'INSERT INTO course_info (课程名称, 学校名称, 主讲老师, 团队成员, 参加人数, 课程进度, 课程简介) VALUES (?, ?, ?, ?, ?, ?, ?)',
            data)
        conn.commit()
    except Exception as e:
        print(f"数据库插入错误: {e}")
    finally:
        conn.close()

2. Selenium 核心操作

主要步骤包括:启动浏览器、模拟登录、页面跳转、筛选元素和 Ajax 数据提取。

A. 启动与登录 (Login & Navigation)

为确保登录成功率,采用手动扫码后暂停 (input()) 的方式,并使用 os.path.join 确保 chromedriver 路径的跨平台兼容性。

    # ... (驱动配置代码) ...
    driver = webdriver.Chrome(service=service, options=chrome_options)

    try:
        driver.get('https://www.icourse163.org/')
        time.sleep(2)

        # 1. 点击登录按钮 (采用 class_name 定位)
        try:
            button = driver.find_element(By.CLASS_NAME, '_3uWA6')
            button.click()
        except:
            print("未找到首页登录按钮")

        # 2. 手动扫码等待 (等待用户操作完成)
        print("🔥🔥🔥 请现在拿起手机,扫描浏览器上的二维码登录!🔥🔥🔥")
        input() # 等待用户按回车

        # 3. 跳转到搜索页面并筛选
        url = 'https://www.icourse163.org/search.htm?search=%E8%AE%A1%E7%AE%97%E6%9C%BA#/'
        driver.get(url)
        time.sleep(3)
        
        # 4. 选中国家精品课 (通过定位父级 span 实现稳定点击)
        parent = driver.find_element(By.XPATH, "//span[contains(@class, 'ant-checkbox')]")
        parent.click()
        print("已筛选:国家精品课")
        time.sleep(3)

B. 数据提取与容错 (Extraction with Tolerance)

由于网站使用了混淆类名(如 _3NYsM_1vfZ-),这些类名随时可能变化,因此在代码中针对每个字段都加入了多重 Xpath 容错机制,确保在主 Xpath 失效时能通过备用 Xpath 成功提取数据。

        # 5. 提取课程列表
        courses = driver.find_elements(By.XPATH, '//div[contains(@class, "_3NYsM")]') 
        # 备用 Xpath (更通用)
        if not courses: 
             courses = driver.find_elements(By.XPATH, '//div[contains(@class, "u-clist")]//div[contains(@class, "u-courseCard")]')

        for i, course in enumerate(courses):
            try:
                # 课程名称 (容错查找)
                try:
                    course_name = course.find_element(By.XPATH, './/div[contains(@class, "_1vfZ-")]').text
                except:
                    course_name = course.find_element(By.XPATH, './/span[contains(@class, "u-course-name")]').text

                # 学校名称 (容错查找)
                try:
                    school_name = course.find_element(By.XPATH, './/a[contains(@class, "_3vJDG")]').text
                except:
                    school_name = course.find_element(By.XPATH, './/a[contains(@class, "t21")]').text
                
                # 教师和团队成员(省略其他字段的容错提取)...

                # 保存数据到数据库
                save_data_tuple = (
                course_name, school_name, main_teacher, team_member_str, attend, process, introduction)
                save_to_db(save_data_tuple)
                
            except Exception as err:
                print(f"解析第 {i + 1} 个课程出错: {err}")
                continue

二、运行结果 (Results)

程序成功提取了搜索结果页面上的课程信息,并将其存储在 course.db 文件中。
image

image

三、心得体会 (Reflections)

面对登录这一复杂交互,我避免了模拟键盘输入账号密码的低效和容易被反爬的风险,而是采用了 “半自动化” 模式:由 Selenium 触发登录弹窗,然后通过 input() 暂停程序,等待用户手动扫码登录。这极大地提高了登录的成功率和稳定性。

中国 mooc 网使用了大量的 混淆类名(例如 _3NYsM、_1vfZ-)。这些类名是随机生成的,非常不稳定,是典型的反爬手段。我的解决方案是,对每个关键数据点(名称、学校、教师)都提供了多个备用 Xpath,或者使用 [contains(@class, "...")] 这种模糊匹配方式。一旦主定位失效,备用 Xpath 能够接替工作,保障了程序的鲁棒性。

整个课程列表是在筛选后通过 Ajax 异步加载的。在数据提取环节,尤其是在提取教师和团队成员时,我通过 find_elements 提取一个列表,然后通过列表长度判断是主讲教师还是团队教学,并进行相应的数据整合("、".join())。这种设计有效应对了数据结构的微小变化。

作业③:

要求:

掌握大数据相关服务,熟悉Xshell的使用
完成文档 华为云_大数据实时分析处理实验手册-Flume日志采集实验(部分)v2.docx 中的任务,即为下面5个任务,具体操作见文档。
环境搭建:
任务一:开通MapReduce服务
实时分析开发实战:
任务一:Python脚本生成测试数据
任务二:配置Kafka
任务三: 安装Flume客户端
任务四:配置Flume采集数据

环境搭建:

image

image

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

image
image

任务二:配置Kafka

image
image
image

任务三: 安装Flume客户端

image
image

任务四:配置Flume采集数据

image
image
image

心得体会:

实时数据采集的关键作用在实验中得到充分体现。Flume作为大数据生态中的数据采集组件,承担着将业务系统产生的实时日志数据高效、可靠地传输到Kafka消息队列的重要任务,这种架构设计确保了数据分析的时效性,为后续的实时处理和分析奠定了坚实基础。

posted @ 2025-12-09 22:02  wujianliang  阅读(6)  评论(0)    收藏  举报