目录
前因
需求分析
1、网盘目录结构
2、大致分析一下包含的文件类型
3、分析文件如何下载
代码实现
1、伪代码编写
1.1、文件目录递归伪代码
1.2、文件下载伪代码
2、请求参数
2.1、目录获取的请求参数
2.2、文件下载的请求参数
3、代码实现
问题分析
前因
先说一下前因,女朋友刚接了一个尽调项目,客户发给了她一堆尽调材料,结果我一看,好家伙,东西还不少,而且更可气的是,这网盘禁止下载,网页右键都被禁用了,所以另存的路子也行不通。
女朋友问我,有没有办法下载下来?
我当然说有,毕竟...男人不能说自己不行!
下面分析一下需求开干!
需求分析
1、网盘目录结构
首先尽调材料包含两个目录
每级目录向下还有多级目录
这个时候,如果我们想拿到所有的目录和目录文件,需要用到的最好的方式就是递归了。记下来,重点!!!
2、大致分析一下包含的文件类型
先随便打开几个目录,会看到存在最多的是.docx .xlsx .pdf和图片格式的文件,这基本上为我们写爬虫提供了有限的困难,因为类型越多,可能面临的爬取的困难就越大。
3、分析文件如何下载
我们首先随便打开一个文件,例如打开图片3第一个pdf,通过浏览器抓包工具分析该文件的实际地址。通过分析,可以看到可能有用的包为以下几个,我们逐个分析
点开第一个listdir请求,查看返回值,会发现是一个json数据,返回的其实是图片3的目录结构,那第一个请求就PASS
接着再分析第二个请求,看到previewoss,基本就有种预感,这个和文件内容有关系。
该请求同样返回的是一个json,键值包括size和url。那现在大胆猜测一下,这个就是文件真实的调用地址。或者我们可以计算一下size对应的文件大小是 4717682Bytes = 4.49Mb ,和图片3第一个pdf大小一致,进一步确定这里的url是对我们有用的,然后我们复制这个url粘贴到浏览器搜索框,发现下载下来了一个没有后缀名的文件。当我把这个文件添加上.pdf后缀后,可以正常打开,并显示和网页一致。
到这里我们就可以开始写代码开始爬虫了,当然也可以把其他类型的文件也一起分析完,然后再写。但是我已经迫不及待了,我喜欢边写边调(别学我)。
代码实现
代码实现之前,我们要先构思一下代码结构,我们要爬取所有的目录文件,那边首先要把递归算法写出来,先定位到各个文件才能进行落地。然后就是对文件的下载代码。
1、伪代码编写
1.1、文件目录递归伪代码
def get_file_dir(主目录):
import requests
目录结构 = 请求获取主目录下目录结构
if 文件类型 in 目录结构:
下载文件
if 目录结构中目录个数 == 0:
pass
else:
# 循环遍历目录
for 目录 in 目录结构:
get_file_path(目录)
1.2、文件下载伪代码
def get_file_detail(file_path):
import requests
resp = 通过requests向file_path发请求
with open(文件保存地址,'wb') as f:
f.write(resp.content)
def get_file_dir(主目录):
import requests
目录结构 = 请求获取主目录下目录结构
if 文件类型 in 目录结构:
下载文件
if 目录结构中目录个数 == 0:
pass
else:
# 循环遍历目录
for 目录 in 目录结构:
get_file_path(目录)
1.2、文件下载伪代码
def get_file_detail(file_path):
import requests
resp = 通过requests向file_path发请求
with open(文件保存地址,'wb') as f:
f.write(resp.content)
按照我们分析文件下载的实际情况来看,其实第一次拿到的文件地址只是我们预览地址,实际地址是存放在previewoss的response中,所以需要进一步完善文件下载伪代码。
def get_file_detail(file_path): import requests resp = 通过requests向file_path发请求 文件实际引用地址 = resp.json()['url'] file_resp = 通过requests向文件实际引用地址发请求 with open(文件保存地址,'wb') as f: f.write(file_resp.content)
2、请求参数
2.1、目录获取的请求参数
我们需求分析时已经得知listdir的请求是获取文件目录,那我们直接就去看listdir请求的请求参数。
请求类型为post请求
这里需要注意请求参数是个dict,我们请求传参时要处理为dict类型 。请求头就不说了,大家都会。
2.2、文件下载的请求参数
同样,我们已经在数据分析中知道了previewoss请求对应的是获取文件真实地址,而文件真实地址只需要直接请求就可以下载,所以现在只需要分析previewoss的请求参数。
注:实际下载过程中遇到了不同文件请求参数不一致的情况,所以还是需要对每种类型文件进行详尽分析,避免下载出问题。
请求还是post请求
请求参数和2.1一致,为dict类型
查看两个请求后我们发现,参数的变化只是docid的不同,那么docid从哪来呢?
这个问题很简单,我们需求分析的时候listdir请求的response的每个列表元素中都有docid的键值,通过比对,其实是一致的!其他键值例如rev在其他类型文件的下载请求参数中是有用的。
3、代码实现
都分析完之后,我们最终的代码实现是这样的。
import json
import os
import requests
headers = {
'cookie': 'xxxx', # cookie 参数不变
'Host': 'xxxx',
'Origin': 'xxxx',
'Referer': 'xxxx',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36',
}
def get_file_dir(url, docid, rootpath='att'):
if not os.path.exists(rootpath):
os.mkdir(rootpath)
data = {
'attr': 'false',
'by': "name",
'docid': docid,
'link': "xxxxx",
'password': "xxxx", # password为网盘分享密码
'sort': "asc", }
resp = requests.post(url, headers=headers, data=json.dumps(data))
dir_list = resp.json()['dirs'] # 目录结构
file_list = resp.json()['files'] # 文件结构
for file in file_list:
# 下载.DOCX 和 .XLSX
if file['name'].endswith('.xlsx') or file['name'].endswith('.docx') :
file_url = 'http://xxxxxx/api/v1/link?method=previewoss'
get_file_detail(file_url, rootpath, file['docid'], file['name'])
# 下载图片
elif file['name'].endswith('.png') or file['name'].endswith('.jpg') :
pic_url = 'http://xxxxxx/api/v1/link'
get_pic_file(pic_url, rootpath, file['docid'], file['name'],file['rev'])
else:
# 下载pdf
file_url = 'http://xxxxxxx/api/v1/link?method=previewoss'
get_file_detail(file_url, rootpath, file['docid'], file['name'])
if len(dir_list) == 0:
pass
else:
for dir in dir_list:
new_docid = dir['docid']
path = dir['name']
get_file_dir(url, new_docid, os.path.join(rootpath, path))
def get_file_detail(file_url, path, docid, docname):
if docname.endswith('.xlsx') or docname.endswith('.docx'):
data = {
'docid': docid,
'link': "xxxxxx",
'password': "xxxx",
'reqhost': "xxxxxxxxxxxx",
'usehttps': 'false',}
docname = docname.split('.')[0]+'.pdf' # 此处做问题分析
elif docname.endswith('.pdf'):
data = {
'attr': 'false',
'by': "name",
'docid': docid,
'link': "xxxxxx",
'password': "xxxx",
'sort': "asc", }
try:
resp = requests.post(file_url, headers=headers, data=json.dumps(data))
print(resp.json()['url'])
file_real = resp.json()['url']
file_resp = requests.get(file_real)
with open(os.path.join(path, docname), 'wb') as f:
f.write(file_resp.content)
print(os.path.join(path, docname), '下载成功!!')
except Exception as e:
print(e)
print(path,docname,'下载失败!!')
def get_pic_file(file_url, path, docid, docname,rev):
data = {
'method': 'thumbnail',
'rev': rev,
'docid': docid,
'width': 10000000000,
'height': 10000000000,
'quality': 50,
'link': "xxxxx",
'password': "xxxx",
'nostatistic': 'true'
}
try:
resp = requests.get(file_url, headers=headers, params=data)
with open(os.path.join(path, docname), 'wb') as f:
f.write(resp.content)
print(os.path.join(path, docname), '下载成功!!')
except Exception as e:
print(e)
print(path,docname,'下载失败!!')
if __name__ == '__main__':
url = 'http://xxxxxxx/api/v1/link?method=listdir'
docid = "xxxx"
get_file_dir(url, docid)
问题分析
本次实战中遇到了几个问题,有的是需求分析未详尽,有的是网站的设置迷惑了爬虫导致的。现在做总结。
1、图片开始未能爬下来
图片格式的文件和pdf,word,excel的请求方式不同,从代码就可以看出来,请求一次直接落地就可以了。还是需要好好做需求分析啊!
2、word,excel 下载后无法打开
word打开报错“发现无法读取的内容”。这个问题困扰了我很久,一直没办法查明原因,后来查原因是说文件后缀名有问题,但是文件的命名又是完全按照网盘的命名,不会存在问题。百思不得解。
后来又详细看了看请求,发现一个response中有PDF标识,尝试着把.docx改成.pdf后,发现文件打开了!!好坑!!
代码里把word,excel文件名全部处理成.pdf。
docname = docname.split('.')[0]+'.pdf'
重新执行爬虫,发现文件都保存下来了!
好了结束。
要去找女朋友请赏了!



