这些用 Python 写的牛逼程序/脚本,你玩过吗?
有网友在 quora 上提问,「你用 python 写过最牛逼的程序/脚本是什么?」。本文摘编了 3 个国外程序员的多个小项目,含代码。
manoj memana jayakumar, 3000+ 顶
更新:凭借这些脚本,我找到了工作!可看我在这个帖子中的回复,《has anyone got a job through quora? or somehow made lots of money through quora?》
1. 电影/电视剧 字幕一键下载器
我们经常会遇到这样的情景,就是打开字幕网站subscene 或者opensubtitles, 搜索电影或电视剧的名字,然后选择正确的抓取器,下载字幕文件,解压,剪切并粘贴到电影所在的文件夹,并且需把字幕文件重命名以匹配电影文件的名字。是不是觉得太无趣呢?对了,我之前写了一个脚本,用来下载正确的电影或电视剧字幕文件,并且存储到与电影文件所在位置。所有的操作步骤仅需一键就可以完成。懵逼了吗?
请看这个 youtube 视频:https://youtu.be/q5yweqgw9x8
源代码存放在github: subtitle-downloader
更新:目前,该脚本支持多个字幕文件同时下载。步骤:按住 ctrl ,选择你想要为其下载字幕的多个文件 , 最后执行脚本即可
2. imdb 查询/电子表格生成器
我是一个电影迷,喜欢看电影。我总是会为该看哪一部电影而困惑,因为我搜集了大量的电影。所以,我应该如何做才能消除这种困惑,选择一部今晚看的电影?没错,就是imdb。我打开 http://imdb.com,输入电影的名字,看排名,阅读和评论,找出一部值得看的电影。
但是,我有太多电影了。谁会想要在搜索框输入所有的电影的名字呢?我肯定不会这样做,尤其是我相信“如果某些东西是重复性的,那么它应该是可以自动化的”。因此,我写了一个 python 脚本, 目的是为了使用 非官方的 imdb api 来获取数据。我选择一个电影文件(文件夹),点击右键,选择‘发送到’,然后 点击 imdb.cmd (顺便提一下,imdb.cmd 这个文件就是我写的 python 脚本),就是这样。
我的浏览器会打开这部电影在 imdb 网站上的准确页面。
仅仅只需点击一个按键,就可以完成如上操作。如果你不能够了解这个脚本到底有多酷,以及它可以为你节省多少时间,请看这个 youtube 视频:https://youtu.be/janncimqgyk
从现在开始,你再也不需要打开你的浏览器,等待加载imdb的页面,键入电影的名字。这个脚本会帮你完成所有的操作。跟往常一样,源代码放在了github:imdb ,并且附有操作说明。当然,由于这个脚本必须去掉文件或文件夹中的无意义的字符,比如“dvdrip, yify, brrip”等,所以在运行脚本的时候会有一定比例的错误。但是经过测试,这个脚本在我几乎所有的电影文件上都运行的很好。
2014-04-01更新:
许多人在问我是否可以写一个脚本,可以发现一个文件夹中所有电影的详细信息,因为每一次只能发现一个电影的详细信息是非常麻烦的。我已经更新了这个脚本,支持处理整个文件夹。脚本会分析这个文件夹里的所有子文件夹,从 imdb上抓取所有电影的详细信息 ,然后打开一个电子表格,根据imdb 上的排名,从高到低降序排列所有的电影。这个表格中包含了 (所有电影)在 imdb url, 年份,情节,分类,获奖信息,演员信息,以及其他的你可能在 imbb找到的信息。下面是脚本执行后,生成的表格范例:
your very own personal imdb database! what more can a movie buff ask for? ;)
source on github: imdb
你也可以有一个个人 imdb 数据库!一个电影爱好者还能够要求更多吗?:)
源代码在 github : imdb
3. theoatmeal.com 连载漫画下载器
我个人超级喜欢 matthew inman 的漫画。它们在疯狂搞笑的同时,却又发人深省。但是,我很厌烦重复点击下一个,然后才能阅读每一个漫画。另外,由于每一个漫画都由多福图片组成,所以手动下载这些漫画是非常困难的。
基于如上原因,我写了一个 python 脚本 ,用来从这个站点下载所有的漫画。这个脚本利用 beautifulsoup (http://www.crummy.com/software/b… ) 解析 html 数据, 所以在运行脚本前,必须安装 beautifulsoup。用于下载燕麦片(马修.英曼的一部漫画作品)的下载器已经上传到github:theoatmeal.com-downloader 。(漫画)下载完后的文件夹是这样的 :d
4. someecards.com 下载器
成功地从http://www.theoatmeal.com 下载了整部漫画后,我在想是否我可以做同样的事情 , 从另一个我喜欢的站点— 搞笑的,唯一的 http://www.someecards.com . 下载一些东西呢?
somececards 的问题是,图片命名是完全随机的,所有图片的排放没有特定的顺序,并且一共有52 个大的类别, 每一个类别都有数以千计的图片。
我知道,如果我的脚本是多线程的话,那将是非常完美的,因为有大量的数据需要解析和下载,因此我给每一个类别中的每一页都分配一个线程。这个脚本会从网站的每一个单独的分类下载搞笑的电子贺卡,并且把每一个放到单独的文件夹。现在,我拥有这个星球上最好笑的电子贺卡私人收藏。下载完成后,我的文件夹是这样的:
没错,我的私人收藏总共包括:52个类别,5036个电子贺卡。源代码在这里: someecards.com-downloader
编辑:很多人问我是否可以共享我下载的所有文件,(在这里,我要说)由于我的网络不太稳定,我没办法把我的收藏上传到网络硬盘,但是我已经上传一个种子文件,你们可以在这里下载:somecards.com site rip torrent
种下种子,传播爱:)
akshit khurana,4400+ 顶
感谢 500 多个朋友在 facebook 上为我送出的生日祝福
有三个故事让我的21岁生日变的难忘,这是最后一个故事。我倾向于在每一条祝福下亲自评论,但是使用 python 来做更好。
1… 2 31. # thanking everyone who wished me on my birthday 4 52. import requests 6 73. import json 8 94. 10 115. # aman s post time 12 136. after = 1353233754 14 157. token = 16 178. 18 199. def get_posts(): 20 2110. """returns dictionary of id, first names of people who posted on my wall 22 2311. between start and end time""" 24 2512. query = ("select post_id, actor_id, message from stream where " 26 2713. "filter_key = others and source_id = me() and " 28 2914. "created_time > 1353233754 limit 200") 30 3115. 32 3316. payload = { q : query, access_token : token} 34 3517. r = requests.get( https://graph.facebook.com/fql , params=payload) 36 3718. result = json.loads(r.text) 38 3919. return result[ data ] 40 4120. 42 4321. def commentall(wallposts): 44 4522. """comments thank you on all posts""" 46 4723. #todo convert to batch request later 48 4924. for wallpost in wallposts: 50 5125. 52 5326. r = requests.get( https://graph.facebook.com/%s % 54 5527. wallpost[ actor_id ]) 56 5728. url = https://graph.facebook.com/%s/comments % wallpost[ post_id ] 58 5929. user = json.loads(r.text) 60 6130. message = thanks %s :) % user[ first_name ] 62 6331. payload = { access_token : token, message : message} 64 6532. s = requests.post(url, data=payload) 66 6733. 68 6934. print "wall post %s done" % wallpost[ post_id ] 70 7135. 72 7336. if __name__ == __main__ : 74 7537. commentall(get_posts()) 76 77…
为了能够顺利运行脚本,你需要从graph api explorer(需适当权限)获得 token。本脚本假设特定时间戳之后的所有帖子都是生日祝福。
尽管对评论功能做了一点改变,我仍然喜欢每一个帖子。
当我的点赞数,评论数以及评论结构在 ticker(facebook一项功能,朋友可以看到另一个朋友在做什么,比如点赞,听歌,看电影等) 中爆涨后,我的一个朋友很快发现此事必有蹊跷。
尽管这个不是我最满意的脚本,但是它简单,快捷,有趣。
当我和 sandesh agrawal 在网络实验室讨论时,有了写这个脚本的想法。为此,sandesh agrawal 耽搁了实验室作业,深表感谢。
tanmay kulshrestha,3300+ 顶
好了,在我失去这个项目之前(一个猪一样的朋友格式化了我的硬盘,我的所有代码都在那个硬盘上)或者说,在我忘记这些代码之前,我决定来回答这个问题。
整理照片
当我对图像处理感兴趣之后,我一直致力于研究机器学习。我写这个有趣的脚本,目的是为了分类图片,很像 facebook 做的那样(当然这是一个不够精确的算法)。我使用了 opencv 的人脸检测算法,“haarcascade_frontalface_default.xml”,它可以从一张照片中检测到人脸。
你可能已经察觉到这张照片的某些地方被错误地识别为人脸。我试图通过修改一些参数(来修正这一问题),但还是某些地方被错误地识别为人脸,这是由相机的相对距离导致的。我会在下一阶段解决这一问题(训练步骤)。
这个训练算法需要一些训练素材,每个人需要至少需要100-120个训练素材(当然多多益善)。我太懒了,并没有为每一个人挑选照片,并把它们复制粘帖到训练文件夹。所以,你可能已经猜到,这个脚本会打开一个图片,识别人脸,并显示每一个人脸(脚本会根据处于当前节点的训练素材给每一个人脸预测一个名字)。伴随着每次你标记的照片,recognizer 会被更新,并且还会包含上一次的训练素材。在训练过程中,你可以增加新的名字。我使用 python 库 tkinter 做了一个 gui。因此,大多数时候,你必须初始化一小部分照片(给照片中的人脸命名),其他的工作都可以交给训练算法。因此,我训练了 recognizer ,然后让它(recognizer)去处理所有的图片。
我使用图片中包含的人的人名来命名图片,(例如:tanmay&*****&*****)。因此,我可以遍历整个文件夹,然后可以通过输入人名的方法来搜索图片。
初始状态下,当一个人脸还没有训练素材时(素材库中还没有包括这个人脸的名字),需要询问他/她的名字。
我可以增加一个名字,像这个样子:
当训练了几个素材后,它会像这个样子:
最后一个是针对应对那些垃圾随机方块而使用的变通解决方案。
带名字的最终文件夹。
所以,现在寻找图片变得相当简单。顺便提一下,很抱歉(我)放大了这些照片。
1import cv2 2 3import sys 4 5import os,random,string 6 7#choices=[ add a name ] 8 9import os 10 11current_directory=os.path.dirname(os.path.abspath(__file__)) 12 13from tkinter import tk 14 15from easygui import * 16 17import numpy as np 18 19x= os.listdir(current_directory) 20 21new_x=[] 22 23testing=[] 24 25for i in x: 26 27if i.find( . )==-1: 28 29new_x+=[i] 30 31else: 32 33testing+=[i] 34 35x=new_x 36 37g=x 38 39choices=[ add a name ]+x 40 41y= range(1,len(x)+1) 42 43def get_images_and_labels(): 44 45global current_directory,x,y,g 46 47if x==[]: 48 49return (false,false) 50 51image_paths=[] 52 53for i in g: 54 55path=current_directory+ +i 56 57for filename in os.listdir(path): 58 59final_path=path+ +filename 60 61image_paths+=[final_path] 62 63# images will contains face images 64 65images = [] 66 67# labels will contains the label that is assigned to the image 68 69labels = [] 70 71for image_path in image_paths: 72 73# read the image and convert to grayscale 74 75img = cv2.imread(image_path,0) 76 77# convert the image format into numpy array 78 79image = np.array(img, uint8 ) 80 81# get the label of the image 82 83backslash=image_path.rindex( ) 84 85underscore=image_path.index( _ ,backslash) 86 87nbr = image_path[backslash+1:underscore] 88 89t=g.index(nbr) 90 91nbr=y[t] 92 93# if face is detected, append the face to images and the label to labels 94 95images.append(image) 96 97labels.append(nbr) 98 99#cv2.imshow("adding faces to traning set...", image) 100 101#cv2.waitkey(50) 102 103# return the images list and labels list 104 105return images, labels 106 107# perform the tranining 108 109def train_recognizer(): 110 111recognizer = cv2.createlbphfacerecognizer() 112 113images, labels = get_images_and_labels() 114 115if images==false: 116 117return false 118 119cv2.destroyallwindows() 120 121recognizer.train(images, np.array(labels)) 122 123return recognizer 124 125def get_name(image_path,recognizer): 126 127global x,choices 128 129#if recognizer== : 130 131# recognizer=train_recognizer() 132 133cascadepath = "haarcascade_frontalface_default.xml" 134 135facecascade = cv2.cascadeclassifier(cascadepath) 136 137#recognizer=train_recognizer() 138 139x1=testing 140 141global g 142 143print image_path 144 145image = cv2.imread(image_path) 146 147img = cv2.cvtcolor(image, cv2.color_bgr2gray) 148 149predict_image = np.array(img, uint8 ) 150 151faces = facecascade.detectmultiscale( 152 153img, 154 155scalefactor=1.3, 156 157minneighbors=5, 158 159minsize=(30, 30), 160 161flags = http://cv2.cv.cv_haar_scale_image 162 163) 164 165for (x, y, w, h) in faces: 166 167f= image[y:y+w,x:x+h] 168 169cv2.imwrite( temp.jpg ,f) 170 171im= temp.jpg 172 173nbr_predicted, conf = recognizer.predict(predict_image[y: y + h, x: x + w]) 174 175predicted_name=g[nbr_predicted-1] 176 177print "{} is correctly recognized with confidence {}".format(predicted_name, conf) 178 179if conf>=140: 180 181continue 182 183msg= is this +predicted_name 184 185reply = buttonbox(msg, image=im, choices=[ yes , no ]) 186 187if reply== yes : 188 189reply=predicted_name 190 191directory=current_directory+ +reply 192 193if not os.path.exists(directory): 194 195os.makedirs(directory) 196 197random_name= .join(random.choice(string.ascii_uppercase + string.digits) for _ in range(7)) 198 199path=directory+ +random_name+ .jpg 200 201cv2.imwrite(path,f) 202 203else: 204 205msg = "who is this?" 206 207reply = buttonbox(msg, image=im, choices=choices) 208 209if reply == add a name : 210 211name=enterbox(msg= enter the name , title= training , strip=true) 212 213print name 214 215choices+=[name] 216 217reply=name 218 219directory=current_directory+ +reply 220 221if not os.path.exists(directory): 222 223os.makedirs(directory) 224 225random_name= .join(random.choice(string.ascii_uppercase + string.digits) for _ in range(7)) 226 227path=directory+ +random_name+ .jpg 228 229print path 230 231cv2.imwrite(path,f) 232 233 234 235# calculate window position 236 237root = tk() 238 239pos = int(root.winfo_screenwidth() * 0.5), int(root.winfo_screenheight() * 0.2) 240 241root.withdraw() 242 243windowposition = "+%d+%d" % pos 244 245 246 247# patch rootwindowposition 248 249rootwindowposition = windowposition 250 251def detect_faces(img): 252 253global choices,current_directory 254 255imagepath = img 256 257facecascade = cv2.cascadeclassifier(cascpath) 258 259image = cv2.imread(imagepath) 260 261gray = cv2.cvtcolor(image, cv2.color_bgr2gray) 262 263faces = facecascade.detectmultiscale( 264 265gray, 266 267scalefactor=1.3, 268 269minneighbors=5, 270 271minsize=(30, 30), 272 273flags = http://cv2.cv.cv_haar_scale_image 274 275) 276 277 278 279print "found {0} faces!".format(len(faces)) 280 281m=0 282 283for (x, y, w, h) in faces: 284 285m+=1 286 287padding=0 288 289f= image[y-padding:y+w+padding,x-padding:x+h+padding] 290 291cv2.imwrite( temp.jpg ,f) 292 293im= temp.jpg 294 295msg = "who is this?" 296 297reply = buttonbox(msg, image=im, choices=choices) 298 299if reply == add a name : 300 301name=enterbox(msg= enter the name , title= training , strip=true) 302 303print name 304 305choices+=[name] 306 307reply=name 308 309directory=current_directory+ +reply 310 311if not os.path.exists(directory): 312 313os.makedirs(directory) 314 315random_name= .join(random.choice(string.ascii_uppercase + string.digits) for _ in range(7)) 316 317path=directory+ +random_name+ .jpg 318 319print path 320 321cv2.imwrite(path,f) 322 323def new(img,recognizer): 324 325imagepath = current_directory+ +img 326 327print imagepath 328 329get_name(imagepath,recognizer) 330 331cascpath = haarcascade_frontalface_default.xml 332 333b=0 334 335os.system("change_name.py") 336 337for filename in os.listdir("."): 338 339b+=1 340 341if b%10==0 or b==1: 342 343os.system("change_name.py") 344 345recognizer=train_recognizer() 346 347if filename.endswith( .jpg ) or filename.endswith( .png ): 348 349print filename 350 351imagepath=filename 352 353#detect_faces(imagepath) 354 355new(imagepath,recognizer) 356 357os.remove(filename) 358 359raw_input( done with this photograph )
我想进一步修改它的搜索功能,其中会包含更多的搜索类型,比如基于地理位置,微笑的脸,伤心的脸等等。(这样我就可以在 skylawns 上 搜索快乐的 tanmay & 沮丧的 akshay & 快乐的…)
我还写了很多脚本,但那都是很久之前的事情了,我也懒得再去检查这些代码了,我会列出部分代码。
github 链接: tanmay2893/image-sorting
gmail 邮件通知
在那段时间,我没有智能手机。导致我常常错过来自于我所在的研究所的邮件(在我的研究所的邮件 id),我写了一个脚本,可以在我的笔记本上运行,而且能给我的手机发信息。我使用 python 的 imap 库来获取邮件。我可以输入一些重要的人的名字,这样一来,当这些人给我发了邮件后,我可以收到短信通知。对于短信, 我使用了 way2sms.com(写了一个 python 脚本,自动登陆我的账户,然后发送 短信)。
pnr (passenger name record旅客订座记录,下同) 状态短讯
铁路方面不经常发送 pnr 状态消息。因此,我写了一个脚本,可以从印度铁路网站获取 pnr 状态。这是非常容易的,因为那个网站没有验证码,即使有,也只是形同虚设的验证码(在过去,一些字母会被写在看起来像图片一样的东西上面,因为他们为这些字母使用了一个 “check” 的背景图)。我们可以轻松地从 html 网页得到这些字母。我不明白他们这样做的目的是什么,难道仅仅是为了愚弄他们自己吗?不管怎么样,我使用短信息脚本来处理它,经过一段时间间隔,它会在我的笔记本上运行一次,就像是一个定时任务,只要 pnr 状态有更新,它就会把更新信息发送给我。
youtube 视频下载器
这个脚本会从 youtube 页面下载所有的 youtube 视频 以及他们所有的字幕文件(从 download and save subtitles 下载)。为了使下载速度更快一点,我使用了多线程。还有一个功能是,即使你的电脑重启了,仍然可以暂停和恢复播放下载的(视频)。我原本想做一个ui的,但是我太懒了… 一旦我的下载任务完成,我就不去关心 ui 的事情了。
板球比分通知器
我猜想这个功能已经在别的地方提到过了。一个窗口通知器。(在右下角的通知区域,它会告诉你实时比分以及评论信息)。如果你愿意的化,在某些时间段,你也可以关掉它。
whatsapp 消息
这个并不太实用,我只是写着玩玩。因为 whatsapp 有网页版,我使用 selenium 和 python 下载我的所有联系人的显示图片,并且,一旦有人更新了他们的显示图片,我将会知道。(如何做到的?非常简单,在设定好时间间隔后,我会一遍又一遍的不停下载所有的头像信息,一旦照片的尺寸发生变化,我将会知道他/她更新了显示图片)。然后我会给他/她发一个信息,不错的头像。我仅仅使用了一次来测试它的可用性。
nalanda 下载器
我们一般在这个叫 ‘nalanda’ 的网站上下载一些教学课件以及其他的课程资料, ‘nalanda’ 在 bits pilani ( nalanda ). 我自己懒得在考试前一天下载所有的课件,所以,我写了这个这个下载器,它可以把每一门科的课件下载到相应的文件夹。
代码:
1import mechanize,os,urllib2,urllib,requests,getpass,time 2 3start_time = time.time() 4 5from bs4 import beautifulsoup 6 7br=mechanize.browser() 8 9br.open( https://nalanda.bits-pilani.ac.in/login/index.php ) 10 11br.select_form(nr=0) 12 13 14 15name= 16 17while name== : 18 19 try: 20 21 print ******* 22 23 username=raw_input( enter your nalanda username: ) 24 25 password=getpass.getpass( password: ) 26 27 br.form[ username ]=username 28 29 br.form[ password ]=password 30 31 res=br.submit() 32 33 response=res.read() 34 35 soup=beautifulsoup(response) 36 37 name=str(soup.find( div ,attrs={ class : logininfo }).a.string)[:-2] 38 39 except: 40 41 print wrong password 42 43f=open( details.txt , w ) 44 45f.write(username+ n +password) 46 47f.close() 48 49print welcome, +name 50 51print all the files will be downloaded in your drive c in a folder named "nalanda" 52 53#print soup.prettify() 54 55div=soup.find_all( div ,attrs={ class : box coursebox }) 56 57 58l=len(div) 59 60a=[] 61 62for i in range(l): 63 64 d=div[i] 65 66 s=str(d.div.h2.a.string) 67 68 s=s[:s.find( ( )] 69 70 c=(s,str(d.div.h2.a[ href ])) 71 72 path= c:nalanda +c[0] 73 74 if not os.path.exists(path): 75 76 os.makedirs(path) 77 78 a+=[c] 79 80#print a 81 82overall=[] 83 84for i in range(l): 85 86 response=br.open(a[i][1]) 87 88 page=response.read() 89 90 soup=beautifulsoup(page) 91 92 li=soup.find_all( li ,attrs={ class : section main clearfix }) 93 94 x=len(li) 95 96 t=[] 97 98 folder=a[i][0] 99 100 print downloading +folder+ files... 101 102 o=[] 103 104 for j in range(x): 105 106 g=li[j].ul 107 108 #print g 109 110 #raw_input( ) 111 112 if g!=none: 113 114 temp=http://g.li[ class ].split( ) 115 116 #raw_input( ) 117 118 if temp[1]== resource : 119 120 #print yes 121 122 #print ******************** 123 124 o+=[j] 125 126 h=li[j].find( div ,attrs={ class : content }) 127 128 s=str(h.h3.string) 129 130 path= c:nalanda +folder 131 132 if path[-1]== : 133 134 path=path[:-1] 135 136 path+= +s 137 138 if not os.path.exists(path): 139 140 os.makedirs(path) 141 142 f=g.find_all( li ) 143 144 r=len(f) 145 146 z=[] 147 148 for e in range(r): 149 150 p=f[e].div.div.a 151 152 q=f[e].find( span ,attrs={ class : resourcelinkdetails }).contents 153 154 link=str(p[ href ]) 155 156 text=str(p.find( span ).contents[0]) 157 158 typ= 159 160 if str(q[0]).find( word )!=-1: 161 162 typ= .docx 163 164 elif str(q[0]).find( jpeg )!=-1: 165 166 typ= .jpg 167 168 else: 169 170 typ= .pdf 171 172 if typ!= .docx : 173 174 res=br.open(link) 175 176 soup=beautifulsoup(res.read()) 177 178 if typ== .jpg : 179 180 di=soup.find( div ,attrs={ class : resourcecontent resourceimg }) 181 182 link=di.img[ src ] 183 184 else: 185 186 di=soup.find( div ,attrs={ class : resourcecontent resourcepdf }) 187 188 link=di.object[ data ] 189 190 try: 191 192 if not os.path.exists(path+ +text+typ): 193 194 br.retrieve(link,path+ +text+typ)[0] 195 196 except: 197 198 print connectivity issues 199 200 z+=[(link,text,typ)] 201 202 t+=[(s,z)] 203 204 if t==[]: 205 206 print no documents in this subject 207 208 overall+=[o] 209 210 #raw_input( press any button to resume ) 211 212#print overall 213 214print time taken to download: +str(time.time()-start_time)+ seconds 215 216print do you think you can download all files faster than this :p 217 218print closing in 10 seconds 219 220time.sleep(10)
我自己的 dc++
这个脚本并不是很有用,目前只有一些学生在用它, 况且,dc ++ 已经提供了一些很酷的功能。我原本可以优化我自己的版本,但是,由于我们已经有了dc ++,我并没有这么做,尽管我已经使用 nodejs 和 python 写了一个基础版本。
工作原理:
打开 dc++ , 进入一个中心站点,然后连接,我写了一个 python 脚本来做这件事。脚本会在 pc上创建一个服务器(可以通过修改 simplehttprequesthandler 来完成)。
在服务器端(使用了nodejs),它会拿到 pc 的连接,共享给其他的用户。
这个是主页面:
这个页面显示了所有的用户和他们的链接。因为我给 nick 加了一个超链接,所以在链接这一拦是空的。
所以,当用户数量增加以后,这个页面会列出所有的用户列表。基本上,这个页面充当了一个你和另外一个人联系的中间人角色。我还做了一个在所有用户中搜索特定文件的功能。
下一篇: 如何减少 Hyperf 框架的扫描时间