刚开学时有一段时间周末没事,于是经常在P站的特辑里收图,但是P站加载图片的速度比较感人,觉得自己身为计算机专业,怎么可以做一张张图慢慢下这么low的事,而且这样效率的确也太低了,于是就想写个程序来帮我下,但是只会C与c++的我看来是无法用他们来做这事的,于是就去学了下简单,强大的python,不得不说,python的模块的确叼,依靠几个模块就可以在完全不知道原理的前提下让程序执行相应功能,这样虽然爽但对于学习不利,我这次就权当写着玩吧,在我学会怎样使用c++来做这事之前我不会再使用python编程了,不过写这个程序过程中我还是知道了一些东西,比如坑爹的编码问题,还有http的一点知识,还有能大概读懂html了,不过,不是通过系统学习,并没有什么卵用。废话不多说,来看代码吧。
from bs4 import BeautifulSoup import requests from PIL import Image from io import BytesIO import os import codecs import sys headers={ ‘Accept‘:‘text/html‘, ‘Accept-Language‘:‘zh-CN,zh;q=0.8‘, ‘Referer‘:"", ‘User-Agent‘:"此处为浏览器的user-agent"#浏览器数据 } order=1 def getpic (src,href,mode=""): os.system("cls") print("共有%d个文件需要下载"%number_of_file) if src[-3:] == "gif": return‘‘‘使用gif来保存静态图片的都是邪教‘‘‘ headers[‘Referer‘] = href ispng=False url=src.replace("_master1200","") url=url.replace(url[20:40],"img-original") if mode==‘mul‘: print(‘正在下载第%d个...‘%order) print("该文件含有多张图:") else: print(‘正在下载第%d个...‘%order) if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): print(‘已下载第%d个‘%order) return else: data=requests.get(url,headers=headers,timeout=60) if str(data)!=‘<Response [200]>‘: ispng=True url=url.replace("jpg","png") if mode == ‘mul‘: if ispng: print("********正在下载第1张") if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers) im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘png‘) print("********已下载第1张") for i in range(150): url=url.replace("p%d.png"%i,"p%d.png"%(i+1)) os.system("cls") print("********正在下载第%d张..."%(i+2)) if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) if str(data)!=‘<Response [200]>‘: break im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘png‘) print("********已下载第%d张"%(i+2)) else: print("********正在下载第1张") if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘jpeg‘) print("********已下载第1张") for i in range(150): url=url.replace("p%d.jpg"%i,"p%d.jpg"%(i+1)) os.system("cls") print("********正在下载第%d张..."%(i+2)) if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) if str(data)!=‘<Response [200]>‘: break im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘jpeg‘) print("********已下载第%d张"%(i+2)) else: if ispng : if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): print(‘已下载第%d个‘%order) return else: data=requests.get(url,headers=headers,timeout=60) if str(data) == ‘<Response [200]>‘: im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘png‘) print(‘已下载第%d个‘%order) else: im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘jpeg‘) print(‘已下载第%d个‘%order) number=sys.argv[1] file_path=sys.argv[2]+‘\\Picture\\‘#修改此处即可改变路径 url_save="http://spotlight.pics/zh/a/%s"%number wb=requests.get(url_save,headers=headers) wb_data=BeautifulSoup(wb.text,‘lxml‘) title=wb_data.h2.string.replace("\n","").replace(":","").replace("?","").replace("\"","").replace(" ","") title=title.replace("<","").replace(">","").replace("|","").replace("*","").replace("/","").replace("\\","") #依据windows目录命名规则 file_path=(file_path+title)+"\\" ‘‘‘判断文件是否存在‘‘‘ if not os.path.exists(file_path): introduce=str(wb_data.h2.next_sibling.next_sibling.next_element) os.mkdir(file_path) f=codecs.open(file_path+"介绍.txt","w","utf-8") f.write("特辑号:%s\n"%number+introduce) f.close() divs=wb_data.body.select(‘div[class="illust-wrap"]‘) number_of_file=len(divs) headers[‘Accept‘]=‘image/webp,image/*,*/*;q=0.8‘ for div in divs: if str(div.a.parent[‘class‘])!=‘[\‘ugoira-player\‘, \‘ui-scroll-view\‘]‘: if str(div.a.parent[‘class‘])==‘[\‘illust-multi-page-wrap\‘]‘: getpic(div.img[‘src‘],div.a[‘href‘],"mul") else: getpic(div.img[‘src‘],div.a[‘href‘]) #要想好动图怎么办 order+=1
说一下,写程序过程中遇到的问题。
先说主要思路,我们来看一下P站的特辑页面
<div class="illust-wrap"> <a href="http://www.pixiv.net/member_illust.php?mode=medium&illust_id=56543092" class="inner-link" onclick="ga(‘send‘, ‘event‘, ‘Article‘, ‘ClickIllustImage‘, 56543092);"> <img src="http://i1.pixiv.net/c/480x960/img-master/img/2016/04/26/15/59/36/56543092_p0_master1200.jpg"> </a> </div>
这是一个图片url的语句块,可以看到图片的url与图片对应的页面链接,但这个url不是高清图的,高清图的url要在链接对应页面里找,正常来说,我们应该request一下那个链接,请求一下html,再对那个页面中的数据提供的大图url进行request,这才是正规途径,但是,我通过观察大图url发现,只要将小图url的一些地方进行替换就是大图的url,于是我就干脆,试探性地request,只要返回的不是
404,那么说明就是大图的url,那么我们就可以把request返回的数据通过byteio写成图片,就相当于下载了。基本思路就是,寻找预览图对应url---》修改预览图url---》request获得数据---》保存数据为图片。所以,主要的问题就是如何分析网页获取图片url,此外,有一点要说的就是,对于含多张图的url,我采用request到返回404为止的方式,又是一招粗暴的方法,正确方法还是获取网页数据并分析确定图片数目,但是,我一开始不知道可以控制request得到数据的类型,于是觉得少request一下更划算,现在看来真是自作聪明,不过这样对性能的影响是好是坏我不清楚,这主要取决于request不存在的url返回404的速度,后来也懒得改了,因为我发现,速度本来就不乐观,网速好的时候还好,网速差的时候真的是。。。。
然后说一下,写程序过程中遇到的各种bug
问题 1:按特辑保存文件,文件名有可能有unicode字符,而默认的文本保存是gbk,这样就会编码错误。
解决方法:使用codecs已utf-8的编码来写文件。
问题 2:动图的处理。
解决方法:我可以假装看不见(去死吧!),好吧,讲真的,总的来说,特辑里的动图相对而言数量少,我觉得不下没关系,但是如果要下的话,P站那里也没有提供高清图的url,我可以加点代码让他下,但是下不了高清,个人觉得不是高清图的话要他何用?
问题 3:gif图,p站图片大部分格式都是png与jpg这两种主流格式,极少部分是gif,我也是在大量测试(收图)的情况下才发现居然有这种坑。
解决方法:我可以再假装看不见,好吧,讲真的。是因为gif真的很少,我为了解决这个bug就得多加一个判断,这样我觉得得不偿失,(这主要是我不访问链接页面获取url数据留下的锅),于是就拒绝下gif,另一个就是无论如何我都很难接受不是动图却使用gif的事实。。。
问题 4: header 的制定。
解决方法:一开始不知道,怎样也request不到图片,傻傻地加了cookie也没用,后来才发现只要user—agent与referer就好了,另外,默认的文字是英文,要加上language才能得到中文标题。
问题 5:windows目录名问题
解决方法:将所有标题中的非法字符去掉,这个真的没办法,这是微软的锅,就算会让标题看起来显得奇怪我也只能去掉。
以上就是所有编程过程中的问题了,最后解释一下这段代码,写这段代码的过程我是使用python3.5来作为解释器,这段代码的功能是接受两个参数,特辑号与保存路径。他就会在路径下创建一个目录名为特辑标题的文件夹,然后将专辑中的所有非动图与gif下载到文件夹中,因为我没有队列开多线程优化(我也不会的说),所有速度比较感人。还有值得一提的是,路径中必须有名为Picture的文件夹,所有特辑都会保存在其中,我觉得这种事不用交给程序,于是就没写进去。
其实这段代码能有效运行,主要功劳都属于beautifulsoup与request这两个功能强大,封装性好的模块(也许太好了,以至于我根本不清楚它的原理,有点小郁闷)。
此外,有了以上那个getpic函数,我们通过分析页面,就可以下载P站各个模块的图。P站具有标签功能与图片的热度显示,对标签较好的维护,使得海量图片有效的组织,图片的热度可以看作图片质量的衡量指标,这也许能看作P站的一个卖点,至少,它自己的确是把这看作是一个卖点,收费20RMP一个月就可以支持按热度搜索标签内容,但我穷,只能写程序来偷偷拥有这个功能,其实,有了getpic,这就不是难事了,但问题是,只有注册用户在登陆后才看到到热度,也就是,要在header中加上注册用户的cookie,一开始不知道,还以为是各种动态脚本生成的加密算法之类的,结果我还是高估了P站程序员的良心。嗯,顺便也贴写代码吧。
from bs4 import BeautifulSoup from PIL import Image import os import requests from io import BytesIO import sys headers={ ‘Accept‘:‘‘, ‘Accept-Language‘:‘zh-CN,zh;q=0.8‘, ‘Referer‘:"", ‘User-Agent‘:"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",#浏览器数据 ‘Cookie‘:"" } cookies="" def getpic (src,href,mode=""): if src[-3:] == "gif": return‘‘‘使用gif来保存静态图片的都是邪教‘‘‘ headers[‘Referer‘] = href ispng=False url=src.replace("_master1200","") url=url.replace(url[20:40],"img-original") if mode==‘mul‘: print("该文件含有多张图:") else: print(‘正在下载...‘) if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): print(‘下载完成‘) return else: data=requests.get(url,headers=headers,timeout=60) if str(data)!=‘<Response [200]>‘: ispng=True url=url.replace("jpg","png") if mode == ‘mul‘: if ispng: print("********正在下载第1张") if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) if str(data) == ‘<Response [200]>‘: im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘png‘) print("********已下载第1张") for i in range(150): url=url.replace("p%d.png"%i,"p%d.png"%(i+1)) os.system("cls") print(‘正在下载第%d页...‘%page) print("********正在下载第%d张..."%(i+2)) if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) if str(data)!=‘<Response [200]>‘: break im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘png‘) print("********已下载第%d张"%(i+2)) else: print("********正在下载第1张") if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘jpeg‘) print("********已下载第1张") for i in range(150): url=url.replace("p%d.jpg"%i,"p%d.jpg"%(i+1)) os.system("cls") print(‘正在下载第%d页...‘%page) print("********正在下载第%d张..."%(i+2)) if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): pass else: data=requests.get(url,headers=headers,timeout=60) if str(data)!=‘<Response [200]>‘: break im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘jpeg‘) print("********已下载第%d张"%(i+2)) else: if ispng : if os.path.exists(file_path+(url.replace(‘/‘,""))[-15:]): print(‘下载完成‘) return else: data=requests.get(url,headers=headers,timeout=60) if str(data) == ‘<Response [200]>‘: im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘png‘) print(‘下载完成‘) else: im=Image.open(BytesIO(data.content)) im.save(file_path+(url.replace(‘/‘,""))[-15:],‘jpeg‘) print(‘下载完成‘) tag=sys.argv[1] hot=int(sys.argv[2]) file_path=sys.argv[3]+‘\\Tag\\‘#修改此处即可改变路径 title=tag.replace("\n","").replace(":","").replace("?","").replace("\"","").replace(" ","") title=title.replace("<","").replace(">","").replace("|","").replace("*","").replace("/","").replace("\\","") #依据windows目录命名规则 file_path=(file_path+title+str(hot))+"\\" if not os.path.exists(file_path): os.mkdir(file_path) if not os.path.exists(file_path+‘begin‘): f=open(file_path+‘begin‘,"w") f.write(‘1‘) f.close() f=open(file_path+‘begin‘,"r") begin=int(f.read()) for page in range(begin,1001): url_save="http://www.pixiv.net/search.php?word=%s&s_mode=s_tag_full&order=date_d&type=illust&p=%d"%(tag,page) headers[‘Referer‘]="" headers[‘Cookie‘] = cookies headers[‘Accept‘] = "text/html" wb=requests.get(url_save,headers=headers) wb_data=BeautifulSoup(wb.text,‘lxml‘) lis=wb_data.body.select(‘li[class="image-item"]‘) if len(lis) == 0: break f=open(file_path+‘begin‘,"w") f.write(str(page)) f.close() headers[‘Cookie‘]="" headers[‘Accept‘]=‘image/webp,image/*,*/*;q=0.8‘ os.system("cls") print(‘正在下载第%d页...‘%page) for li in lis: if str(li.ul)!=‘None‘: if int(li.ul.i.next_sibling)>=hot: os.system("cls") print(‘正在下载第%d页...‘%page) if li.a[‘class‘][2]==‘multiple‘: getpic(li.img[‘src‘],‘http://www.pixiv.net‘+li.a[‘href‘],‘mul‘) else: getpic(li.img[‘src‘],‘http://www.pixiv.net‘+li.a[‘href‘])
这个和上面那个差不多,接受三个参数,标签名,热度(数字),路径,就可以保存图片到路径下,名为标签+热度的文件夹里了,下载速度一般不乐观,主要取决于热度,热度越大,速度越快。
因为不是多线程下载,所有速度不乐观,而且这两段代码我都设定了60秒的request超时限制,所以有可能出现代码动不动就崩溃的现象,一般来说这时只要继续重新运行就好,但是手动点好麻烦,于是,我就写了两个windowsbat来作为程序接口调用他们。通过接受返回码来确定下载是否完成,没完成的话就重新调用程序,所以说脚本真是解放生产力啊,但是还有一个问题,就是程序要支持断点续传,对特辑而言,页面的请求只有一次,且图片数量少,于是只要在下载request每张图之前判断图片是否存在就好,(因为主要时间都消耗在了request和图片写入上,特别是前者),但是对于tag的下载,request页面就不止一次,于是很有必要保存最后一次下载页面的页数。
这是两个脚本
特辑:
@echo off if not exist Picture md Picture echo 请选择 echo 1 : 打开每日特辑 echo 2 : 输入特辑号 set /p choose= if not %choose% == 1 goto else start http://spotlight.pics/zh :else echo 请输入特辑号 set /p number= :excuteagain cls python getpic.py %number% %cd% if not %errorlevel%==0 goto excuteagain echo 是否继续下载?输入‘c’继续或输入‘q’退出 set/p continue= cls if not %continue%==q goto else
@echo off if not exist Tag md Tag echo 请输入标签 set /p tag= echo 请输入热度(数字) set /p hot= :loop python Tag.py %tag% %hot% %cd% if not %errorlevel%==0 goto loop del F:\Tag\%tag%%hot%\begin
嗯,以上就是全部了,最后说一下,python真的是一门不错的语言,简单,强大,易懂,完美地体现脚本语言的魅力,我觉得非计算机人员比如所各个高校的非计算机专业学生,在学编程时不应该学c,打击积极性,而且学了就忘,应该学python才对。