明日方舟干员立绘爬虫


想要查看明日方舟全干员精二及皮肤立绘的可以直接点击该链接查看。(由于图片数目较多,该链接加载速度可能会较慢,大约30秒左右)

最初的想法

作为明日方舟的高玩,之前一直有做一个干员立绘爬虫的想法,但是却由于找不到链接的规律,任务无法进展下去。经过昨天与noionion的讨论,我们发现PRTS图片的链接为URL转码,思路顿时清晰了起来。
这里以干员“山”的立绘图片链接为例
“立绘_山_2”对应的图片链接
对“立绘_山_2”文字进行URL转码
我们可以发现,二者有惊人的一致性,也就是说:PRTS里立绘的图片链接为:http://prts.wiki/images/*/**/“立绘_干员名”对应的URL转码 + "_1/2/skin1/skin2/skin3"(1为干员精英化0/1阶的默认皮肤,2为干员精英化2阶的默认皮肤,skin1/skin2/skin3为干员的其他皮肤)
所以只要输入所有方舟干员的名称,再让程序通过URL转码得到对应的图片链接,就可以爬取到对应链接的图片
PS:在编写代码时出现了一些小问题,首先是我发现URL转码需要用到新的库urllib,然而我现有的库里不包含它,之后我便准备使用命令行conda命令进行安装(相对于pipconda命令安装的包质量有保障,版本没问题),却发现我上次装机后忘记重新安装Miniconda了,开始菜单里没有Anaconda Prompt,这就使得我无法使用conda命令,于是我又在网上查找Anaconda Prompt对应的文件位置)
后来我发现,python3.x版本已经把urllib库和urllib2库合并成了urllib库,anaconda3中集成了urllib,所以不用pip和conda命令安装,可以直接import urllib
虚惊一场
与此同时,在查找之前的方舟抽卡模拟器代码生成本代码的list时,我又发现了抽卡模拟器代码的问题:由于一直没有维护,其中random.randint()中的数值已经不符合list中的实际数值,即最大随机数已经不再是list中的最大位置,所以我又进行了一些更改。
代码原思路如下:

import urllib
import urllib.parse
import random

names = ["棘刺","铃兰","早露","温蒂","傀影","风笛",
       "刻俄柏","阿","煌","莫斯提马","麦哲伦","赫拉格",
       "黑","陈","斯卡蒂","银灰","塞雷娅","星熊","夜莺",
       "闪灵","安洁莉娜","艾雅法拉",
       "伊芙利特","推进之王","能天使","森蚺","史尔特尔","瑕光","泥岩","山",
        "安哲拉", "贾维", "蜜蜡", "断崖", "莱恩哈特",
        "月禾", "石棉", "极境", "巫恋", "慑砂",
        "惊蛰", "吽", "灰喉", "布洛卡", "苇草", "槐琥",
        "送葬人", "星极", "格劳克斯", "诗怀雅",
        "夜魔", "食铁兽", "狮蝎", "空", "真理", "初雪",
        "崖心", "守林人", "普罗旺斯", "可颂", "雷蛇", "红",
        "临光", "华法琳", "赫默", "梅尔", "天火", "陨星", "白金",
        "蓝毒", "幽灵鲨", "拉普兰德", "芙兰卡", "德克萨斯",
        "凛冬", "白面鸮", "燧石", "四月", "奥斯塔", "絮雨", "卡夫卡",
        "孑","卡达","波登可","刻刀","宴","安比尔",
        "梅","红云","桃金娘","苏苏洛","格雷伊","猎蜂",
        "阿消","地灵","深海色","古米","蛇屠箱","角峰","调香师","嘉维尔",
        "末药","暗索","砾","慕斯","霜叶","缠丸","杜宾","红豆",
        "清道夫","讯使","白雪","流星","杰西卡","远山","夜烟","酸糖",
        "芳汀","泡泡","杰克","松果",
        "斑点","泡普卡","月见夜","空爆","梓兰","史都华德",
         "安塞尔","芙蓉","炎熔","安德切尔",
         "克洛斯","米格鲁","卡缇","梅兰莎","翎羽","香草","芬"]
for index in range(len(names)): #len(names)为names列表的长度,我们从列表里逐一读取干员名称
    ganyuan_name = names[index] #ganyuan_name即为读取到的干员名称
    print(urllib.parse.quote(ganyuan_name)) #这里将干员名称统一转为URL码形式
string = "http://prts.wiki/images/*/**/" + urllib.parse.quote(ganyuan_name) #这里的string为链接的开头部分
pass #后来舍弃了这种思路,所以就没有往下编写

思路的改变

昨天晚上noionion自己做了一个明日方舟立绘的爬虫(他文章开头提到的那个朋友就是我),看完他的代码,我意识到即使不知道干员的名称也可以制作爬虫,又正好遇上元旦放假有时间,便开始了自己的爬虫程序制作。
但是他的代码依然有一定的问题:一是爬取的图片有一定的重复度(比方说有时会有“立绘_干员名_skin2.png”和“立绘_干员名_skin2b.png”两个相同的图片出现),二是用此代码爬取的皮肤图片(即链接里有”skin”字样的图片)文件名内是不包含皮肤名的,只有skin1/2/3字样。
作为一名高玩,不能坐视不管
于是我准备在他代码的基础上做一定的修改。

开始编写!

根据他博客里的提示,我点击了PRTS里相应的按钮,进入了立绘检索界面
但是由于以前只是学习过相应的慕课,几乎没有实战经验,所以这次遇到了大量问题,也查阅了很多资料
我先研究了noionion的代码
看不懂
所以我先复制过来跑了一遍代码,了解了基本的流程
noionion源码(大佬的代码果然强)
之后查阅相关资料,给代码上了详尽的注释
晚上校园网不大好,手机流量费惊人
(代码中的“注”我会用图片解释说明)

# 引入程序需要的相应包(类似C语言中的# include<stdio.h> 、include<math.h>等)
import os
from bs4 import BeautifulSoup
import time
import requests

# 设置爬取图片的网址
url = "http://prts.wiki/index.php?title=%E7%89%B9%E6%AE%8A:%E6%90%9C%E7%B4%A2&limit=500&offset=0&profile=images&search=%E7%AB%8B%E7%BB%98"


# 重定义请求头,防止被网页发现是爬虫,从而进行反爬操作
headers = {
    "Cookie": "arccount62298=c; arccount62019=c",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
}


# 爬取模块
html = requests.get(url, headers=headers) ##  这里是使用requests函数用之前重定义的请求头读取网页信息
### 测试代码
### print(html.text)
html.encoding = html.apparent_encoding ## encoding是从http中的header中的charset字段中提取的编码方式,若header中没有charset字段则默认为ISO-8859-1编码模式,无法解析中文,这是乱码的原因,apparent_encoding会从网页的内容中分析网页编码的方式,所以apparent_encoding比encoding更加准确,当网页出现乱码时可以把apparent_encoding的编码格式赋值给encoding。
soup = BeautifulSoup(html.text, "html.parser") ## "html.parser"是为了让解析速度快,容错率高,具体讲解网址为https://blog.csdn.net/yangjiajia123456/article/details/80959896
list = soup.find_all(class_ = "searchResultImage") ## 注一:对爬取页面进行“检查”操作后发现,所有的图片都包含在一个<table class="searchResultImage">的class中,所以这行代码的作用是查找html代码中标签为class且值为searchRusultImgge的所有代码块
### 测试代码
### print(list)
### 可以看到,相比于html.text,list里没有大量的网页配置信息,只有以图片名称为核心的列表


# 检查项目文件夹下是否有“Arknights”这个文件夹,没有的话新建一个,有的话就不新建了
try:
    os.mkdir("./Arknights")  ##  注二:创建文件夹(这个文件夹创建在现在的项目文件夹里,比方说我这里用的是“明日方舟干员立绘项目”项目文件夹,爬取到的图片就存在“明日方舟干员立绘项目”项目文件夹下的“Arknights”文件夹里,该函数具体介绍网址为https://blog.csdn.net/qq_20412595/article/details/82423764)
except:
    pass
# 将当前工作路径切换为“Arknights”这个文件夹
os.chdir("./Arknights") ##  切换当前工作路径(本函数的具体介绍在https://www.runoob.com/python3/python3-os-chdir.html)

# 将最初读取的图片设置为第一张图片
num=0

# 图片的爬取与本地存储
for s in list:
    string = str(s) ## 将list中的单个元素化为字符串,再针对该字符串进行处理

    namebegin = string.find('title="文件') ##  注三:显然,所有立绘的文件名开头都有“文件”二字,找到“文件”二字就相当于找到了立绘 注四:为了读取图片里包含的干员名,我们发现能够通过title入手,向后搜索名称,这里找到了"title"中"t”的位置
    nameend = string[namebegin:].find('png') ## 找到图片文件名末尾的"png"字样对应的位置

    # 我加入的测试代码
    # print(namebegin)
    # print(nameend)

    name = string[namebegin+10:namebegin+nameend+3] ## 取从”文件“到”png“之间的字段作为name参数(这里的namebegin+nameend就很灵性,其实根据上面的测试代码,nameend更有name长度的意义,所以结束位置才用的namebegin+nameend+3)
    name = name.replace(" ","_") ## 将图片文件名里的空格转为”_“

    urlbegin = string.find('data-src="/images/thumb/') ## 与上面找干员名称同理,找到链接对应的开头"data-src="/images/thumb/",进而搜索链接名称
    urlend = string[urlbegin:].find('png') ## 与上面找干员名称同理,找到链接末尾的'png',进而确定链接名称

    imgurl = 'http://prts.wiki/images/' + string[urlbegin+24:urlbegin+urlend+3] ## 注五:图片链接都以"http://prts.wiki/images/"开头,这里是为了记录图片对应的链接

    img = requests.get(imgurl, headers=headers).content
    with open(name, 'wb') as f:
        f.write(img) ## 将爬取到的图片写入“Arknights”这个文件夹里
        num+=1 ## num=num+1,使得下面print函数里的”已爬取{}张“中的{}对应数值加1
        print("已爬取{}张,图片名称为:{},链接为:{}".format(num,name,imgurl)) ## 这里{}的意义与C语言中的"%d"/"%f"十分相近,是从format()函数里读取对应位置的参数
    # "r"--以读方式打开,只能读文件,如果文件不存在,会发生异常
    # "w"--以写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件
    # "rb"--以二进制读方式打开,只能读文件,如果文件不存在,会发生异常
    # "wb"--以二进制写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件(所以这个程序就算运行多次,文件夹里也不会出现重复的立绘)
    time.sleep(1) ## 休息一秒后爬取下一张图片,这个函数在链接https://blog.csdn.net/weixin_45949073/article/details/104989562里讲得很清楚

代码中注一
代码中注二
代码中注三
代码中注四
代码中注五

一些优化与改进

程序已经能爬取全部的干员立绘了(包括我之前遗漏的游戏二测凯尔希与肉鸽模式的四位干员),但是我还希望能够更进一步进行优化:
优化目标:
1.对于网站里的部分重复立绘,希望程序能够自动选择,不读取重复立绘
2.能够只爬取某一类型的立绘(比如只爬取干员皮肤、只爬取干员精二立绘等等)
3.对于干员皮肤立绘,希望程序不是输出skin1/2/3,而是输出对应的皮肤名
4.对于对应职业的干员,希望程序能够对其自动分组,相同职业的干员能够分入同一文件夹
这些优化目标的难度是逐级上升的,所以我们逐步进行:

目标一

我们首先浏览了原代码爬取到的图片,发现了以下立绘重复现象(这里抽取五处作为例子):
立绘重复1
立绘重复2
立绘重复3
立绘重复4
立绘重复5
立绘重复6
这样就找到了对应的规律:大部分立绘重复图片都是在文件名为“正常立绘文件名+部分字样”的图片里,这里的部分字样可能是“_V1”、“_V2”(干员华法琳独有)和“b”三种,所以我们只需要截取部分文字即可。我们需要对namebegin+10位置的字符进行处理,
如果该字符是“1”或“2”,就说明该立绘为干员的默认皮肤,只需要读到本位即可;
如果该字符是“s”,就说明该立绘为干员其他皮肤,需要向后再读4位;
附:后来我发现还有一张名称为“精二立绘A”的能天使立绘漏网之鱼
所以修改代码如下:

# 引入程序需要的相应包(类似C语言中的# include<stdio.h> 、include<math.h>等)
import os
from bs4 import BeautifulSoup
import time
import requests

# 设置爬取图片的网址
url = "http://prts.wiki/index.php?title=%E7%89%B9%E6%AE%8A:%E6%90%9C%E7%B4%A2&limit=500&offset=0&profile=images&search=%E7%AB%8B%E7%BB%98"


# 重定义请求头,防止被网页发现是爬虫,从而进行反爬操作
headers = {
    "Cookie": "arccount62298=c; arccount62019=c",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
}


# 爬取模块
html = requests.get(url, headers=headers) ##  这里是使用requests函数用之前重定义的请求头读取网页信息
### 测试代码
### print(html.text)
html.encoding = html.apparent_encoding ## encoding是从http中的header中的charset字段中提取的编码方式,若header中没有charset字段则默认为ISO-8859-1编码模式,无法解析中文,这是乱码的原因,apparent_encoding会从网页的内容中分析网页编码的方式,所以apparent_encoding比encoding更加准确,当网页出现乱码时可以把apparent_encoding的编码格式赋值给encoding。
soup = BeautifulSoup(html.text, "html.parser") ## "html.parser"是为了让解析速度快,容错率高,具体讲解网址为https://blog.csdn.net/yangjiajia123456/article/details/80959896
list = soup.find_all(class_ = "searchResultImage") ## 注一:对爬取页面进行“检查”操作后发现,所有的图片都包含在一个<table class="searchResultImage">的class中,所以这行代码的作用是查找html代码中标签为class且值为searchRusultImgge的所有代码块
### 测试代码
### print(list)
### 可以看到,相比于html.text,list里没有大量的网页配置信息,只有以图片名称为核心的列表


# 检查项目文件夹下是否有“Arknights”这个文件夹,没有的话新建一个,有的话就不新建了
try:
    os.mkdir("./Arknights")  ##  注二:创建文件夹(这个文件夹创建在现在的项目文件夹里,比方说我这里用的是“明日方舟干员立绘项目”项目文件夹,爬取到的图片就存在“明日方舟干员立绘项目”项目文件夹下的“Arknights”文件夹里,该函数具体介绍网址为https://blog.csdn.net/qq_20412595/article/details/82423764)
except:
    pass
# 将当前工作路径切换为“Arknights”这个文件夹
os.chdir("./Arknights") ##  切换当前工作路径(本函数的具体介绍在https://www.runoob.com/python3/python3-os-chdir.html)

# 将最初读取的图片设置为第一张图片
num=0

# 测试用参数
# num1 = 0
# num2 = 0

# 图片的爬取与本地存储
for s in list:
    string = str(s) ## 将list中的单个元素化为字符串,再针对该字符串进行处理

    namebegin = string.find('title="文件') ##  注三:显然,所有立绘的文件名开头都有“文件”二字,找到“文件”二字就相当于找到了立绘 注四:为了读取图片里包含的干员名,我们发现能够通过title入手,向后搜索名称,这里找到了"title"中"t”的位置
    # name_point_1 = namebegin+12 ## 为了读取干员名称,我们这里找到第一个空格的位置name_point_1(这里空格刚好在namebegin后的第二格)
    # name_point_2 = string.find(' 1'or' 2'or' skin') ## 这里找到第二个空格的位置name_point_2(本方法返回值远小于namebegin的数值,是错误的)
    nameend = string[namebegin:].find('png') ## 找到图片文件名末尾的"png"字样对应的位置

    ## 测试代码
    # print(namebegin)
    # print(name_point_1)
    # print(name_point_2)
    # print(nameend)

    ## 这里如果使用name_point_2 = string.find(' 1'or' 2'or' skin'),会显示位置为54,远小于namebegin的位置值,是错误的,所以用这种方法难以找到第二个空格的位置,所以我们将"干员名 1/2/skin(b/_V).png"整体令为name1,再在name1里寻找空格位置
    name1 = string[namebegin+13:namebegin+nameend-1]
    name1begin = name1.find('"干员名"')
    name1_point_1 = name1begin
    name1_point_2 = name1.find(' 1'or' 2'or' skin') ## 根据测试代码我们有,输出结果为-1,即name1[name1begin]对应于name1的最后一个字符

    # 测试代码
    print(name1) # 输出结果格式"暗索 skin1 V1"
    # print(name1begin)
    # print(name1[name1begin])
    # print(name1_point_1)
    # print(name1_point_2)

    print(string[(namebegin+nameend-3):(namebegin+nameend-1)]) ## 本代码用来查看后两位并进行检验

    if(string[namebegin+nameend-3:namebegin+nameend-1]=='V1'): ## 若后两位为V1
        # num1+=1
        # print('num1={}'.format(num1))
        name1 = name1[:-3] ## 除去后三位"空格+V1"
        name1 = name1 + '.png' ## 取name1为文件名,再将'png'加在文件名后
    elif(string[namebegin+nameend-2:namebegin+nameend-1]=='b'): ## 若最后一位为b
        # num2+=1
        # print('num2={}'.format(num2))
        name1 = name1[:-1]  ## 除去最后一位"b"
        name1 = name1 + '.png' ## 取name1为文件名,再将'png'加在文件名后
    else:
        name1 = name1 + '.png'

    # 测试代码
    # print(name1)

    # 修改文件名称
    name1 = name1.replace(" ","_") ## 将图片文件名里的空格转为”_“

    # 设置显示的链接名
    urlbegin = string.find('data-src="/images/thumb/') ## 与上面找干员名称同理,找到链接对应的开头"data-src="/images/thumb/",进而搜索链接名称
    urlend = string[urlbegin:].find('png') ## 与上面找干员名称同理,找到链接末尾的'png',进而确定链接名称
    imgurl = 'http://prts.wiki/images/' + string[urlbegin+24:urlbegin+urlend+3] ## 注五:图片链接都以"http://prts.wiki/images/"开头,这里是为了记录图片对应的链接

    # 爬取图片
    img = requests.get(imgurl, headers=headers).content

    # 图片写入本地文件夹
    if((name1)!="精二立绘A.png"):# 为了不读取那一张”精二立绘A“图片,专门做了一个循环(其实直接寻找删除也可以)
        with open(name1, 'wb') as f:
            f.write(img) ## 将爬取到的图片写入“Arknights”这个文件夹里
            num+=1 ## num=num+1,使得下面print函数里的”已爬取{}张“中的{}对应数值加1
            print("已爬取{}张,图片名称为:{},链接为:{}".format(num,name1,imgurl)) ## 这里{}的意义与C语言中的"%d"/"%f"十分相近,是从format()函数里读取对应位置的参数
        # "r"--以读方式打开,只能读文件,如果文件不存在,会发生异常
        # "w"--以写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件
        # "rb"--以二进制读方式打开,只能读文件,如果文件不存在,会发生异常
        # "wb"--以二进制写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件(所以这个程序就算运行多次,文件夹里也不会出现重复的立绘)
        time.sleep(1) ## 休息一秒后爬取下一张图片,这个函数在链接https://blog.csdn.net/weixin_45949073/article/details/104989562里讲得很清楚

运行代码,爬虫运行成功,且爬取到的图片没有重复!(注:只有四星以上的干员才有精英阶段二,可以解锁”_2”立绘,三星及以下干员无”_2”立绘)
爬取结果

这里的视频插入参考了itsNekoDeng的博客
(斑点_1和斑点_skin1之间有些许不同,你能找出来吗?)
经过一一核对,99%干员立绘均无重复,唯有极少数特殊干员(如阿米娅仅能在精二阶段后切换近卫形态,Pith、Sharp、Stormeye、Touch这四位是肉鸽模式干员,这五位干员的立绘1与立绘2重复),对这类极少数特殊情况我们手动处理即可,目标达成!

后来经过与noionion的交谈,我发现代码可以更加更加简化,话不多说,下面放简化后的代码:
(最后在图片本地写入模块把前面讲到的五位特殊干员的重复图片给处理了)

# 引入程序需要的相应包(类似C语言中的# include<stdio.h> 、include<math.h>等)
import os
from bs4 import BeautifulSoup
import time
import requests

# 设置爬取图片的网址
url = "http://prts.wiki/index.php?title=%E7%89%B9%E6%AE%8A:%E6%90%9C%E7%B4%A2&limit=500&offset=0&profile=images&search=%E7%AB%8B%E7%BB%98"


# 重定义请求头,防止被网页发现是爬虫,从而进行反爬操作
headers = {
    "Cookie": "arccount62298=c; arccount62019=c",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
}


# 爬取模块
html = requests.get(url, headers=headers) ##  这里是使用requests函数用之前重定义的请求头读取网页信息
### 测试代码
### print(html.text)
html.encoding = html.apparent_encoding ## encoding是从http中的header中的charset字段中提取的编码方式,若header中没有charset字段则默认为ISO-8859-1编码模式,无法解析中文,这是乱码的原因,apparent_encoding会从网页的内容中分析网页编码的方式,所以apparent_encoding比encoding更加准确,当网页出现乱码时可以把apparent_encoding的编码格式赋值给encoding。
soup = BeautifulSoup(html.text, "html.parser") ## "html.parser"是为了让解析速度快,容错率高,具体讲解网址为https://blog.csdn.net/yangjiajia123456/article/details/80959896
list = soup.find_all(class_ = "searchResultImage") ## 注一:对爬取页面进行“检查”操作后发现,所有的图片都包含在一个<table class="searchResultImage">的class中,所以这行代码的作用是查找html代码中标签为class且值为searchRusultImgge的所有代码块
### 测试代码
### print(list)
### 可以看到,相比于html.text,list里没有大量的网页配置信息,只有以图片名称为核心的列表


# 检查项目文件夹下是否有“Arknights”这个文件夹,没有的话新建一个,有的话就不新建了
try:
    os.mkdir("./Arknights")  ##  注二:创建文件夹(这个文件夹创建在现在的项目文件夹里,比方说我这里用的是“明日方舟干员立绘项目”项目文件夹,爬取到的图片就存在“明日方舟干员立绘项目”项目文件夹下的“Arknights”文件夹里,该函数具体介绍网址为https://blog.csdn.net/qq_20412595/article/details/82423764)
except:
    pass
# 将当前工作路径切换为“Arknights”这个文件夹
os.chdir("./Arknights") ##  切换当前工作路径(本函数的具体介绍在https://www.runoob.com/python3/python3-os-chdir.html)

# 将最初读取的图片设置为第一张图片
num=0

# 测试用参数
# num1 = 0
# num2 = 0

# 图片的爬取与本地存储
for s in list:
    string = str(s) ## 将list中的单个元素化为字符串,再针对该字符串进行处理

    namebegin = string.find('title="文件') ##  注三:显然,所有立绘的文件名开头都有“文件”二字,找到“文件”二字就相当于找到了立绘 注四:为了读取图片里包含的干员名,我们发现能够通过title入手,向后搜索名称,这里找到了"title"中"t”的位置
    # name_point_1 = namebegin+12 ## 为了读取干员名称,我们这里找到第一个空格的位置name_point_1(这里空格刚好在namebegin后的第二格)
    # name_point_2 = string.find(' 1'or' 2'or' skin') ## 这里找到第二个空格的位置name_point_2(本方法返回值远小于namebegin的数值,是错误的)
    nameend = string[namebegin:].find('png') ## 找到图片文件名末尾的"png"字样对应的位置

    ## 测试代码
    # print(namebegin)
    # print(name_point_1)
    # print(name_point_2)
    # print(nameend)

    ## 这里如果使用name_point_2 = string.find(' 1'or' 2'or' skin'),会显示位置为54,远小于namebegin的位置值,是错误的,所以用这种方法难以找到第二个空格的位置,所以我们将"干员名 1/2/skin(b/_V).png"整体令为name1,再在name1里寻找空格位置
    name1 = string[namebegin+13:namebegin+nameend-1]
    name1begin = name1.find('"干员名"')
    name1_point_1 = name1begin
    name1_point_2 = name1.find(' 1'or' 2'or' skin') ## 根据测试代码我们有,输出结果为-1,即name1[name1begin]对应于name1的最后一个字符

    # 测试代码
    # print(name1) # 输出结果格式"暗索 skin1 V1"
    # print(name1begin)
    # print(name1[name1begin])
    # print(name1_point_1)
    # print(name1_point_2)

    # print(string[(namebegin+nameend-3):(namebegin+nameend-1)]) ## 本代码用来查看后两位并进行检验

    if(string[namebegin+nameend-3:namebegin+nameend-1]=='V1'): ## 若后两位为V1
        # num1+=1
        # print('num1={}'.format(num1))
        continue
    elif(string[namebegin+nameend-2:namebegin+nameend-1]=='b'): ## 若最后一位为b
        # num2+=1
        # print('num2={}'.format(num2))
        continue
    else:
        name1 = name1 + '.png'

    # 测试代码
    # print(name1)

    # 修改文件名称
    name1 = name1.replace(" ","_") ## 将图片文件名里的空格转为”_“

    # 设置显示的链接名
    urlbegin = string.find('data-src="/images/thumb/') ## 与上面找干员名称同理,找到链接对应的开头"data-src="/images/thumb/",进而搜索链接名称
    urlend = string[urlbegin:].find('png') ## 与上面找干员名称同理,找到链接末尾的'png',进而确定链接名称
    imgurl = 'http://prts.wiki/images/' + string[urlbegin+24:urlbegin+urlend+3] ## 注五:图片链接都以"http://prts.wiki/images/"开头,这里是为了记录图片对应的链接

    # 爬取图片
    img = requests.get(imgurl, headers=headers).content

    # 测试代码
    # print(name1)

    # 图片写入本地文件夹
    if(name1!='精二立绘A.png' and name1!='Pith_2.png' and name1!='Sharp_2.png' and name1!='Stormeye_2.png' and name1!='Touch_2.png' and name1!='阿米娅(近卫)_2.png'):# 为了不读取”精二立绘A“与五位特殊干员(前面博文有说到)的图片,专门做了一个循环(其实爬取后再直接寻找删除也可以)
        with open(name1, 'wb') as f:
            f.write(img) ## 将爬取到的图片写入“Arknights”这个文件夹里
            num+=1 ## num=num+1,使得下面print函数里的”已爬取{}张“中的{}对应数值加1
            print("已爬取{}张,图片名称为:{},链接为:{}".format(num,name1,imgurl)) ## 这里{}的意义与C语言中的"%d"/"%f"十分相近,是从format()函数里读取对应位置的参数
        # "r"--以读方式打开,只能读文件,如果文件不存在,会发生异常
        # "w"--以写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件
        # "rb"--以二进制读方式打开,只能读文件,如果文件不存在,会发生异常
        # "wb"--以二进制写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件(所以这个程序就算运行多次,文件夹里也不会出现重复的立绘)
        time.sleep(1) ## 休息一秒后爬取下一张图片,这个函数在链接https://blog.csdn.net/weixin_45949073/article/details/104989562里讲得很清楚

这里简化了对”name1”变量的复杂处理,让代码清晰了很多。
上面的程序已经很好地满足我们的要求了,但后来我们又进行了一次优化,去除了上面的逻辑运算符,改用对列表进行硬处理:
原因是noionion大佬建议我对列表进行硬操作,而不是用!=这种逻辑运算符(说可能会炸,我慌的一批,所以就改了)

# 引入程序需要的相应包(类似C语言中的# include<stdio.h> 、include<math.h>等)
import os
from bs4 import BeautifulSoup
import time
import requests

# 设置爬取图片的网址
url = "http://prts.wiki/index.php?title=%E7%89%B9%E6%AE%8A:%E6%90%9C%E7%B4%A2&limit=500&offset=0&profile=images&search=%E7%AB%8B%E7%BB%98"


# 重定义请求头,防止被网页发现是爬虫,从而进行反爬操作
headers = {
    "Cookie": "arccount62298=c; arccount62019=c",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
}


# 爬取模块
html = requests.get(url, headers=headers) ##  这里是使用requests函数用之前重定义的请求头读取网页信息
### 测试代码
### print(html.text)
html.encoding = html.apparent_encoding ## encoding是从http中的header中的charset字段中提取的编码方式,若header中没有charset字段则默认为ISO-8859-1编码模式,无法解析中文,这是乱码的原因,apparent_encoding会从网页的内容中分析网页编码的方式,所以apparent_encoding比encoding更加准确,当网页出现乱码时可以把apparent_encoding的编码格式赋值给encoding。
soup = BeautifulSoup(html.text, "html.parser") ## "html.parser"是为了让解析速度快,容错率高,具体讲解网址为https://blog.csdn.net/yangjiajia123456/article/details/80959896
list = soup.find_all(class_ = "searchResultImage") ## 注一:对爬取页面进行“检查”操作后发现,所有的图片都包含在一个<table class="searchResultImage">的class中,所以这行代码的作用是查找html代码中标签为class且值为searchRusultImgge的所有代码块
### 测试代码
### print(list)
### 可以看到,相比于html.text,list里没有大量的网页配置信息,只有以图片名称为核心的列表


# 检查项目文件夹下是否有“Arknights”这个文件夹,没有的话新建一个,有的话就不新建了
try:
    os.mkdir("./Arknights")  ##  注二:创建文件夹(这个文件夹创建在现在的项目文件夹里,比方说我这里用的是“明日方舟干员立绘项目”项目文件夹,爬取到的图片就存在“明日方舟干员立绘项目”项目文件夹下的“Arknights”文件夹里,该函数具体介绍网址为https://blog.csdn.net/qq_20412595/article/details/82423764)
    ## 项目文件夹默认在代码存放的地方,如果你放在桌面,那后面生成的Arknights文件夹就在桌面
except:
    pass

# 将当前工作路径切换为“Arknights”这个文件夹
os.chdir("./Arknights") ##  切换当前工作路径(本函数的具体介绍在https://www.runoob.com/python3/python3-os-chdir.html)

# 将最初读取的图片设置为第一张图片
num=0

# 测试用参数
# num1 = 0
# num2 = 0

# 图片的爬取与本地存储
for s in list:
    string = str(s) ## 将list中的单个元素化为字符串,再针对该字符串进行处理

    namebegin = string.find('title="文件') ##  注三:显然,所有立绘的文件名开头都有“文件”二字,找到“文件”二字就相当于找到了立绘 注四:为了读取图片里包含的干员名,我们发现能够通过title入手,向后搜索名称,这里找到了"title"中"t”的位置
    # name_point_1 = namebegin+12 ## 为了读取干员名称,我们这里找到第一个空格的位置name_point_1(这里空格刚好在namebegin后的第二格)
    # name_point_2 = string.find(' 1'or' 2'or' skin') ## 这里找到第二个空格的位置name_point_2(本方法返回值远小于namebegin的数值,是错误的)
    nameend = string[namebegin:].find('png') ## 找到图片文件名末尾的"png"字样对应的位置

    ## 测试代码
    # print(namebegin)
    # print(name_point_1)
    # print(name_point_2)
    # print(nameend)

    ## 这里如果使用name_point_2 = string.find(' 1'or' 2'or' skin'),会显示位置为54,远小于namebegin的位置值,是错误的,所以用这种方法难以找到第二个空格的位置,所以我们将"干员名 1/2/skin(b/_V).png"整体令为name1,再在name1里寻找空格位置
    name1 = string[namebegin+13:namebegin+nameend-1]
    name1begin = name1.find('"干员名"')
    name1_point_1 = name1begin
    name1_point_2 = name1.find(' 1'or' 2'or' skin') ## 根据测试代码我们有,输出结果为-1,即name1[name1begin]对应于name1的最后一个字符

    # 测试代码
    # print(name1) # 输出结果格式"暗索 skin1 V1"
    # print(name1begin)
    # print(name1[name1begin])
    # print(name1_point_1)
    # print(name1_point_2)

    # print(string[(namebegin+nameend-3):(namebegin+nameend-1)]) ## 本代码用来查看后两位并进行检验

    if(string[namebegin+nameend-3:namebegin+nameend-1] in ['V1','V2']): ## 若后两位为V1或V2
        # num1+=1
        # print('num1={}'.format(num1))
        continue
    elif(string[namebegin+nameend-2:namebegin+nameend-1]=='b'): ## 若最后一位为b
        # num2+=1
        # print('num2={}'.format(num2))
        continue
    else:
        name1 = name1 + '.png'

    # 测试代码
    # print(name1)

    # 修改文件名称
    name1 = name1.replace(" ","_") ## 将图片文件名里的空格转为”_“

    # 设置显示的链接名
    urlbegin = string.find('data-src="/images/thumb/') ## 与上面找干员名称同理,找到链接对应的开头"data-src="/images/thumb/",进而搜索链接名称
    urlend = string[urlbegin:].find('png') ## 与上面找干员名称同理,找到链接末尾的'png',进而确定链接名称
    imgurl = 'http://prts.wiki/images/' + string[urlbegin+24:urlbegin+urlend+3] ## 注五:图片链接都以"http://prts.wiki/images/"开头,这里是为了记录图片对应的链接

    # 爬取图片
    img = requests.get(imgurl, headers=headers).content

    # 测试代码
    # print(name1)

    # 图片写入本地文件夹
    if(name1 not in ['精二立绘A.png','Pith_2.png','Sharp_2.png','Stormeye_2.png','Touch_2.png','阿米娅(近卫)_2.png']): ## 为了不读取”精二立绘A“与五位特殊干员(前面博文有说到)的图片,专门做了一个循环(其实爬取后再直接寻找删除也可以)
    ## 经过noionion大佬的提示,or的优先级不高,所以不能用if(name1 != '精二立绘A.png' or 'Pith_2.png' or 'Sharp_2.png' or 'Stormeye_2.png' or 'Touch_2.png' or '阿米娅(近卫)_2.png'):写法
    ## 他还建议我对列表进行硬操作,而不是用!=这种逻辑运算符(说可能会炸,我慌的一批)
    ## 所以我去除了if(name1 != ('精二立绘A.png' or 'Pith_2.png' or 'Sharp_2.png' or 'Stormeye_2.png' or 'Touch_2.png' or '阿米娅(近卫)_2.png')),改用全新的判断语法
        with open(name1, 'wb') as f:
            f.write(img) ## 将爬取到的图片写入“Arknights”这个文件夹里
            num+=1 ## num=num+1,使得下面print函数里的”已爬取{}张“中的{}对应数值加1
            print("已爬取{}张,图片名称为:{},链接为:{}".format(num,name1,imgurl)) ## 这里{}的意义与C语言中的"%d"/"%f"十分相近,是从format()函数里读取对应位置的参数
        # "r"--以读方式打开,只能读文件,如果文件不存在,会发生异常
        # "w"--以写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件
        # "rb"--以二进制读方式打开,只能读文件,如果文件不存在,会发生异常
        # "wb"--以二进制写方式打开,只能写文件,如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件(所以这个程序就算运行多次,文件夹里也不会出现重复的立绘)
        time.sleep(1) ## 休息一秒后爬取下一张图片,这个函数在链接https://blog.csdn.net/weixin_45949073/article/details/104989562里讲得很清楚

目标二

对于刚刚我们获得的不重复的图片,其类型分为三种:
在文件名“立绘_干员名_”后:
1/2/skin1/skin2/skin3(1为干员精英化0/1阶的默认皮肤,2为干员精英化2阶的默认皮肤,skin1/skin2/skin3为干员的其他皮肤)
经过两天时间的不断修改与优化,最终代码如下:
注:本代码的机理为处理本地文件夹’Arknights’里的所有图片,所以需要先运行目标一的代码才能运行此代码。

import os
import random
from PIL import Image

# 这次编码中我尝试使用函数来进行编程,三个'def'对应于三个函数
def file_name(file_dir): ## 定义一个以文件夹名称为自变量的函数,之后可以直接调用,该函数的作用是扫描出./Arknights文件夹里的所有立绘文件名
    L = []
    for root, dirs, files in os.walk(file_dir):
        ## os.walk()函数
        ## top 是你所要遍历的目录的地址, 返回的是一个三元组(root,dirs,files)。
        ## root 所指的是当前正在遍历的这个文件夹的本身的地址
        ## dirs 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
        ## files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录)
        for file in files:
            # 测试代码
            # print(os.path.splitext(file)[0])
            # print(os.path.splitext(file)[1])
            if os.path.splitext(file)[1] == '.png':
                L.append(os.path.join(root, file))
    n = len(L) ## 读取L列表的长度
    # 测试代码
    # print(type(file_name(file_dir)))
    ## 这里若是用return L,n,返回的是一个元组(tuple),其中的两个元素分别为列表L和数值n,若是用return L,返回的是一个列表
    return L ## 注意这里返回的是一个列表,不能采用return L,n + for i in file_name(file_dir).n:方式,会报错


def search(file_dir): ## 该函数能够在文件夹中搜索对应的精英0/1阶段,精英二阶段以及皮肤各自对应的立绘及其对应位置的i值
    ## 匹配文件名列表
    L_1 = [] ## 精英0/1阶段(五位特殊干员:阿米娅、Pith、Sharp、Stormeye、Touch除外)立绘文件名列表
    L_2 = [] ## 精英二阶段立绘文件名列表
    L_skin = [] ## 皮肤立绘文件名列表
    L_error = [] ## 读取错误的立绘名列表

    ## 匹配文件对应位置列表
    L_1_i = [] ## 记录匹配图片对应i值的列表
    L_2_i = []
    L_skin_i = []
    L_error_i = []

    ## 匹配文件数目列表
    n_1 = 0 ## 精英阶段一立绘数
    n_2 = 0 ## 精英阶段二立绘数
    n_skin = 0 ## 皮肤立绘数
    n_error = 0 ## 读取错误的立绘数
    for i in range(len(file_name(file_dir))): ## 这里不能用for i in int(file_name(file_dir)[1]),详见链接https://blog.csdn.net/qq_41888799/article/details/82223362
        if('skin' in file_name(file_dir)[i]): ## 先检索皮肤立绘,否则如果先检索精英阶段立绘,skin1/2中也包含1/2,使得检索结果不准确
            n_skin += 1
            settlementskin_1 = L_skin.append(file_name(file_dir)[i]) ## 向“皮肤立绘文件名列表”中加入该文件名
            settlementskin_2 = L_skin_i.append(i) ## 向“匹配文件对应位置列表”中加入文件名在所有文件名中的位置
        elif('1' in file_name(file_dir)[i]): ## 再检索精英0/1阶立绘
            n_1 += 1
            settlement1_1 = L_1.append(file_name(file_dir)[i]) ## 向“精英0/1阶段(五位特殊干员:阿米娅、Pith、Sharp、Stormeye、Touch除外)立绘文件名列表”中加入该文件名
            settlement1_2 = L_1_i.append(i)
        elif('2' in file_name(file_dir)[i]): ## 再检索精英2阶立绘
            n_2 += 1
            settlement2_1 = L_2.append(file_name(file_dir)[i]) ## 向“精英二阶段立绘文件名列表”中加入该文件名
            settlement2_2 = L_2_i.append(i)
        else: ## 这里其实可以去除,目的是为了检查是否有读取错误的图片:
            n_error += 1
            settlementerror_1 = L_error.append(file_name(file_dir)[i]) ## 向“读取错误的立绘名列表”中加入该文件名
            settlementerror_2 = L_error_i.append(i)
        i += 1 ## 这里不能使用C语言中的i++/++i,因为Python中的数字类型是不可变数据,当变量值发生改变时,会新申请一块内存赋值为新值,然后将变量指向新的内存地址。而在C语言中,i指向的内存地址并不会发生改变,而是改变内存的内容。详见链接https://blog.csdn.net/elvirangel/article/details/102743249
    # 测试代码
    # print(L_1)
    # print(L_2)
    # print(L_skin)
    # print(L_error)
    # print(n_1) # 184(这里及以下三个数字为截至2020年1月2日我们按照目标一的代码爬取到的图片分组后各组内图片的数目,后期由于游戏会更新,立绘数目也会更新,再运行该代码下列数字也会更新,所以请不要以此为准检验自己的代码是否正确
    # print(n_2) # 149
    # print(n_skin) # 94
    # print(n_error) # 0
    return L_1_i,L_2_i,L_skin_i,L_1,L_2,L_skin


def createdocument(path): ## 该函数能够创建文件夹,让其包含各自对应分类的图片
    # 去除首位空格
    path = path.strip()
    # 去除尾部 \ 符号
    path = path.rstrip("\\")

    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(path)

    # 判断结果
    if not isExists:
        # 如果不存在该目录则创建目录
        # 创建目录操作函数
        os.makedirs(path)
        # print(path + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建,并提示目录已存在
        # print(path + ' 目录已存在')
        return False


def transferPictures(file_dir, nowpath, newpath): ## 将文件夹下的不同类别的文件夹中的部分图片转移到另一个文件夹下的相同类别的文件夹下,并删除原文件夹中的相应图片(类似于剪切)
    a = search(file_dir)[3] ## 用新列表a来保存search返回的列表L_1,使之后的程序运行效率增加
    b = search(file_dir)[4] ## 用新列表b来保存search返回的列表L_2,使之后的程序运行效率增加
    c = search(file_dir)[5] ## 用新列表c来保存search返回的列表L_skin,使之后的程序运行效率增加
    # print(a)
    # print(newpath)
    num_1 = 0
    num_2 = 0
    num_skin = 0

    for i in range(len(a)): ## 遍历a中所有的元素(精英化一阶情况)
        imgname = a[i]  ## 我们发现如果使用n = search('./Arknights')[1][i],代码速度会很慢,这是由于若使用该函数,程序就会再执行一次search函数,很影响程序效率,使用我们使用新列表a来保存search返回的列表L_1
        ## 我们发现imgname1为“./Arknights\W_2.png”格式,所以需要进行一些处理
        imgname = imgname.strip(file_dir) + 'ng' ## 不能使用imgname = imgname.strip('./Arknights')代码,因为.strip()函数对字符串的操作是:只要头尾包含有指定字符序列中的字符就删除,而'./Arknights'中含有字符'n'、'g'会导致imgname变为'\W_2.p'格式
        imgname = imgname.replace('\\','/') ## 把'\'改为'/'(文件路径最好不要既带'/'又带'\')
        # print('imgname为{}'.format(imgname))
        imgpath = nowpath + imgname ## 原位置
        newimgpath = newpath[0] + imgname ## 准备放入的位置
        # print('imgpath为{}'.format(imgpath))
        # print('newimgpath为{}'.format(newimgpath))
        if not os.path.exists(newimgpath):  ## 如果该根目录newimgpath不存在,建立一个这样的根目录,否则会出现FileNotFoundError: [Errno 2] No such file or directory: './精英化二阶立绘\\W_2.png'报错
            img = Image.new('RGB', (800, 1280), (255, 255, 255))
            img.save('newimgpath', 'PNG') ## 创建一个新的png文件,用于写入原图片
        im = Image.open(imgpath)  ## 打开该文件路径进行写入
        im.save(newimgpath) ## 写入图片
        os.remove(imgpath) ## 转移完后删除原图片
        ## 使用close()函数无法关闭,原因是Image没有close()函数
        fp = open('newimgpath', 'rb')
        img = Image.open(fp)
        fp.close()
        num_1 += 1  ## num=num+1,使得下面print函数里的”精英化一阶立绘已移动{}张“中的{}对应数值加1
        print('精英化一阶立绘已移动{}张,图片名称为:{}'.format(num_1,imgname.strip('/')))

    for i in range(len(b)): ## 遍历a中所有的元素(精英化一阶情况)
        imgname = b[i]  ## 我们发现如果使用n = search('./Arknights')[1][i],代码速度会很慢,这是由于若使用该函数,程序就会再执行一次search函数,很影响程序效率,使用我们使用新列表a来保存search返回的列表L_2
        ## 我们发现imgname1为“./Arknights\W_2.png”格式,所以需要进行一些处理
        imgname = imgname.strip(file_dir) + 'ng' ## 不能使用imgname = imgname.strip('./Arknights')代码,因为.strip()函数对字符串的操作是:只要头尾包含有指定字符序列中的字符就删除,而'./Arknights'中含有字符'n'、'g'会导致imgname变为'\W_2.p'格式
        imgname = imgname.replace('\\','/')
        # print('imgname为{}'.format(imgname))
        imgpath = nowpath + imgname ## 原位置
        newimgpath = newpath[1] + imgname ## 准备放入的位置
        # print('imgpath为{}'.format(imgpath))
        # print('newimgpath为{}'.format(newimgpath))
        if not os.path.exists(newimgpath):  ## 如果该根目录newimgpath不存在,建立一个这样的根目录,否则会出现FileNotFoundError: [Errno 2] No such file or directory: './精英化二阶立绘\\W_2.png'报错
            img = Image.new('RGB', (800, 1280), (255, 255, 255))
            img.save('newimgpath', 'PNG') ## 创建一个新的png文件,用于写入原图片
        im = Image.open(imgpath)  ## 打开该文件路径进行写入
        im.save(newimgpath) ## 写入图片
        os.remove(imgpath) ## 转移完后删除原图片
        ## 使用close()函数无法关闭,原因是Image没有close()函数
        fp = open('newimgpath', 'rb')
        img = Image.open(fp)
        fp.close()
        num_2 += 1  ## num=num+1,使得下面print函数里的”精英化二阶立绘已移动{}张“中的{}对应数值加1
        print('精英化二阶立绘已移动{}张,图片名称为:{}'.format(num_2,imgname.strip('/')))

    for i in range(len(c)): ## 遍历a中所有的元素(精英化一阶情况)
        imgname = c[i]  ## 我们发现如果使用n = search('./Arknights')[1][i],代码速度会很慢,这是由于若使用该函数,程序就会再执行一次search函数,很影响程序效率,使用我们使用新列表a来保存search返回的列表L_1
        ## 我们发现imgname1为“./Arknights\W_2.png”格式,所以需要进行一些处理
        imgname = imgname.strip(file_dir) + 'ng' ## 不能使用imgname = imgname.strip('./Arknights')代码,因为.strip()函数对字符串的操作是:只要头尾包含有指定字符序列中的字符就删除,而'./Arknights'中含有字符'n'、'g'会导致imgname变为'\W_2.p'格式
        imgname = imgname.replace('\\','/')
        # print('imgname为{}'.format(imgname))
        imgpath = nowpath + imgname ## 原位置
        newimgpath = newpath[2] + imgname ## 准备放入的位置
        # print('imgpath为{}'.format(imgpath))
        # print('newimgpath为{}'.format(newimgpath))
        if not os.path.exists(newimgpath):  ## 如果该根目录newimgpath不存在,建立一个这样的根目录,否则会出现FileNotFoundError: [Errno 2] No such file or directory: './精英化二阶立绘\\W_2.png'报错
            img = Image.new('RGB', (800, 1280), (255, 255, 255))
            img.save('newimgpath', 'PNG') ## 创建一个新的png文件,用于写入原图片
        im = Image.open(imgpath)  ## 打开该文件路径进行写入
        im.save(newimgpath) ## 写入图片
        os.remove(imgpath) ## 转移完后删除原图片
        ## 使用close()函数无法关闭,原因是Image没有close()函数
        fp = open('newimgpath', 'rb')
        img = Image.open(fp)
        fp.close()
        num_skin += 1  ## num=num+1,使得下面print函数里的”皮肤立绘已移动{}张“中的{}对应数值加1
        print('皮肤立绘已移动{}张,图片名称为:{}'.format(num_skin,imgname.strip('/')))

# 测试代码
# print(file_name('./Arknights'))
# print(search('./Arknights'))
# print(search('./Arknights')[4]) ## 爬取到的精二阶段立绘在全立绘列表中所在的位置号列表

# 定义要创建的目录
newpath = ['./精英化一阶立绘', './精英化二阶立绘', './皮肤立绘']
# 调用函数createdocument(path)创建目录
for i in range(len(newpath)): ## 按照上述列表中元素的顺序创建三个文件夹
    createdocument(newpath[i])

# 转移图片位置
settlement = transferPictures('./Arknights','./Arknights',['./精英化一阶立绘','./精英化二阶立绘','./皮肤立绘']) ## 这里的newpath有三种可能,我们以列表形式将其输入函数
<!-- vlog模块 --> 
![精英化一阶](https://heart-of-engine-picture-bed-1.oss-cn-beijing.aliyuncs.com/img/%E7%B2%BE%E8%8B%B1%E5%8C%96%E4%B8%80%E9%98%B6.png) ![精英化二阶](https://heart-of-engine-picture-bed-1.oss-cn-beijing.aliyuncs.com/img/%E7%B2%BE%E8%8B%B1%E5%8C%96%E4%BA%8C%E9%98%B6.png) ![皮肤](https://heart-of-engine-picture-bed-1.oss-cn-beijing.aliyuncs.com/img/%E7%9A%AE%E8%82%A4.png) noionion也写了一段代码,仅用77行代码就完成了我上面198行代码所能完成的事(他的代码中还包含很多空行),只不过他是在线处理(爬取下图片时就进行分类),而我是离线处理(对图片文件夹进行处理) ~~因为我做过爬虫了,就想做个文件处理的代码了解一下新领域~~ 下面是代码的实际效果: 下面贴上他的代码:(已经本人允许)
import os
from bs4 import BeautifulSoup
import time
import requests

url = "http://prts.wiki/index.php?title=%E7%89%B9%E6%AE%8A:%E6%90%9C%E7%B4%A2&limit=500&offset=0&profile=images&search=%E7%AB%8B%E7%BB%98"

headers = {
    "Cookie": "arccount62298=c; arccount62019=c",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
}

html = requests.get(url, headers=headers)
html.encoding = html.apparent_encoding
soup = BeautifulSoup(html.text, "html.parser")

list = soup.find_all(class_ = "searchResultImage")

try:
    os.mkdir("./Arknights")  ##  创建文件夹
except:
    pass

os.chdir("./Arknights")

try:
    os.mkdir("./skin")
except:
    pass

try:
    os.mkdir("./1")
except:
    pass

try:
    os.mkdir("./2")
except:
    pass

num=0

for s in list:
    string = str(s)

    namebegin = string.find('title="文件')
    nameend = string[namebegin:].find('png')

    # print(string[namebegin+7:namebegin+nameend+3])

    name = string[namebegin+10:namebegin+nameend+3]
    name = name.replace(" ","_")

    if name.find("b") != -1 or name.find("V") !=-1 or name.find('立绘A') != -1: continue

    urlbegin = string.find('data-src="/images/thumb/')
    urlend = string[urlbegin:].find('png')

    # print(string[urlbegin+24:urlbegin+urlend+3])

    imgurl = 'http://prts.wiki/images/' + string[urlbegin+24:urlbegin+urlend+3]

    # print(name, imgurl)

    if name.find("_skin") != -1: os.chdir('./skin')
    elif name.find("_1") != -1: os.chdir('./1')
    elif name.find("_2") != -1: os.chdir('./2')

    img = requests.get(imgurl, headers=headers).content
    with open(name, 'wb') as f:
        f.write(img)
        num+=1
        print("已爬取{}张,图片名称为:{},链接为:{}".format(num,name,imgurl))

    os.chdir("..")

    time.sleep(1)

可以看到,他非常精妙地用三行简短的判断语句

if name.find("_skin") != -1: os.chdir('./skin')
elif name.find("_1") != -1: os.chdir('./1')
elif name.find("_2") != -1: os.chdir('./2')

替换了之前复杂的三大段判断

for i in range(len(a)): ## 遍历a中所有的元素(精英化一阶情况)
    imgname = a[i]  ## 我们发现如果使用n = search('./Arknights')[1][i],代码速度会很慢,这是由于若使用该函数,程序就会再执行一次search函数,很影响程序效率,使用我们使用新列表a来保存search返回的列表L_1
    ## 我们发现imgname1为“./Arknights\W_2.png”格式,所以需要进行一些处理
    imgname = imgname.strip(file_dir) + 'ng' ## 不能使用imgname = imgname.strip('./Arknights')代码,因为.strip()函数对字符串的操作是:只要头尾包含有指定字符序列中的字符就删除,而'./Arknights'中含有字符'n'、'g'会导致imgname变为'\W_2.p'格式
    imgname = imgname.replace('\\','/') ## 把'\'改为'/'(文件路径最好不要既带'/'又带'\')
    # print('imgname为{}'.format(imgname))
    imgpath = nowpath + imgname ## 原位置
    newimgpath = newpath[0] + imgname ## 准备放入的位置
    # print('imgpath为{}'.format(imgpath))
    # print('newimgpath为{}'.format(newimgpath))
    if not os.path.exists(newimgpath):  ## 如果该根目录newimgpath不存在,建立一个这样的根目录,否则会出现FileNotFoundError: [Errno 2] No such file or directory: './精英化二阶立绘\\W_2.png'报错
        img = Image.new('RGB', (800, 1280), (255, 255, 255))
        img.save('newimgpath', 'PNG') ## 创建一个新的png文件,用于写入原图片
    im = Image.open(imgpath)  ## 打开该文件路径进行写入
    im.save(newimgpath) ## 写入图片
    os.remove(imgpath) ## 转移完后删除原图片
    ## 使用close()函数无法关闭,原因是Image没有close()函数
    fp = open('newimgpath', 'rb')
    img = Image.open(fp)
    fp.close()
    num_1 += 1  ## num=num+1,使得下面print函数里的”精英化一阶立绘已移动{}张“中的{}对应数值加1
    print('精英化一阶立绘已移动{}张,图片名称为:{}'.format(num_1,imgname.strip('/')))
for i in range(len(b)): ## 遍历a中所有的元素(精英化一阶情况)
    imgname = b[i]  ## 我们发现如果使用n = search('./Arknights')[1][i],代码速度会很慢,这是由于若使用该函数,程序就会再执行一次search函数,很影响程序效率,使用我们使用新列表a来保存search返回的列表L_2
    ## 我们发现imgname1为“./Arknights\W_2.png”格式,所以需要进行一些处理
    imgname = imgname.strip(file_dir) + 'ng' ## 不能使用imgname = imgname.strip('./Arknights')代码,因为.strip()函数对字符串的操作是:只要头尾包含有指定字符序列中的字符就删除,而'./Arknights'中含有字符'n'、'g'会导致imgname变为'\W_2.p'格式
    imgname = imgname.replace('\\','/')
    # print('imgname为{}'.format(imgname))
    imgpath = nowpath + imgname ## 原位置
    newimgpath = newpath[1] + imgname ## 准备放入的位置
    # print('imgpath为{}'.format(imgpath))
    # print('newimgpath为{}'.format(newimgpath))
    if not os.path.exists(newimgpath):  ## 如果该根目录newimgpath不存在,建立一个这样的根目录,否则会出现FileNotFoundError: [Errno 2] No such file or directory: './精英化二阶立绘\\W_2.png'报错
        img = Image.new('RGB', (800, 1280), (255, 255, 255))
        img.save('newimgpath', 'PNG') ## 创建一个新的png文件,用于写入原图片
    im = Image.open(imgpath)  ## 打开该文件路径进行写入
    im.save(newimgpath) ## 写入图片
    os.remove(imgpath) ## 转移完后删除原图片
    ## 使用close()函数无法关闭,原因是Image没有close()函数
    fp = open('newimgpath', 'rb')
    img = Image.open(fp)
    fp.close()
    num_2 += 1  ## num=num+1,使得下面print函数里的”精英化二阶立绘已移动{}张“中的{}对应数值加1
    print('精英化二阶立绘已移动{}张,图片名称为:{}'.format(num_2,imgname.strip('/')))
for i in range(len(c)): ## 遍历a中所有的元素(精英化一阶情况)
    imgname = c[i]  ## 我们发现如果使用n = search('./Arknights')[1][i],代码速度会很慢,这是由于若使用该函数,程序就会再执行一次search函数,很影响程序效率,使用我们使用新列表a来保存search返回的列表L_1
    ## 我们发现imgname1为“./Arknights\W_2.png”格式,所以需要进行一些处理
    imgname = imgname.strip(file_dir) + 'ng' ## 不能使用imgname = imgname.strip('./Arknights')代码,因为.strip()函数对字符串的操作是:只要头尾包含有指定字符序列中的字符就删除,而'./Arknights'中含有字符'n'、'g'会导致imgname变为'\W_2.p'格式
    imgname = imgname.replace('\\','/')
    # print('imgname为{}'.format(imgname))
    imgpath = nowpath + imgname ## 原位置
    newimgpath = newpath[2] + imgname ## 准备放入的位置
    # print('imgpath为{}'.format(imgpath))
    # print('newimgpath为{}'.format(newimgpath))
    if not os.path.exists(newimgpath):  ## 如果该根目录newimgpath不存在,建立一个这样的根目录,否则会出现FileNotFoundError: [Errno 2] No such file or directory: './精英化二阶立绘\\W_2.png'报错
        img = Image.new('RGB', (800, 1280), (255, 255, 255))
        img.save('newimgpath', 'PNG') ## 创建一个新的png文件,用于写入原图片
    im = Image.open(imgpath)  ## 打开该文件路径进行写入
    im.save(newimgpath) ## 写入图片
    os.remove(imgpath) ## 转移完后删除原图片
    ## 使用close()函数无法关闭,原因是Image没有close()函数
    fp = open('newimgpath', 'rb')
    img = Image.open(fp)
    fp.close()
    num_skin += 1  ## num=num+1,使得下面print函数里的”皮肤立绘已移动{}张“中的{}对应数值加1
    print('皮肤立绘已移动{}张,图片名称为:{}'.format(num_skin,imgname.strip('/')))

这让我意识到代码简洁度的重要性,好的代码不仅要功能强大,还要短小精悍,过长的代码不仅容易因为疏漏出现各种BUG,还会使代码变得晦涩难懂,让其他人难以下手。
以后我的代码会尽量优化,减少不必要的语句。

目标三

博主要准备期末考试啦!目标三和四留到期末考完吧!

参考网站

爬虫区:
新年前爬个明日方舟的立绘_noionion
Requests库基本使用_简书
BeautifulSoup4的介绍与使用_CSDN
爬虫(5)一文搞懂cookie原理和使用_CSDN
python中os.mkdir()函数_CSDN
Python爬虫常用之HtmlParser_博客园

文件处理区:
python 将文件夹中的部分图片转移到另一文件夹_CSDN
Python_day7_列表、遍历列表、循环函数、range_CSDN
Python创建目录文件夹_博客园
python批量创建文件夹和文件_CSDN
Python os.walk 和python glob.glob使用_CSDN
python5 : .strip() / os.path…() /reload(sys)_CSDN
python 字符串所有操作_CSDN
附:在CSDN搜索时发现了一个11岁的大佬,他的CSDN链接

新年祝愿

最后,祝所有看到这篇文章的小伙伴们新年快乐!!!


文章作者: Heart-of-engine
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Heart-of-engine !
打赏
  目录