作业4

数据采集与融合技术作业4

作业①:

要求:熟练掌握 Selenium 查找 HTML 元素、爬取 Ajax 网页数据、等待 HTML 元素等内容。 使用 Selenium 框架+ MySQL 数据库存储技术路线爬取“沪深 A 股”、“上证 A 股”、“深证 A 股”3 个板块的股票数据信息。
候选网站:东方财富网:http://quote.eastmoney.com/center/gridlist.html#hs_a_board

(1)实验步骤:

1.采集沪深京A股数据,这是默认的 Tab

复制xpath://*[@id="mainc"]/div/div/div[4]/table,定位到表格
image-7

        # 使用精确的 XPath 定位到数据表格的主体部分,并提取所有数据行
        main_table_xpath = '//*[@id="mainc"]/div/div/div[4]/table' # 主数据表格的 XPath
        trs = tree.xpath(f'{main_table_xpath}/tbody/tr') # 从表格 tbody 中获取所有行
        
        data_list = [] # 用于存储从表格中提取出的每行股票数据

表格中要过滤掉表头行跟无效文本
任务1无效文本

    for i, tr in enumerate(trs): # 遍历每一行数据
            tds = [td.strip() for td in tr.xpath('.//text()') if td.strip()]
            # not any(...): 检查行内是否包含表头关键字,排除表头行
            if len(tds) > 5 and not any(col in ''.join(tds) for col in ['代码', '名称', '相关', '沪股通', '深股通', '港股通']):
                # 过滤掉一些无关的文本,如“股吧”、“资金流”等
                filtered_tds = [item for item in tds if item not in ['股吧', '资金流', '数据']]
                if len(filtered_tds) < 13: # 如果提取的数据少于13列(序号+12个字段)
                    filtered_tds.extend([None] * (13 - len(filtered_tds))) 
                data_list.append(filtered_tds) # 将处理后的数据行添加到列表中
                if i < 3:  
                    print(f"  第{i+1}行数据: {filtered_tds[:13]}...") 

对应信息插入数据库

        for data in data_list: # 遍历每条股票数据进行插入
            try:
                if len(data) >= 13: 
                    cursor.execute(
                        f"""INSERT INTO `{table_name}` 
                           (stocksymbol, stockname, LatestPrice, Pricelimit, Riseandfall, 
                            volume, turnover, amplitude, max_price, min_price, today_open, yesterday_close) 
                           VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", 
                        (data[1], data[2], data[3], data[4], data[5], # 股票代码、名称、最新价、涨跌幅、涨跌额
                         data[6], data[7], data[8], data[9], data[10], data[11], data[12]) # 成交量、成交额、振幅、最高、最低、今开、昨收
                    )
                    success_count += 1
            except pymysql.MySQLError as e: 
                print(f"插入数据时出错: {e}")
                print(f"问题: {data}") 
            except IndexError: 
                print(f"数据行格式不正确或长度不足,跳过: {data}")

        conn.commit()
        print(f"{table_name} 数据保存完成,共 {success_count} 条记录")
        return True

2.采集上证A股数据

使用浏览器复制 XPath 来定位深证A股的 Tab 按钮
image

scrape_stock_data(driver, con, cursor, "shangzhengagu", '//*[@id="mainc"]/div/div/div[2]/ul/li[2]/a') 

其余步骤一致

3.采集深证A股数据

使用浏览器复制 XPath 来定位深证A股的 Tab 按钮
image

scrape_stock_data(driver, con, cursor, "shenzhengagu", '//*[@id="mainc"]/div/div/div[2]/ul/li[3]/a')

输出信息:

终端输出信息
image-3

MySQL数据库
hushenjing
image-4
shangzhengagu
image-5
shenzhengagu
image-6

(2)心得体会:

重点在于通过 XPath 精确定位元素,并对网络延迟进行处理,确保页面数据的完整性。通过 cursor.execute 方法将数据存储到 MySQL 数据库中,加深了我对数据库操作的理解。

代码仓库:

https://gitee.com/oozyt/datacollecting/blob/master/作业4/stock.py

作业2

o 要求:熟练掌握 Selenium 查找 HTML 元素、实现用户模拟登录、爬取 Ajax 网页数据、
等待 HTML 元素等内容。 使用 Selenium 框架+MySQL 爬取中国 mooc 网课程资源信息(课程号、课程名
称、学校名称、主讲教师、团队成员、参加人数、课程进度、课程简介)
o 候选网站:中国 mooc 网:https://www.icourse163.org

(1)实验步骤

1.登录操作

点击登录按钮
# 点击登录/注册按钮
wait.until(EC.element_to_be_clickable((By.XPATH, "//div[text()='登录/注册']"))).click() 

登录按钮

切换到登录iframe

切换到登录页面中的 iframe。登录表单位于 iframe 内,因此在输入凭据之前需要切换上下文。frame_to_be_available_and_switch_to_it 检查该 iframe 是否可用,并自动切换到这个 iframe。

# 切换到登录iframe
        wait.until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[contains(@id, 'x-URS-iframe')]")))

iframe

输入用户名和密码并点击登录按钮
        # 输入用户名和密码
        username_input = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@placeholder='请输入手机号' or @placeholder='请输入邮箱帐号']")))
        password_input = driver.find_element(By.XPATH, "//input[@placeholder='请输入密码']")
        username_input.send_keys(MOOC_USERNAME); password_input.send_keys(MOOC_PASSWORD) 
        # 点击登录按钮
        driver.find_element(By.XPATH, "//a[@id='submitBtn']").click() 
检查登录状态

利用登录后出现的个人中心定位

wait.until(EC.presence_of_element_located((By.XPATH, "//a[text()='个人中心']"))); print("登录成功!"); return True

2.搜索操作

定位搜索框

搜索框

点击元素定位到搜索框检查元素并提供xpath定位

        search_box = wait.until(EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'HprCh')]//input"))) # 定位搜索框
        search_box.send_keys(SEARCH_KEYWORD); # 输入关键词
        search_icon = wait.until(EC.element_to_be_clickable((By.XPATH, "//div[contains(@class, 'HprCh')]//span[contains(@class, 'E3Zsq')]")));
        search_icon.click(); # 点击搜索按钮
点击筛选框

由于直接搜索容易出现考研等宣传报名类课程,且很多要爬取信息字段没有,所有我在搜索之后进一步点击筛选框优化爬虫效率

        filter_checkbox = wait.until(EC.element_to_be_clickable((By.XPATH, "//label[contains(., '国家精品课')]")));
        driver.execute_script("arguments[0].click();", filter_checkbox); # 点击“国家精品课”筛选
点击课程卡片

开始我是使用js点击课程卡片,发现容易发生误触,跳转到了讲师界面。查看网页元素之后,直接定位到课程标题,不容易发生误触

课程标题

for i in range(len(all_cards_on_page)): # 遍历每张卡片
                original_window = driver.current_window_handle # 记录当前窗口句柄
                try:
                    all_cards = driver.find_elements(By.XPATH, "//div[contains(@class, '_3NYsM')]") # 重新获取卡片,避免StaleElementReferenceException
                    if i >= len(all_cards): continue

                    card_to_click = all_cards[i]
                    driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", card_to_click); # 滚动到卡片
                    time.sleep(0.5);
                    
                    # 精确点击课程标题div,避免误触
                    try:
                        course_title_div = card_to_click.find_element(By.XPATH, ".//div[contains(@class, '_1vfZ-')]") # 定位课程标题
                        driver.execute_script("arguments[0].click();", course_title_div) # 使用JS点击
                    except NoSuchElementException:
                        print(f"   第{page_num}页第{i+1}门课程未找到课程标题div,尝试点击整个卡片。")
                        card_to_click.click() # 未找到标题则点击整个卡片

3.爬取操作

进入课程详情页,定位到每一个需要爬取的信息,复制xpath,例如:
课程名字cName字段
课程名字

课程进度cProcess字段

时间

特别注意的是课程号不出现在课程详情页面上,而是出现在链接的url中

课程号

完整的数据抓取函数

def scrape_course_details(driver, wait):
    time.sleep(4) 
    data = {}
    # 从URL提取课程ID
    try: data['course_id'] = re.search(r'course/([A-Z0-9a-z_-]+)', driver.current_url).group(1) 
    except: data['course_id'] = "N/A"
    # 获取课程名称
    locators_name = [{'by': By.XPATH, 'value': "//span[contains(@class, 'course-title')]"}, {'by': By.XPATH, 'value': "//div[@class='title']/span"} ]
    data['course_name'] = get_text_with_fallbacks(driver, locators_name) 
    if not data['course_name']: return None 
    # 获取学校名称
    try: data['school_name'] = driver.find_element(By.XPATH, "//a[contains(@class, 'm-teachers_school-img')]").get_attribute('data-label').split('-')[-1] 
    except: data['school_name'] = get_text_with_fallbacks(driver, [{'by': By.XPATH, 'value': "//div[contains(@class, 'course-teacher-i')]//div[@class='name']"}])  
    # 获取参加人数
    locators_participants = [{'by': By.XPATH, 'value': "//span[@class='count']"}, {'by': By.XPATH, 'value': "//span[contains(text(), '人参加')]"}, {'by': By.XPATH, 'value': "//span[contains(text(), '人报名')]"} ]
    participants_text = get_text_with_fallbacks(driver, locators_participants)
    if participants_text:
        try: data['participant_count'] = int(re.search(r'(\d+)', participants_text.replace(',', '')).group(1)) 
        except: data['participant_count'] = 0
    # 获取课程进度/开课时间
    locators_process = [
        {'by': By.XPATH, 'value': "//div[@class='course-enroll-info_course-info_term-info']//span[contains(text(), '开课时间')]/following-sibling::span"}, 
        {'by': By.XPATH, 'value': "//div[contains(@class, 'ux-dropdown_hd')]"}, 
        {'by': By.XPATH, 'value': "//div[contains(@class, 'state-tag')]"}       
    ]
    data['course_status'] = get_text_with_fallbacks(driver, locators_process) 
    # 获取课程简介
    locators_intro = [{'by': By.ID, 'value': "j-rectxt2"}, {'by': By.ID, 'value': "j-rect-intro"} ]
    data['course_intro'] = get_text_with_fallbacks(driver, locators_intro) 
    # 获取教师信息 (主讲教师和团队成员)
    try:
        teacher_cards = driver.find_elements(By.XPATH, "//div[contains(@class, 'u-tchcard')]")
        teacher_names = [card.find_element(By.TAG_NAME, 'h3').text for card in teacher_cards if card.find_element(By.TAG_NAME, 'h3').text]
        data['main_teacher'] = teacher_names[0] if teacher_names else None 
        data['team_members'] = ', '.join(teacher_names[1:]) if len(teacher_names) > 1 else None 
    except: data['main_teacher'], data['team_members'] = None, None
    if not data.get('main_teacher'):
        data['main_teacher'] = get_text_with_fallbacks(driver, [{'by': By.XPATH, 'value': "//div[contains(@class, 'course-teacher-i')]//div[@class='name']"}])
    return data

输出信息:

终端输出信息

image

image-1

MYSQL数据库

image-2

(2)心得体会:

模拟登录流程感受到真实用户交互中的复杂性。这里需要考虑可能出现的弹窗,以及不同的 iframe。在 try-except 结构中有效地处理了可能的异常情况,确保了脚本的稳定性。使用xpath定位各爬取字段,加深了我对其的熟练程度与理解运用。

代码仓库:

https://gitee.com/oozyt/datacollecting/blob/master/作业4/mooc.py

作业③:

要求: 掌握大数据相关服务,熟悉 Xshell 的使用 完成文档 华为云_大数据实时分析处理实验手册-Flume 日志采集实验(部分)v2.docx 中的任务,即为下面 5 个任务,具体操作见文档

环境搭建:任务一:开通 MapReduce 服务

image-9

实时分析开发实战:

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

执行Python命令,测试生成100条数据

image-10

image-11

任务二:配置 Kafka

在kafka中创建topic
image-12
查看topic信息
image-13

任务三: 安装 Flume 客户端

下载flume客户端
提示:客户端生成成功,保存在服务器(192.168.0.155)如下路径:/tmp/FusionInsight-Client/FusionInsight_Cluster_1_Flume_Client.tar

image-14

校验下载的客户端文件包

image-15

image-16

安装Flume运行环境

image-17

image-18

执行安装脚本

image-19

服务重启成功

image-20

任务四:配置 Flume 采集数据

修改配置文件并创建消费者消费kafka中的数据

image-21

新开一个PuTTY会话窗口,进入Python脚本所在目录,执行python脚本,再生成一份数据。

image-22

原窗口已经消费出了数据:

image-23

心得体会:

在这个实验中,通过使用大数据工具(如 Flume 和 Kafka)进行数据流的处理,让我对大数据的架构有了初步的认识。环境的搭建与配置让我意识到,数据采集和分析流程的复杂性。

posted @ 2025-12-10 01:54  ooozyz  阅读(2)  评论(0)    收藏  举报