蓝奏云批量下载 with urllib3 — Python39


万能的群友经常会发蓝色链接,其中有一些蓝色的蓝奏云,看见那么多学习资料实在是馋得很,必须全部拿下。
话不多说,直接开始。



0.引用包

我这里用到了urllib3,现在的主流还是requests和urllib,各有各的特点,就目前我所需要的工具来说urllib3可以向下兼容,但相对requests来说缺少cookie管理。

import urllib3				# 主角
from lxml import etree		# 用于获取xpath
import re					# xpath取不到的地方需要正则
import json					# 用于将js字典转换为python字典
import os					# 用于获得系统路径



1.分析

需要解析的URL如下图:
在这里插入图片描述
我们先用GET方法请求一下

# 实例化产生请求对象
http = urllib3.PoolManager(timeout=4.0)
# 构造请求包
url = '这里是需要解析的URL地址'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.3',
    'referer': url
}
# get请求指定网址
resp = http.request('GET', url, headers=headers)
# 编码/解码
html = resp.data.decode('utf-8')

获得了该网页的源代码并保存到html中,现在很多网页都是这种动态式的,一堆数据发下来让你的浏览器自行处理,现在我们找到主要目标 下载链接 的构建函数:
在这里插入图片描述
对照一下,以上代码的执行效果如下:
在这里插入图片描述
现在回到函数,整个函数是通过msg的参数来运行的:
在这里插入图片描述
那么msg来自哪里呢,通过(初步)学习JS以及(立志)多年爬虫经验,可以知道,这个ajax方法通过post到filemoreajax.php所获取的包作为msg传递给函数,然后函数将数据整理好呈现出来。知道了这个行为,我们就可以跳过浏览器直接去这个地址拿数据。
在这里插入图片描述



2.过程

接下来对POST工作做一下前期铺垫:
首先需要构造data,上面的data里面有三个值(pgs / ibhkhh / ihfwja)为变量,可以从script标签开头找到,fid和uid虽然不是变量,但每个网页肯定会变,也要提取。
在这里插入图片描述
其余使用re进行提取,并构造包体,这里的 ‘t’ 和 ‘k’ 的变量名都是6位随机数,因此提取6位数开头的分号中的数据,pgs是页码,我们可以手动改,不用提取。

# 用re提取参数
title = re.findall(r'<title>([\w\W]+?)</title>', html, re.MULTILINE)	# 提取文件名,非post参数
# pgs = re.findall(r'pgs =([\d]*);', html, re.MULTILINE)
fid = re.findall(r'\'fid\':([\w]+?),', html, re.MULTILINE)
uid = re.findall(r'\'uid\':\'([\w]+?)\',', html, re.MULTILINE)
params = re.findall(r'var [\w]{6} = \'([\w]+?)\';', html, re.MULTILINE)
# 构造fields
fields = {
    'lx': 2,
    'fid': int(fid[0]),
    'uid': uid[0],
    'pg': 1,
    'rep': '0',
    't': params[0],
    'k': params[1],
    'up': 1,
}

铺垫完成,开始进行POST,转个码,然后转换为python格式的字典:

# post文件目录接口
indexApi = 'https://www.lanzoui.com/filemoreajax.php'
indexRes = http.request('POST', indexApi, headers=headers, fields=fields)
indexData = indexRes.data.decode('unicode_escape')
# 转换为python字典
index = json.loads(indexData)

整理后的数据存在了index中,‘text’ 里面的 ‘id’ 就是我们需要获取的下载地址
在这里插入图片描述
在这里插入图片描述
可以列个表,更加直观:

# 列出文件
print(title[0])
print('链接\t\t文件名\t\t大小\t\t时间')
for i in range(len(index['text'])):
    print(index['text'][i]['id'], end='\t\t')
    print(index['text'][i]['name_all'], end='\t\t')
    print(index['text'][i]['size'], end='\t\t')
    print(index['text'][i]['time'])

在这里插入图片描述
项目已经有了重大进展,但还没有完成,以最上面的链接为例:
在这里插入图片描述
可以发现三个按钮用的是同一个链接XD,这个链接才是下载地址,用xpath取出来GET一下(注意,GET的时候headers要带上accept-language参数,不然不给过,这个就很奇怪了)

downUrl = 'https://www.lanzoui.com/%s' % ibkvJtkvykj
downRes = http.request('GET', downUrl, headers=headers)
# 编码/解码
downData = downRes.data.decode('utf-8')
# 将data数据作为HTML选择器
selector = etree.HTML(downData)
# 下载按钮用的是iframe,所以还要再get一次真正的下载地址
downLink = selector.xpath('/html/body/div[3]/div[2]/div[4]/iframe/@src')[0]
# 请求iframe原链接
url2 = 'https://www.lanzoui.com%s' % downLink
headers2 = {
   'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
   'Accept-Language': 'zh-CN,zh;q=0.9',
   'referer': url2
}
res2 = http.request('GET', url2, headers=headers2)
data2 = res2.data.decode('utf-8')

从data2中可以看到,下载连接就在ajaxm.php中,老样子构建并POST
在这里插入图片描述

# sign参数会改变,要先得到sign
sign = re.findall(r'\'sign\':([\w]+?),\'ves\'', data2, re.MULTILINE)[0]
ajaxdata = re.findall(r'var ajaxdata = \'(\??[\w]+?)\';', data2, re.MULTILINE)[0]
postsign = re.findall(r'var %s = \'([\w]+?)\';' % sign, data2, re.MULTILINE)[0]
websignkey = re.findall(r'\'websignkey\':\'([\w]+?)\'', data2, re.MULTILINE)[0]
# 构建fields
fields2 = {
    'action':'downprocess',
    'signs':ajaxdata,
    'sign':postsign,
    'ves':1,
    'websign':'',
    'websignkey':websignkey 
    }
# 这次POST得到真正的下载链接
realUrl = 'https://www.lanzoui.com/ajaxm.php'
realRes = http.request('POST', realUrl, headers=headers2, fields=fields2)
realData = realRes.data.decode('utf-8')
realLink = json.loads(realData)

将dom与url组装起来就是真正的下载地址了
在这里插入图片描述

# 组装链接
downloadLink = realLink['dom'] + '/file/' + realLink['url']
# 然后GET
fin = http.request('GET', downloadLink, headers=headers2)

现在得到了针对浏览器的下载链接,如果要进一步偷懒则需要一点加工
这里的data实际上就是二进制流,可以直接储存为文件
在这里插入图片描述

# 先获取当前py文件位置
cd = os.getcwd()
# 如果当前位置没有该文件夹则创建一个
if not os.path.exists(title[0]) :
    os.mkdir(cd + '\\' + title[0])
# 创建 当前位置=>标题=>文件名 然后将获取的二进制数据写入
with open('%s\\%s\\%s' % (cd, title[0], index['text'][0]['name_all']), "wb") as file:
    file.write(fin.data)
    print('已下载\t%s' % index['text'][0]['name_all'])



3.整理

接下来进入最后一步,写一个循环进行批量下载。循环就简单了,把单个下载线程括起来变量改掉就完事了。
在这里插入图片描述
跑一下
在这里插入图片描述
alright,结束战斗
在这里插入图片描述

4.完整代码

import urllib3
from lxml import etree
import re
import json
import os

def getIndex(lanzoui):
    global title
    global index
    global headers
    url = lanzoui
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.3',
        'referer': url
    }
    resp = http.request('GET', url, headers=headers)
    html = resp.data.decode('utf-8')
    # pgs = re.findall(r'pgs =([\d]*);', html, re.MULTILINE) 提取出来没有用,我们手动去翻页
    title = re.findall(r'<title>([\w\W]+?)</title>', html, re.MULTILINE)
    fid = re.findall(r'\'fid\':([\w]+?),', html, re.MULTILINE)
    uid = re.findall(r'\'uid\':\'([\w]+?)\',', html, re.MULTILINE)
    params = re.findall(r'var [\w]{6} = \'([\w]+?)\';', html, re.MULTILINE)
    fields = {
        'lx': 2,
        'fid': int(fid[0]),
        'uid': uid[0],
        'pg': 1,
        'rep': '0',
        't': params[0],
        'k': params[1],
        'up': 1,
    }
    indexApi = 'https://www.lanzoui.com/filemoreajax.php'
    indexRes = http.request('POST', indexApi, headers=headers, fields=fields)
    indexData = indexRes.data.decode('unicode_escape')
    index = json.loads(indexData)
    print(title[0])
    print('链接\t\t文件名\t\t大小\t\t时间')
    for i in range(len(index['text'])):
        print(index['text'][i]['id'], end='\t\t')
        print(index['text'][i]['name_all'], end='\t\t')
        print(index['text'][i]['size'], end='\t\t')
        print(index['text'][i]['time'])

def downFiles():
    for i in range(len(index['text'])):
        downUrl = 'https://www.lanzoui.com/%s' % index['text'][i]['id']
        downRes = http.request('GET', downUrl, headers=headers)
        downData = downRes.data.decode('utf-8')
        selector = etree.HTML(downData)
        downLink = selector.xpath('/html/body/div[3]/div[2]/div[4]/iframe/@src')[0]
        url2 = 'https://www.lanzoui.com%s' % downLink
        headers2 = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'referer': url2
        }
        res2 = http.request('GET', url2, headers=headers2)
        data2 = res2.data.decode('utf-8')
        sign = re.findall(r'\'sign\':([\w]+?),', data2, re.MULTILINE)[0]
        ajaxdata = re.findall(r'var ajaxdata = \'(\??[\w]+?)\';', data2, re.MULTILINE)[0]
        postsign = re.findall(r'var %s = \'([\w]+?)\';' % sign, data2, re.MULTILINE)[0]
        websignkey = re.findall(r'\'websignkey\':\'([\w]+?)\'', data2, re.MULTILINE)[0]
        fields2 = {
            'action':'downprocess',
            'signs':ajaxdata,
            'sign':postsign,
            'ves':1,
            'websign':'',
            'websignkey':websignkey
            }
        realUrl = 'https://www.lanzoui.com/ajaxm.php'
        realRes = http.request('POST', realUrl, headers=headers2, fields=fields2)
        realData = realRes.data.decode('utf-8')
        realLink = json.loads(realData)
        downloadLink = realLink['dom'] + '/file/' + realLink['url']
        fin = http.request('GET', downloadLink, headers=headers2)
        cd = os.getcwd()
        if not os.path.exists(title[0]):
            os.mkdir(cd + '\\' + title[0])
        with open('%s\\%s\\%s' % (cd, title[0], index['text'][i]['name_all']), "wb") as file:
            file.write(fin.data)
            print('已下载\t%s' % index['text'][i]['name_all'])
    print('已完成所有下载')

http = urllib3.PoolManager(timeout=4.0)

if __name__ == '__main__':
    getIndex('https://www.lanzoui.com/xxxxxxx')	# 这里是网址
    downFiles()		# 这里是下载函数,可以考虑做一个中断控制,我用不到就不做了


# -*- coding: utf-8 -*-
# @Description 	: 蓝奏云是个好网站,下载请不要泛滥,良好的互联网环境需要我们共同来维护
# @Date 		: 2021/09/10
# Copyleft © 2021 Ursa
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值