无聊就玩玩爬虫吧


虽然期末考试有点坎坷(永远记不住的近代史,裸考概念全不会的C语言,复完就忘得退学分析),怎么说也到了寒假,多少有点时间去学点有意思的东西(预习?预习是不可能的)。上个月底摸了下python记下语法就溜了,然后看到班上的巨佬写得出神入化就想着回来看看爬虫(才不是为了涩图)

63740442_p0.jpg

二仪式镇楼(原图: https://i.loli.net/2020/01/15/xOJHqAafWXik3D6.png

爬虫简介

爬虫,wiki上说是一种用来自动浏览万维网的网络机器人 。个人摸索敲代码爬了一些东西,其实感觉就是一个脚本,模仿用户浏览网站,直接获取网页代码或者和服务器交互,从而得到自己要的资源变色,所以实际上只要会点HTML,JS和C/S的知识,利用下python的几个函数就好。

实践

其一

“有些人看着看着就打开了浏览器”,我当然也有几个小网站,毕竟b站也不是全部番买(18R?怎么可能,我可是良民)。所以就你了 http://www.nicotv.me/ !要上面的云盘链接和提取码就好。

随手点开一部,一眼可以网址的规律,没错,就是数字…

TIM图片20200115210827.png

手动去试了试,1到11962都是有效的(虽然后来知道了只有一百来个是有云盘链接的…)。打开F12看看链接对应的标签就可以开始动手写了,懒人就写一个网页的就好(只要搞掂一个加个循环就好,才不是懒)

TIM图片20200115211458.png

Ready perfect!(慎勇语气~~)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding: utf-8 -*-
#Spyder(Python 3.7)
# 引入获取网页源代码的模块
import requests
#re模块用来筛选源代码
import re

index=57836
url="http://www.nicotv.club/video/detail/{}.html".format(index)
html=requests.get(url)
tree=html.text
#用findall和正则表达式筛选链接和提取码
link=re.findall(r"href=\"https://pan.baidu.com/s/(.+?)\"",tree)
num=re.findall(r"提取码:(.+?)</a></li>",tree)
#findall 返回的list变成string
link=link[0]
num=num[0]
#将link重新拼接一下
link="https://pan.baidu.com/s/"+link
#然后print或者open()一个txt保存也行
print(link)
print("提取码:"+num)

后面试了下发现真的有链接的网页比较少,加上要储存在txt,于是就多获取了个标题和”百度网盘下载”选项卡,如果选项卡的list为空就跳过。

其二

云盘资源岂能满足我的※趣!我身体很好,涩图都给我.jpg。然后就对某p站的镜像站下手了,好吧,其实就是不想开粉色小飞机去p站。( https://pixivic.com/

输入关键词FGO搜索(老子也是マスター),看网址:

TIM图片20200115215204.png

像上面一样用requests获取网页的源代码,发现只是一个框架,并没有任何图片的相关信息,所以应该是动态加载的。打开万能的F12,点进几个JS,在其中一个发现了加载图片的部分。

1
2
3
4
5
6
7
8
9
url = 'https://api.pixivic.com';
//....
function init() {
ajax("get", url + "/illustrations", "page=" + page + "&keyword=" + keyword, showlist, true);
}
//...
function ajax(method, url, data, callback, flag) {
//....
}

就是用AJAX的方式进行交互,提交的方式是GET,参数有页数page和搜索的关键词keyword。继续解析这个showlist函数,应该就是用来展示图片的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function showlist(data) {
var str = JSON.parse(data).data.illustrations,
elem = '',
imgH = 0,
content = document.getElementById("waterfall"),
minIndex;
var originalUrl, largeUrl,
pre ="https://img.cheerfun.dev:23334/get/",originalPre="https://bigimg.cheerfun.dev/get/";
if (str.length > 0) {
str.forEach(function (item, index) {
var oDiv = document.createElement("div");
oDiv.className = "box";
imgH = Math.ceil(228 * item.height / item.width);

//url = item.meta_single_page.original_image_url;
originalUrl = item.imageUrls[0].original;
largeUrl = item.imageUrls[0].large.replace('_webp', '');
elem = '<a href="' + originalPre + originalUrl + '" alt ="' + item.title + '" class="image" rel="https://www.pixiv.net/member_illust.php?mode=medium&illust_id=' + item.id + '">\<img src ="' + pre + largeUrl + '" height ="' + imgH + '" width="228" alt="' + item.title + '">\</a>';
elem += item.title.length == 0 ? "" : '<p>' + item.title + '</p>';
});
//...
}
}

其中出现的两个参数pre和originalPre指向的网页路径就很有意思,应该是实现镜像站的功能的,也不管它了,多个字符串组合后形成一个新的链接,然后elem的href使用了,那么应该就是图片的本体的链接。再回到永远滴神F12的Network里抓个JSON包:

TIM图片20200115221830.png

发现了一个天坑级别的包(虽然跟着上面的函数可以找到组合前链接的位置,但是真的深…)l链接有4个,p站老油条应该一眼就看出最后一个original是原图(写着英文不是废话吗!!!),试了下直接打开这个链接:

https://i.pximg.net/img-original/img/2016/07/09/00/10/10/57808612_p0.jpg

结果吃了一个403,然后学着上面的函数组合了下变成:

https://bigimg.cheerfun.dev/get/https://i.pximg.net/img-original/img/2016/07/09/00/10/10/57808612_p0.jpg

2.jpg

黑贞老婆出现了(想桃子)!所以下载图片时组合下链接就好。

综合一下:模仿ajax获取json包,然后拿original链接并拼接,最后下载就好。就写两个函数,一个得到可下载链接,一个下载。

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
# -*- coding: utf-8 -*-
import requests
import os
from urllib.request import urlretrieve
from urllib.parse import quote

keyword='FGO'
pre = "https://bigimg.cheerfun.dev/get/"
file_path='./img/'+keyword
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
}

#得到keyword的第i+1页,j+1张图的可下载链接 一页30张
def getLink(i,j,keyword):
#keyword可能是中文,所以就用quote进行了urlencode
url='https://api.pixivic.com/illustrations?keyword={}&page={}'.format(quote(keyword),i)
response=requests.get(url,headers)
ctx=response.json()
#print(ctx)
link=ctx['data']['illustrations'][j]['imageUrls'][0]['original']
return pre+link

#保存图片
def saveImg(link,file_path):
try:
if not os.path.exists(file_path):
os.makedirs(file_path)
file_name=file_path+'/'+(link.split('/'))[-1]
urlretrieve(link,file_name)
print('---下载成功')
except IOError as e:
print('---文件操作失败:',e)
except Exception as e:
print('---错误:',e)

#获取5*30张
for i in range(5):
for j in range(30):
print('正在处理第{}页,第{}张图...'.format(i+1,j+1))
link=getLink(i+1,j,keyword)
saveImg(link,file_path)
print('处理完成')

因为可以开着然后开LOL,打完基本上就下完了,所以也不弄什么多线程了!(也许某种意义上这也是双线程??)。获取链接没有问题,所以连一开始判断链接为空的部分都删了,反而是一开始下载老是出错,下载出错大多数图片下载不完整,想着检验重下就好。对urlretrieve()谷歌了之后发现是可以加个回调函数的,如下:

1
2
3
4
5
6
7
def callback(a,b,c):
'''回调函数
@a: 已经下载的数据块
@b: 数据块的大小
@c: 远程文件的大小
'''
urllib.request.urlretrieve(url, filename, callbak)

用回调函数的第三个参数就可以得到原始文件大小,然后用:

1
fsize = os.path.getsize(filePath)

获取已下载文件大小去对比,如果不对的话就重新下载。但是很麻烦对吧!!于是try…except…大法好,经过多次尝试发现就算是重新下载,一般也很难下载成功,所以干脆抛出错误之后连重新下载也不搞了(只要下的图够多,就可以把损失降低???)

然后?就没然后了,只是磁盘满了而已(笑~)(借一部私聊?当然没问题)

后语

其实个人不懂python,就连一些语法点也要回去复习,不过靠着谷歌多多少少把功能实现了,至于账号密码登录之类的还没有去碰,因为对C/S其实也没有完全搞清楚,靠谷歌肯定还是能做的,但是没多大意思。

寒假来了,老老实实投了份简历,最后没有被寒假的CTF训练营录取,自己安慰自己说要预习数据结构,要学Java,学更多东西去追上别人,还是有点心灰意冷吧。不过,现在的我还有热情,那就去学吧,也许自学学的零零散散,不像课堂一样系统,但终究是自己的力量。


4-7 补充

大佬的Pixivic迎来了一次大更新,前后端也开源了,后端用的Java,前端用了Vue.js,对做类似项目比较有参考价值,github地址网站上也写着了(传送门:前端 后端)。

因为更新了,所以上面请求和拼接链接的方式也有些改变。

https://api.pixivic.com/illustrations?keyword=fgo&page=1&pageSize=30 没有太大变化,更多参数和api可以查看后端文档,然后得到的origin地址去掉 https://i.pximg.net/ 然后把后面的接在 https://original.img.cheerfun.dev/ 后即可(有时候会报403,原因是headers的问题),可以这样解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
import os
import urllib
headers_2 = [
('referer','https://pixivic.com/illusts/'),
('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36'),
('accept','text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9')]
#...
opener = urllib.request.build_opener()
opener.addheaders = headers_2
data = opener.open(link)
f = open(file_name,'wb')
f.write(data.read())
f.close()

主要就是给urllib加个headers,注意必须包括referer。

9-8补充

Pixivic再次更新,需要登录才能查看图片,虽然可以模拟登录继续爬虫,但是还是算了吧