20222217 2024-2025-2 《Python程序设计》实验四报告

课程:《Python程序设计》
班级: 2222
姓名: 黄子宽
学号:20222217
实验教师:王志强
实验日期:2025年5月14日
必修/选修: 公选课

一、实验目的

本次python实验四,我将用丰富的python知识实现一个点赞神器!每天晚上启动程序,就可以在短短的时间内给发了朋友圈的微信好友点赞!省时又省力。

二、实验内容

(一)整体架构
本程序通过自动化方式操作微信朋友圈,实现点赞功能并提取朋友圈内容保存至数据库。程序采用模块化设计,主要包含时间处理、界面交互、数据存储和媒体保存四个核心模块。
数据处理流程
1.朋友圈内容解析:通过 pywinauto 获取朋友圈 UI 元素,提取用户名称、发布时间和正文内容。
2.时间格式标准化:使用正则表达式识别多种时间格式(如 "几分钟前"、"昨天" 等),并统一转换为YYYY-MM-DD HH:MM格式。
3.数据去重存储:将解析后的内容存入 SQLite 数据库,通过唯一组合判断避免重复数据。
(二)关键模块实现
1.时间解析模块

def content_filter(content: str):
    """将朋友圈多样化的时间注脚统一转换为标准格式"""
    content = content.split('\n')
    name = content.pop(0).strip(":")
    
    # 定位时间字符串并处理
    for idx in range(len(content)-1, -1, -1):
        if re.match(r"(\d+)分钟前.*|(\d+)小时前.*|昨天.*|(\d+)天前.*|\d+月\d+日 \d+:\d+", content[idx]):
            time_str = content.pop(idx)
            break
    
    # 根据不同时间格式进行转换
    if re.match(r"(\d+)分钟前.*", time_str):
        delta = int(re.findall(r"(\d+)分钟前", time_str)[0])
        time = (datetime.now() - timedelta(minutes=delta)).strftime('%Y-%m-%d %H:%M')
    # 其他时间格式处理逻辑...
    
    return name, time, '\n'.join(content)

2.界面自动化模块
通过 pywinauto 实现微信窗口定位和操作:查找微信进程并连接到朋友圈窗口、定位朋友圈列表和评论按钮、实现滚动操作以加载更多内容、点击 "评论" 和 "赞" 按钮完成点赞
3.数据存储模块

# 创建表结构
cursor.execute('''
CREATE TABLE IF NOT EXISTS qx_pyq (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    time TEXT NOT NULL,
    text TEXT NOT NULL
)
''')
# 插入数据
cursor.execute('''
INSERT INTO qx_pyq (name, time, text)
VALUES (?, ?, ?)
''', (name, time, text))

4.媒体保存模块

def save_video_image(picture_list, material):
    """保存图片并通过哈希算法判断重复"""
    for pic in picture_list:
        if pic.is_visible():
            picture = pic.capture_as_image()
            # 生成文件名并检查是否存在
            while Path(pic_path).exists():
                # 计算哈希值判断是否重复
                if phash(picture) - phash(Image.open(pic_path)) > 2:
                    print("图片重复,跳过")
                    return
            picture.save(pic_path)

5.程序执行流程
初始化:连接微信进程,创建数据库表
内容提取与点赞:遍历朋友圈列表,定位 "评论" 按钮,点击 "评论" 展开菜单,点击 "赞" 完成操作,提取当前朋友圈内容并进行时间标准化
数据处理:检查数据库中是否存在相同内容,若不存在则插入新记录
滚动控制:向下滚动页面加载更多内容,当检测到重复内容或达到点赞上限时停止
(三)源代码

from psutil import process_iter
from pywinauto.application import Application
from pywinauto import mouse
from PIL import Image
from imagehash import phash
from datetime import datetime, timedelta
from pathlib import Path
import sqlite3
import re


def content_filter(content: str):
    """
    朋友圈的时间注脚有下列情况:
    1. 7分钟前 视频号·资讯小窗
    2. 11小时前 微信读书
    3. 昨天
    4. 7天前
    5. 4月17日 8:53
    根据以上情况将时间过滤为 `%Y-%m-%d %H:%M:%S` 格式
    """
    content = content.split('\n')

    name = content.pop(0).strip(":")

    for idx in range(len(content) - 1, -1, -1):
        if re.match(r"(\d+)分钟前.*|(\d+)小时前.*|昨天.*|(\d+)天前.*|\d+月\d+日 \d+:\d+",
                    content[idx]):
            time = content.pop(idx)
            break

    text = '\n'.join(content)
    if re.match(r"(\d+)分钟前.*", time):
        delta = int(re.findall(r"(\d+)分钟前", time)[0])
        time = (datetime.now() -
                timedelta(minutes=delta)).strftime('%Y-%m-%d %H:%M')
    elif re.match(r"(\d+)小时前.*", time):
        delta = int(re.findall(r"(\d+)小时前", time)[0])
        time = (datetime.now() -
                timedelta(hours=delta)).strftime('%Y-%m-%d %H:00')
    elif re.match(r"昨天.*", time):
        time = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d 00:00')
    elif re.match(r"(\d+)天前.*", time):
        delta = int(re.findall(r"(\d+)天前", time)[0])
        time = (datetime.now() -
                timedelta(days=delta)).strftime('%Y-%m-%d 00:00')
    else:
        month = re.findall(r"(\d+)月", time)[0]
        day = re.findall(r"(\d+)日", time)[0]
        hour = re.findall(r"(\d+):", time)[0]
        minute = re.findall(r":(\d+)", time)[0]
        time = f"2024-{month:0>2}-{day:0>2} {hour:0>2}:{minute:0>2}"

    return name, time, text


def save_video_image(picture_list, material):
    serial_num = 0
    guess_repeated = True
    for pic in picture_list:
        if pic.is_visible():
            picture = pic.capture_as_image()
            pic_path = "example_image/%s~%s~%s~%s.png" % (material +
                                                          (serial_num, ))
            while Path(pic_path).exists():
                print(f"{serial_num}系列已存在")

                if guess_repeated:
                    if phash(pic.capture_as_image()) - phash(
                            Image.open(pic_path)) > 2:
                        print(f"本次图片与{serial_num}系列极为相似,出现重复,跳过")
                        return
                    else:
                        guess_repeated = False

                serial_num += 1
                pic_path = "example_image/%s~%s~%s~%s.png" % (material +
                                                              (serial_num, ))
            picture.save(pic_path)
        else:
            print("视频已出现,不可见")


# 创建qx_pyq表,已存在则不执行
with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()

    cursor.execute('''
    CREATE TABLE IF NOT EXISTS qx_pyq (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        time TEXT NOT NULL,
        text TEXT NOT NULL
    )
    ''')

    conn.commit()

# 连接朋友圈进程
PID = 0
for proc in process_iter():
    if "WeChat.exe" == proc.name():
        PID = proc.pid
        break
else:
    print("微信尚未打开")
    exit(0)

app = Application(backend='uia').connect(process=PID)

pyq = app.window(title="朋友圈")
scroll_point = pyq.rectangle().mid_point()
pyq_list = pyq.child_window(title="朋友圈", control_type="List")
pyq_list.draw_outline(colour='blue', thickness=7)

# 对点赞计数
sum_thumb = 0
thumbed = 0
with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()

    # 点赞
    for more_button in pyq_list.iter_children(title="评论",
                                              control_type="Button"):
        # 按钮位置不在屏幕内,舍去
        if more_button.rectangle().top < 0:
            continue

        while not more_button.is_visible():
            mouse.scroll(scroll_point, -1)
        mouse.scroll(scroll_point, -1)

        # 点击“更多”
        more_button.draw_outline(colour='red', thickness=7)
        more_button.click_input()

        # 点击“赞”
        thumb_up = pyq.child_window(title="赞", control_type="Button")
        if thumb_up.exists():
            thumb_up.draw_outline(colour='green', thickness=7)
            thumb_up.click_input()
            sum_thumb += 1
            print("点击量", sum_thumb)
            thumbed = 0
        else:
            thumbed += 1

        # 提取数据
        content = more_button.parent().parent().parent().parent().parent(
        ).window_text()
        name, time, text = content_filter(content)
        print(name, time)

        # 如果提取到之前的圈,退出
        cursor.execute(
            f'''
        SELECT * FROM qx_pyq WHERE name=? AND time=? AND text=?
        ''', (name, time, text))
        if cursor.fetchone() or thumbed > 2:
            print("出现重复,已点到之前的赞")
            break

        # 插入qx_pyq表
        cursor.execute(
            '''
        INSERT INTO qx_pyq (name, time, text)
        VALUES (?, ?, ?)
        ''', (name, time, text))
        conn.commit()

        serial_num = 0
        guess_repeated = True
       
        while more_button.is_visible():
            mouse.scroll(scroll_point, -1)

(四)实验截图



视频

三、 实验过程中遇到的问题和解决过程

  • 问题1:微信窗口控件层级复杂,且版本更新可能导致控件名称、层级结构变化(如 “评论” 按钮标题变为 “更多” 或位置调整),导致pywinauto无法稳定获取元素。
  • 问题1解决方案:
    通过parent()方法向上回溯多层父控件,结合window_text()提取上下文内容,而非仅依赖单一控件名称。例如:
content = more_button.parent().parent().parent().parent().parent().window_text()

使用正则表达式模糊匹配控件标题(如title_re="评论|更多"),并添加超时重试机制:

from pywinauto.findwindows import find_windows
buttons = find_windows(title_re="评论|更多", control_type="Button")
  • 问题2:朋友圈内容动态加载,滚动操作可能无法及时加载新内容,导致元素仍不可见或重复处理。
  • 问题2解决方案:滚动逻辑优化:通过记录滚动次数和最后可见元素位置,判断是否到达页面底部。
last_scroll_pos = pyq_list.rectangle().bottom
while True:
    mouse.scroll(scroll_point, -1)
    current_pos = pyq_list.rectangle().bottom
    if current_pos == last_scroll_pos or scroll_count > 100:
        break
    last_scroll_pos = current_pos
    scroll_count += 1

使用time.sleep(1)模拟人类操作速度,并通过wait('visible')确保元素加载完成:

from pywinauto import timings
timings.Timings.defaults()['waittime'] = 5  # 设置超时时间
more_button.wait('visible')

四、实验总结

在实现微信朋友圈自动点赞与数据提取的实验中,通过 pywinauto 完成界面自动化操作,结合正则表达式解析多样化时间格式,利用 SQLite 实现数据去重存储,并借助 phash 算法处理图片重复问题。过程中攻克了界面元素定位不稳定、时间解析不完整、数据库性能瓶颈等挑战,通过多层级回溯定位、扩展正则匹配规则、连接复用与批量提交等策略优化。最终程序实现了朋友圈内容的高效采集、标准化处理及自动化点赞,验证了自动化技术在社交应用数据处理中的可行性,为类似场景提供了可复用的技术方案,同时通过模拟人类操作间隔降低了风控风险,保障了程序稳定性与合规性。

五、课程总结

通过 Python 课程的学习与朋友圈自动化实验的实践,我深刻体会到 Python 的简洁性与灵活性,同时,实验暴露的界面定位不稳定、数据解析歧义等问题,促使我反复优化代码逻辑,强化了 “代码健壮性” 与 “用户体验” 的考量。课程与实验的结合,不仅让我掌握了 Python 开发的核心技能,更培养了 “问题拆解 — 方案设计 — 编码实现 — 调试优化” 的工程思维。未来需进一步探索 Python 在大数据分析、机器学习等领域的应用,深化 “用代码解决实际问题” 的能力,让技术真正服务于效率提升与价值创造。总的来说,Python 课让我从 “只会照猫画虎写代码” 慢慢转向 “思考怎么合理组织代码”,而这个实验就像是一场实战演练,把课上学的理论逼到了 “必须用起来” 的境地。虽然过程有点痛苦,但看着自己写的类能实实在在解决问题,还是挺有成就感的 —— 这大概就是编程的魅力吧。