python爬虫:新手爬取NASA每日精选图片

作为一位科幻爱好者,初学python后,便想要使用python爬虫爬些漂亮的宇宙图片,于是乎就把目标瞄准了NASA的每日精选图片。

不过话说回来,毕竟NASA这样的官方组织我们还是要看看是否允许爬虫访问,我们先来看看:https://www.nasa.gov/robots.txt

robots.txt:
#Robots.txt file from http://www.nasa.gov
#All robots will spider the domain

User-agent: *
Disallow: /worldbook/
Disallow: /offices/oce/llis/

好的,那么看来我们爬起来并不违反人家的规则。
爬取的是每日图片,地址为:
https://www.nasa.gov/multimedia/imagegallery/iotd.html
查看网页源代码发现是使用的是json传输的数据,又发现请求参数中(params),‘size’:后面对应的数字对应传输的图片地址数量。那么根据此我们就可以开始写代码啦!

另外,在写代码及我自己运行的过程中,毕竟NASA服务器在境外,所以链接起来往往会出现问题,不管是链接还是下载过程中,都容易出现各种:ReadTimeout ,又或者是其他time out 报错,所以我基本上每一步过程中都加入了若报错就重复请求的代码。(重复请求次数我都是设置的5次)

好接下来开始看代码:

# 需要用到的几个模块
import os
import requests
import sys
from tqdm import tqdm
# 主服务器请求函数
def time_try(url_try,params):
    try:
        response = requests.get(url_try,headers=headers,timeout=3,params=params)
    except :
        for i in range(1, 7):
            if i == 6:
                print('请求链接失败,请查看网络连接并重启程序进行重试。')
                os.system('pause')
                sys.exit()
            else:
                try:
                    print('请求链接超时,第{}次重复请求'.format(i))
                    response = requests.get(url_try,headers=headers,timeout=3,params=params)
                    break
                except:
                    continue
    return response 

接下来这一步是整个下载过程的函数,我把其中的每一步都进行了拆分写小函数,便于接下来学习到新知识可以很容易的进行增加或者删减功能。
整个下载过程是可以实现断点续传,下载到一半链接异常也可以重新进行请求且接着刚刚下载的文件继续进行下载。
当然,为了好看,我们怎么少得了显示下载进度条的功能呢~
进度条是使用tqdm模块实现的

# 获取目标文件大小
def getfile_size(url):
    global file_size
    requests.packages.urllib3.disable_warnings()
    try:
        file_size = int(requests.get(url, timeout=5, stream=True, verify=False, headers=headers).headers['Content-Length'])
        return file_size
    except:
        for con in range(1,7):
            if con == 6:
                print('请求文件失败,请查看网络连接并重启程序进行重试。')
                os.system('pause')
                sys.exit()
            else:
                try:
                    print('请求文件超时,第{}次重新尝试'.format(con))
                    file_size = int(requests.get(url, timeout=5, stream=True, verify=False, headers=headers).headers['Content-Length'])
                    return file_size
                except :
                    continue

# 读取本地文件大小
def getfirst_byte(url,dst):
    global first_byte
    if os.path.exists(dst):
        first_byte = os.path.getsize(dst)
    else:
        first_byte = 0
    return first_byte

# 实现下载进度条
def rate(url,dst,header):
    global pbar
    req = requests.get(url, timeout=5, stream=True, verify=False, headers=header)
    pbar = tqdm(
        total=file_size, initial=first_byte,
        unit='B', unit_scale=True, desc='爬取第{}张图片:'.format(pic_dl,),ascii=True)    
    with(open(dst, 'ab')) as f:
        for chunk in req.iter_content(chunk_size=1024):
            if chunk:
                f.write(chunk)
                pbar.update(1024)
    pbar.close()
    return pbar

# 下载链接中断重试
def retry(url,dst):
    for o in range(1,7):
        if o == 6:
            pbar.close()
            goon = noerror('{}\n网络异常,是否继续爬取?\n1、跳过此图片继续爬取\n2、退出程序\n请输入选项1或者2:'.format('-'*30),1,3)
            if goon == 1:
                print('-'*30)
                os.remove(dst) 
                return o
            else:
                os.remove(dst) 
                sys.exit()
        else:
            try:
                pbar.close()
                print('请求下载异常,第{}次重新请求'.format(o))
                getfirst_byte(url,dst)
                header = {
     
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 Edg/79.0.309.58',
                    "Range": "bytes={}-{}".format(first_byte, file_size)
                    }
                rate(url,dst,header)
                return first_byte
            except:
                continue  

# 整个下载流程
def download(url, dst):
    getfile_size(url)
    getfirst_byte(url,dst)
    if first_byte >= file_size:
        print('发现已经完成的图片{},\n执行下一张图片请求……'.format(url.split('/')[-1]))
        return first_byte
    header = {
     
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 Edg/79.0.309.58',
        "Range": "bytes={}-{}".format(first_byte, file_size)
        }
    try:
        rate(url,dst,header)
    except:
        retry(url,dst)

还有两个方便的小函数

# 创建下载文件夹目录
def download_path(folder_name):
    folder = '/{}/'.format(folder_name)
    path = os.getcwd() + folder
    if not os.path.exists(path):
        os.makedirs(path)
    return folder_name

#防止输入报错
def noerror(question,range1,range2):
    while True:
        many=input(question)
        if many in str(list(range(range1,range2))):
            number=int(many)
            break
        else:
            print('输入有误,请重新输入!')
            continue
    return number

实际上以上的函数是可以适用于任何类似网站的文件下载,下面这部分只是针对NASA网页进行的一个分析,只要最后提取到目标文件的url,以上代码基本可以通用。
以上函数,url代表目标文件url。dst代表要下载到本地的路径。
最后一段有些乱的网页分析过程:

if __name__ == "__main__":
    url_main = 'https://www.nasa.gov/sites/default/files/'
    url = 'https://www.nasa.gov/api/2/ubernode/_search'
    headers = {
     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36 Edg/79.0.309.58'}
    page = noerror('请问你想下载NASA多少张最新精选图片(不超过999张)?:',1,1000)
    pic_dl = 0
    params = {
     
    'size': str(page),
    'from': '0',
    'sort': 'promo-date-time:desc',
    'q': '((ubernode-type:image) AND (routes:1446))',
    '_source_include': 'promo-date-time,master-image,nid,title,topics,missions,collections,other-tags,ubernode-type,primary-tag,secondary-tag,cardfeed-title,type,collection-asset-link,link-or-attachment,pr-leader-sentence,image-feature-caption,attachments,uri',
    }
    print('{}\n正在链接NASA服务器\n{}'.format('-'*30,'-'*30))
    res_nasa = time_try(url,params)
    print('链接服务器成功!\n开始下载NASA精选图片……') 
    json_nasa = res_nasa.json()
    url_local_1 = json_nasa['hits']['hits']
    for pic in url_local_1:
        pic_dl+=1
        pic_local = pic['_source']['master-image']['uri']
        url = url_main + pic_local[9:]        
        dst = r'{}/{}'.format(download_path('nasa'),url.split('/')[-1])
        download(url, dst)
    print('全部文件下载完毕,请按任意键退出')
    os.system('pause')
    sys.exit()

这部分代码我是选择性的下载想要下载的图片数量(1-999)
只要把以上的代码粘贴在一起,就可以下载nasa的宇宙高清大图啦~
也真心希望各位前辈看完后可以给出一些改进的建议,毕竟初学,身边也缺少可以交流技术的朋友,先行感谢一下各位前辈!

你可能感兴趣的:(爬虫,python)