本帖最后由 pinhai 于 2020-2-26 23:13 编辑
一、引子因发现有人悬赏下载淘豆网(taodocs.com)文档,之前看过python下载文泉的文档,就想试试看能不能用python下载淘豆网付费文档。
二、分析 以https://www.taodocs.com/p-55966379.html为例,浏览器为火狐。ctrl+shift+E打开开发工具-网络,刷新该页面,试一下“图像”选项,观察左侧请求,不难发现该页面里面加载的文档都是图片,分辨率810*1146,至此确定下载思路为下载每个页面的图片,最后合成为pdf格式。
三、探索 1.要想下载图片就必须知道图片地址,查看上图所点选的请求,发现消息头为https://file.taodocs.com:808/img/Hj5pnzEjlNUzKb8@8S9qCO38LfhgpL2T,非常简单,前半部分为服务器地址https://file.taodocs.com:808/img/,后面为一个字符串,查看图片地址问题简化为查找字符串生成方式。多次打开该页面,发现针对同一个页面,该字符串是固定不变的,更进一步简化了探索过程。通过调试器查找该字符串,没有结果,也正常,如果能在源码中找到该字符串,这个问题也过于简单了。
2.既然在源码中没有,那应该是从服务器动态获取的,重新翻看各个请求,发现这样一个请求
https://file.taodocs.com:808/?from=pc_55966379&furl=lBk9LJ9uXl6xoMR8lbVBog==&callback=jQuery112405711252286986038_1582691611656&_=158269161165,其响应结果为
[JavaScript] 纯文本查看 复制代码 jQuery112405711252286986038_1582691611656({"imgs":["i8iHNYeTqVJnoWn94KhuiUcGAbR7NkEda7Pz/A2BXR6Klbsjxc6VBFlNSo2v+E+gPbaY0kGjILRUfWGVklmq1Q==","i8iHNYeTqVJnoWn94KhuiUcGAbR7NkEda7Pz/A2BXR5qt+SMkv+qjTc0lEgGxV7hMMSI+gTcr2AiNP8TFOBijg==","i8iHNYeTqVJnoWn94KhuiUcGAbR7NkEda7Pz/A2BXR7A4Dh7vPZP21hM7cvADzC9TKG2FdbWuk9LnmXlFAkXgg=="],"next":"lBk9LJ9uXl59juySZznxSh6QvU@bBUhr","pageNum":10,"ts":28.779500000000002}); 其中imgs标签很可疑,但不是地址,可能经过了加密。翻看代码,发现(其实找了好久)js代码中对imgs变量进行了一个处理,处理函数是EiePQRNA,查看定义为:
[JavaScript] 纯文本查看 复制代码 function EiePQRNA(_0x2abb91) { var _0x53a94f = { 'ZWphd': '**********' }; var _0x2d16f9 = _0x53a94f['ZWphd']; var _0x6048e2 = L8LXdspqSjEiePQRNA['AES']['decrypt'](_0x2abb91, L8LXdspqSjEiePQRNA['enc']['Utf8']['parse'](_0x2d16f9), { 'mode': L8LXdspqSjEiePQRNA['mode']['ECB'], 'padding': L8LXdspqSjEiePQRNA['pad']['Pkcs7'] })['toString'](L8LXdspqSjEiePQRNA['enc']['Utf8']); return _0x6048e2; }
可见加密方式为AES,加密模式为ECB,秘钥也是明文,在此隐去了,没有偏移量,拿到在线解密网站试一下,结果正是我们要找的图片地址。
好了,既然图片地址找到了,我们就要构造请求了,看上述请求发现,55966379容易,就是文档的编号,在网址中可以方便接取,furl是什么?我们需要看一下,重新看上述请求的响应,除了imgs保存了加密过的图片地址外,还有一个next参数,长度和我们furl长度一致,理解一下next,就是下一个的意思,再看imgs中只存了3个地址,可以猜想到网页是分段加载的,next中存储的正是下一个请求的furl参数。这样就串起来了,只要构造出第一个reuqest,后面就可以递归调用了。那么第一个request如何构造呢?
3.继续翻看网络请求,发现第一个涉及获取图片地址的请求和后面的请求别无二致,也有furl参数,那么他的furl从哪里来呢?看到https://www.taodocs.com/showProduct.aspx?fn=docinfo&id=55966379请求比较可疑,他返回了一个参数,叫nextPageStr,将他带入获取图片地址请求中(callback和_参数不管),利用重发功能重新发起请求,发现可得正常响应。
4.至此,我们理顺了网站加载所有文档页面的过程:
①使用https://www.taodocs.com/showProduct.aspx?fn=docinfo&id=55966379获取nextPageStr。
②使用nextpagestr作为furl构造https://file.taodocs.com:808/?from=pc_55966379&furl=lBk9LJ9uXl6xoMR8lbVBog==&callback=jQuery112405711252286986038_1582691611656&_=158269161165获取图片地址imgs和next参数,imgs通过json解包并通过EiePQRNA函数解密后获取图片实际地址下载显示。
③使用next参数作为下一个请求的furl,直到next为空,结束请求。
5.题外话,第一次分析时,该网站的请求图片请求并不是发送到https://file.taodocs.com:808,而是https://view.taodocs.com:808,并且返回的imgs也是明码,没有加密,要更加容易一些,试验代码也是基于https://view.taodocs.com:808的,等我要来写这篇帖子时发现不一样了,黑人问号脸 ??,害我又找了半天。ps,view.taodocs.com:808依然可用,且无需解密。
四,实践
啥也别说了,撸代码(header直接从firefox中复制) [Python] 纯文本查看 复制代码 import requests
import time
import base64
from Crypto.Cipher import AES
headers1 = {
'Host': 'file.taodocs.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Referer': 'https://www.taodocs.com/p-107648150.html',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache',
}
headers2={'Host': 'www.taodocs.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Referer':'https://www.taodocs.com/p-107648150.html',
'Cache-Control': 'max-age=0',
'TE': 'Trailers',}
def getimgs(url):
pid = url.split('-')[1][:-5]#截取pid
print(pid)
response=requests.get('https://www.taodocs.com/showProduct.aspx?fn=docinfo&id=' + pid, headers=headers2)
nextstr = response.json()['nextPageStr']#获取第一个nextstr
title = response.json()['data'][0]['Title']#获取文档标题
print(title)
imglist=[]
i=1
while(nextstr):#循环获取nextstr,直到nextstr为空
url2='https://file.taodocs.com:808/?from=pc_'+pid+'&furl='+nextstr#构造请求
response1 = requests.get(url2, headers=headers1).json()
try:
imglist=response1['imgs']#获取imgs
except:
print("=================================wait===================================")
time.sleep(30)#遇错后延迟,主要是服务器有时间间隔限制
response1 = requests.get(url2, headers=headers1).json()
imglist=response1['imgs']
for img in imglist:
imgjiemi=aes_decode(img, '*********')#解密,这里我把秘钥隐去了
url = 'https:' + imgjiemi
print(url)
response = requests.get(url)
imgbody = response.content
with open('e:/1/' + title+str(i+imglist.index(img) )+ '.jpg', 'wb') as f:
f.write(imgbody)#下载保存,没有合成pdf,主要是累了
i=i+len(imglist)
nextstr=response1['next']
def aes_decode(data, key):#解密函数
try:
aes = AES.new(str.encode(key), AES.MODE_ECB) # 初始化加密器
decrypted_text = aes.decrypt(base64.decodebytes(bytes(data, encoding='utf8'))).decode("utf8") # 解密
decrypted_text = decrypted_text[:-ord(decrypted_text[-1])] # 去除多余补位
except Exception as e:
print(e)
return decrypted_text
urltoStart='https://www.taodocs.com/p-55966379.html'
getimgs(urltoStart)
六、总结
这个帖子是我来到吾爱的第一个帖子,费了不少劲,python也是纯自学,请各位前辈多多鼓励! 写这个帖子一是分享,二是记录学习过程,希望不足之处,各位帮助提高!
|