文章

python迷你项目学习

基于Python课老师布置的作业题目の学习存档。

一、统计中文文本词频,生成词云图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 使用 conda install 的话,要在管理员终端中下载

from os import path
# 中文文本分词
import jieba
# 词频统计
from collections import Counter
# 生成词云
from wordcloud import WordCloud, ImageColorGenerator
# 图片处理
# imageio.imread('cat.jpg')
import numpy as np
# import matplotlib.pyplot as plt
# 相比上面的库,图像处理功能更强大
from PIL import Image

# 设置基本路径
d = path.dirname(__file__)
# 设置停用词表路径
stopword_path = path.join(d, 'stop_words_zh.txt')
# 读取停用词表
# Return a list of the lines in the string, breaking at line boundaries
with open(stopword_path, 'r', encoding='utf-8') as stopword_f:
    stopword_list = stopword_f.read().splitlines()
# 设置要分析的文本路径
text_path = path.join(d, '杀人十角馆.txt')
# 读取文本
with open(text_path, 'r', encoding='utf-8') as text_f:
    text = text_f.read()
# seg_list暂存jieba.cut初步分割后的词表
seg_list = jieba.cut(text, cut_all=False, HMM=True)
liststr = '/'.join(seg_list)

# 去除标点和停用词
wordlist = []
for i in liststr.split('/'):
    # 去除文字前后空格
    word = i.strip()
    # 符合条件的词语加入列表(停用词表还是漏了很多词,不管了)
    if not(word in stopword_list) and len(word)>1:
        wordlist.append(word)
# 去停用词后,将列表转换为词频字典
frequency = dict(Counter(wordlist))

# 加载背景图
background_image = np.array(Image.open(path.join(d, 'image.jpg')))
# 提取图片颜色,设置词云颜色
bg_color = ImageColorGenerator(background_image, default_color=None)
# 生成词云
wc = WordCloud(
    # 背景颜色
    background_color='white', 
    # 背景图片
    mask=background_image, 
    # 中文汉字显示
    font_path='C:\Windows\Fonts\simhei.ttf', 
    # 最大词数
    max_words=500, 
    # 自定义颜色后记得在参数中指定
    color_func=bg_color
    )
# 根据词频生成词云
wc.generate_from_frequencies(frequency)
# 生成图片
wc.to_file('output.png')

  • 最终词云图:
    • (因为停用词表和文本不太搭,去除效果不佳)

output

二、电影推荐系统(协同过滤算法)

  • 参考文章:

    协同过滤算法概述与python 实现协同过滤算法基于内容
    协同过滤(cf)——usr-item和item-item介绍原创
    DataFrame对象数据去重:pandas: Get unique values and their counts in a column
    ndarray数组连接:NumPy: Concatenate arrays with np.concatenate, np.stack, etc.

  • 用到的库/模块:

    • sklearn:高阶算法
    • pandas:数据分析(提供了数据结构 DataFrameSeries
    • numpy:数组、矩阵处理(提供了多维数组对象 ndarray
  • 名词解释:

    • user - item/ user - base:对比用户间喜好的相似性。根据与目标用户喜好相似的用户,向目标用户推荐与其最相似用户的喜好物品。
    • item - item/ item - base:综合不同用户对不同物品的评分,向目标用户推荐与其喜好相似的物品。
    • 相似度计算:
      • 欧式距离法(euclidean 系数法)
      • p系数法(pearson 系数法)
    • pandas DataFrame:一种方便的二维数据矩阵结构,pandas 中对其内置了很多方便的方法。详见:pandas DataFrame
  • 大致步骤(我也不是很清楚):

    1. 定要素——确定参与偏好相似评价的各个要素;
    2. 读数据——用 pandas.read_csv(), 将用户评分数据读取到 DataFrame 对象中;
    3. 数据处理——将 DataFrame 数据集转化为矩阵;
    4. 计算相似度——确定相似度计算方法,求出两两主体之间的相似度,并存储为相似度矩阵;
      • 以欧式距离法为例:用 sklearn.…….pairwise_distances() 计算两向量间距离;之后将距离转化为相似度,分别创建用户相似度矩阵 & 电影相似度矩阵
    5. 加权度——基于相似度和评价度量,为每个主体未评价过的对象计算推荐指数;
      • 若采用 item - item 模式,则“剩余用户 sim * 各电影评分 = 各电影最终权重”
    6. 输出最终预测结果。
  • 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    
    import os
    import numpy
    import pandas
    from sklearn import model_selection
    from sklearn.metrics.pairwise import pairwise_distances
      
      
    # 欧氏距离转化为相似度
      
    def dis_to_sim(rating_matrix):
        for x in range(len(rating_matrix)):
            for y in range(len(rating_matrix[x])):
                rating_matrix[x][y] = 1 / (1 + rating_matrix[x][y])
        return rating_matrix
      
    # 两种预测模式
      
    def predict(user_id, ratings, similarity, type='user'):
        pred = 0
      
        if type == 'item':
              
            weight_matrix = numpy.zeros_like(ratings)
              
            # 剩余用户 sim * 各电影评分 = 各电影权重
            for r in range(len(ratings)):
                for s in range(len(similarity[user_id-1])):
                    weight_matrix[r][s] = ratings[r][s] * similarity[user_id-1][s]
                      
            # 对同一电影的所有用户权重:求和、取平均权重,即为该电影最终权重
            # 计算沿指定轴 axis 的算术平均值,返回含指定轴平均值的 numpy 数组: axis=0为沿行,axis=1为沿列
            mean_weight = weight_matrix.mean(axis=1)
              
              
            # 排除目标用户看过的(即用户评过分的)电影
            movie_No = numpy.argmax(mean_weight)
            for i in range(len(mean_weight)):
                if ratings[user_id-1][movie_No] != 0:
                    break
                numpy.delete(mean_weight, movie_No)
                movie_No = numpy.argmax(mean_weight)
                  
            # 返回权重最高的电影の序号(即 numpy 数组下标加一)
            pred = movie_No + 1
      
        elif type == 'user':
              
            # 对用户相似度矩阵排序后,取相似度最大的人(除开自己!于是使用切片 & 拼接)
            # 拼接 numpy 数组:https://stackoverflow.com/questions/9236926/concatenating-two-one-dimensional-numpy-arrays
            similar_user_array = numpy.concatenate((similarity[user_id-1][:user_id-2], similarity[user_id-1][user_id:]), axis=0)
            similar_user = numpy.argmax(similar_user_array)
              
              
            # 推荐相似的人看过 and 目标分析用户没看过的电影序号
            movie_No = numpy.argmax(ratings[similar_user])
            for i in range(len(ratings[similar_user])):
                if ratings[similar_user][movie_No] != 0 and ratings[user_id-1][movie_No] == 0:
                    break
                numpy.delete(ratings[similar_user], movie_No)
                movie_No = numpy.argmax(ratings[similar_user])
                  
            # 返回权重最高的电影の序号(即 numpy 数组下标加一)
            pred = movie_No + 1
              
              
        return pred
      
      
    # 输入用户 id 
      
    user_id = int(input('输入用户id:'))
      
    # 生成训练、评估数据集
      
    file_path = os.path.join('ml-100k', 'u.data')
    # 自定义列名
    headers = ['user_id', 'movie_id', 'rating', 'timestamp']
    # Read a comma-separated values (csv) file into DataFrame.
    df = pandas.read_csv(filepath_or_buffer=file_path, 
                         sep='\t', names=headers)
      
    # 统计用户 & 电影总数,创建 user-item 矩阵时使用
      
    # 若数据集中包含 N/A 则使用 .nunique() 较佳
    n_users = df.user_id.unique().shape[0]
    n_movies = df.movie_id.unique().shape[0]
      
    # 将 df 数据集转化为 user-item 矩阵
      
    rating_matrix = numpy.zeros((n_users, n_movies))
    # Iterate over DataFrame rows as namedtuples.
    for line in df.itertuples():
        rating_matrix[line[1]-1, line[2]-1] = line[3]
          
      
    # 计算相似度,构造相似度矩阵:采用欧式距离
      
    # 此方法采用向量数组或距离矩阵,并返回距离矩阵。
    user_distance = pairwise_distances(rating_matrix, metric='euclidean')
    # 在 NumPy 中,矩阵对象有一个 .T 属性,用于返回矩阵的转置。
    movie_distance = pairwise_distances(rating_matrix.T, metric='euclidean')
    # 距离转换为相似度:加一取倒
    user_sim = dis_to_sim(user_distance)
    movie_sim = dis_to_sim(movie_distance)
      
      
    # 预测
      
    # 根据(用户)对电影的评分推荐相似电影
    item_prediction = predict(user_id, rating_matrix, movie_sim, type='item')
    # 根据相似用户推荐电影
    user_prediction = predict(user_id, rating_matrix, user_sim, type='user')
      
    # 将电影序号和电影名对应为字典;使用.split('|')分割数据
      
    movie_dict = {}
    # 编码问题:https://stackoverflow.com/questions/19699367/for-line-in-results-in-unicodedecodeerror-utf-8-codec-cant-decode-byte
    with open(os.path.join('ml-100k', 'u.item'), 'r', encoding='ISO-8859-1') as info_f:
        info_list = info_f.readlines()
        for str in info_list[:]:
            fields = str.split('|')
            movie_id = int(fields[0])
            movie_title = (fields[1])
            # 添加字典元素
            movie_dict[movie_id] = movie_title
      
    # 输出推荐结果
    print('为你推荐: ')
    print('猜你喜欢电影:《{}》'.format(movie_dict[item_prediction]))
    print('和你类似的用户也喜欢电影:《{}》'.format(movie_dict[user_prediction]))
      
    

三、b站视频信息爬取

  • b站 api 端口:

  • 参考文章:

    B站视频信息爬取教程 - 知乎
    哔哩哔哩分区视频详细信息爬取(三连、播放量、标签)等_怎么根据bv号获得b站视频分类-CSDN博客
    Python爬虫实战:爬取B站Top100视频,分析弹幕、播放量和分类并数据可视化 - 雨月空间站
    【教程】如何获取B站用户Cookie | Jimmy’s TechBlog
    【Python】大数据挖掘课程作业1——使用爬虫爬取B站评论、弹幕与UP主的投稿视频列表_数据挖掘_RM -RF /星-开放原子开发者工作坊

  • 需求:

    这次的需求比较简单,只是传入一个视频 url ,返回 json 形式的视频信息数据即可。

  • 用到的库/模块:

    • json:json 格式转换
    • re:提供操作正则表达式的方法,用于筛选字符串元素
    • requests:发送 HTTP 请求、处理 HTTP 响应等
  • 名词解释:

    • JSON 数据格式:类似 Python 字典,包含可嵌套的键值对;引用值的方式也和字典类似。同时易于机器 & 人类理解而被广泛使用。(补充知识:JSON5 是一种基于 JSON 的扩展格式,支持注释和其他一些增强功能。)

      • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      data = {
        "name": "Alice",
        "age": 25,
        "isStudent": true,
        "courses": [
          "Mathematics",
          "Computer Science"
        ],
        "address": {
          "street": "123 Main St",
          "city": "Wonderland",
          "zip": "12345"
        }
      }
          
      // 引用键值举例——注意 str 格式
      data['name'] = 'Alice'
      data['courses'] = ['Mathematics', 'Computer Science']
      
    • 正则表达式:用于字符串匹配和提取

      • re 库常用函数:Python 正则表达式:http://www.runoob.com/python/python-reg-expressions.html
      • 常见正则式规则:

      字符匹配

      • .:匹配除换行符外的任何单个字符。
        • 例子:a.b 可以匹配 aab, acb, a1b 等。
      • [ ]:字符集,匹配方括号中的任意一个字符。
        • 例子:[abc] 可以匹配 a, b, c
      • [^ ]:否定字符集,匹配不在方括号中的任意字符。
        • 例子:[^abc] 可以匹配 d, e, 1 等。
      • \*:匹配前面的字符零次或多次。
        • 例子:a* 可以匹配 ` , a, aa, aaa` 等。
      • +:匹配前面的字符一次或多次。
        • 例子:a+ 可以匹配a, aa, aaa 等。
      • ?:匹配前面的字符零次或一次。
        • 例子:a? 可以匹配 ` , a`。
      • {n}:匹配前面的字符恰好n次。
        • 例子:a{3} 可以匹配 aaa
      • {n,}:匹配前面的字符至少n次。
        • 例子:a{2,} 可以匹配 aa, aaa, aaaa 等。
      • {n,m}:匹配前面的字符至少n次,至多m次。
        • 例子:a{2,4} 可以匹配 aa, aaa, aaaa

      特殊字符

      • \d:匹配任何数字字符,相当于 [0-9]
        • 例子:\d 可以匹配 0, 1, 2 等。
      • \D:匹配任何非数字字符,相当于 [^0-9]
        • 例子:\D 可以匹配 a, b, ! 等。
      • \w:匹配任何字母、数字或下划线字符,相当于 [a-zA-Z0-9_]
        • 例子:\w 可以匹配 a, 1, _ 等。
      • \W:匹配任何非字母、数字或下划线字符,相当于 [^a-zA-Z0-9_]
        • 例子:\W 可以匹配 !, @, # 等。
      • \s:匹配任何空白字符(空格、制表符、换行符等)。
        • 例子:\s 可以匹配空格、制表符、换行符等。
      • \S:匹配任何非空白字符。
        • 例子:\S 可以匹配 a, 1, ! 等。

      边界匹配

      • ^:匹配字符串的开始。
        • 例子:^abc 可以匹配以 abc 开头的字符串。
      • $:匹配字符串的结束。
        • 例子:abc$ 可以匹配以 abc 结尾的字符串。
      • \b:匹配单词边界。
        • 例子:\bword\b 可以匹配 word,但不匹配 wordy
      • \B:匹配非单词边界。
        • 例子:\Bword\B 可以匹配 awordb,但不匹配 word

      逻辑运算

      • |:逻辑或,匹配左边或右边的表达式。
        • 例子:a|b 可以匹配 ab
      • ():分组,组合多个字符作为一个整体进行匹配。
        • 例子:(abc) 可以匹配 abc
      • (?:...):非捕获组,不捕获匹配的文本,仅用于匹配。
        • 例子:(?:abc) 匹配 abc 但不捕获。
    • Cookie:类似浏览器页面对用户信息的缓存,让网页能够更方便地向用户提供个性化服务 。

    • 防盗链 Referer:用于告诉目标服务器,请求是从特定页面发起的;如果 Referer 头指示请求不是来自允许的域名,请求可能会被服务器拒绝。

  • 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import json
import re 
import requests

# 如何获取自己的b站 cookie 见上面教程
user_cookie = "b站用户 cookie"
# b站 api 可能会更新变化,记得及时去上面的 github 库中校对最新 api 端口
info_api = f"https://api.bilibili.com/x/web-interface/view?bvid="

# 传入单个视频 url,返回视频基本信息
def get_biliinfo(bili_url):
    # 用正则式从 url 中提取视频 BV 号
    pattern = re.compile(r'/video/([a-zA-Z0-9]+)')
    # 存储格式为列表
    # 后面用循环主要是为了提取出列表中的字符串 bvid 元素。
    bvids = pattern.findall(bili_url)

    # 爬取视频信息(目前端口返回的数据中同时包含了视频基本信息、三连数、历史最高排名等)
    header = {
        # 伪装浏览器客户端(这里伪装的是 Edge 浏览器)
        # 后面的数据:浏览器版本号
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.110 Safari/537.36 Edg/114.0.1823.79", 
        # 防盗链:
        'Referer': "https://www.bilibili.com/", 
        'Cookie': user_cookie
    }
    for bvid in bvids:
        try:
            response = requests.get(url=info_api+bvid, headers=header)
            # 检查服务器是否接受请求,200则请求成功
            print(response.status_code)
            #返回json格式的视频信息数据
            info_json = response.json()

        except:
            # 若上面的步骤没执行成功,则进入 except 版块
            print('something goes wrong...')
	
    # 这里因为需求比较简单,没有对视频数据进行进一步的筛选和分类
    # 返回视频信息数据
    return info_json

四、Python游戏版块

纯萌新去B站入门最佳(教程零门槛)!
日后经验 & 内容丰富了可能会单独分离出来。

pgzero (pygame zero)

pygame

基础:

  • 图片绘制—— surface 对象、Rect 对象

  • 文字显示:

    在 pygame 窗口上显示文本有 7 个基本步骤:

    • 使用pygame的display.set_mode()方法创建一个显示表面对象。
    • 使用 pygame 的 font.Font() 方法创建一个 Font 对象。
    • 使用 pygame 字体对象的 render() 方法创建一个文本表面对象 iesurface 对象,在其中绘制文本。
    • 使用 pygame 文本表面对象的 get_rect() 方法为文本表面对象创建一个矩形对象。
    • 通过设置 pygame 矩形对象的 center 属性的值来设置矩形对象的位置。
    • 使用 pygame 显示表面对象的 blit() 方法将文本表面对象复制到显示表面对象。
    • 使用 pygame 的 display.update() 方法在 pygame 窗口上显示显示表面对象。

    用法示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    # 显示文字の字体
    info_font = pygame.font.SysFont('arial', 30)
    # draw text on a new Surface
    text = info_font.render('Choose your Servant', True, WHITE)
    rec_text = text.get_rect(); rec_text.center = (400, 100)
    screen.blit(text, rec_text)
      
    # 显示变量的话,在更新变量后要记得更新文本
    self.score_text = info_font.render('score:' + str(self.hero.score), True, WHITE)
    

    What fonts can I use with pygame.font.Font? - Stack Overflow

    Python | 在 PyGame 窗口中显示文本 - GeeksforGeeks

  • 键盘输入—— pygame.event.get()

  • 碰撞检测:Rect 对象

    python - How do I detect collision in pygame? - Stack Overflow

  • 时间延迟:设置两个变量,使用时间差

    • 获得时间:Clock对象.get_time()

      用法示例:

      1
      2
      3
      4
      5
      6
      7
      
      clock = pygame.time.Clock()  
      # 老问题:边界用大于等于,而非直接等于  
      if self.spawn_timer >= 50000:  
          self.new_enemy()  
          # 计数器置0  
          self.spawn_timer = 0   
      self.spawn_timer += clock.get_time()  
      
  • 背景音乐 & 音效

    • 背景音乐类:pygame.mixer.music
    • 音效类:pygame.mixer.Sound

进阶(伪)——类封装:

进阶:

使用 .json 文件存取游戏中数据
实现 RPG 游戏中对话文本打字显示的效果

How to animate scrolling text in Python! (Pygame typewriter style text animation)

1
2
3
screen.blit(pygame.image.load(food_images[random.randrange(0, len(food_images)-1)]).convert_alpha(), \
                (700, 380, 100, 100))
pygame.display.flip()

pyxel

本文由作者按照 CC BY 4.0 进行授权