异步 & ajax | 下载unplash高清大图

2019-04-26  本文已影响0人  0与1的邂逅

写在前面:

第一篇关于爬虫的文章,主要涉及异步操作带ajax网页的分析。

这里限于篇幅原因,可能无法对每一行代码进行详细的剖析,在后面的文章中,我也将不断地总结、记录一些python库地用法。

大家都知道,python的第三方库正在不断地扩大,因此,对于常用库的积累和对相应库的学习,都是非常重要的。(在我看来,自己造轮子挺不现实的)


Ajax是什么?

AjaxA synchronous Javascript And XML(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。

Ajax = 异步 JavaScript 和 XML 或者是 HTML(标准通用标记语言的子集)。

Ajax 是一种用于创建快速动态网页的技术。

Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

传统的网页(不使用 Ajax)如果需要更新内容,必须重载整个网页页面。

——《百度百科》

关键一点:Ajax请求的文件格式是XHR

举例子说,当你访问unplash的时候,往下滑动图片,你会发现在你滑动的过程中,有些图片还没加载出来,需要等待一小段时间,这些图片就是通过Ajax加载的。

关于Ajax的更多信息,可以看一下崔庆才大佬的博客:


1. 分析并访问Ajax:

(因为用的是火狐浏览器,所以开发者选项是中文的,不过还是建议用谷歌浏览器)

首先,按下F12,打开开发者选项,选中Network(网络)这一项。一开始,当我们还为向下滑动鼠标时,只显示了一个get请求,如下图。

当我们开始向下滑动鼠标的时候,下方的图片开始通过Ajax加载出来,生成许多的CSS、JS等格式的文件。而我们知道Ajax的文件格式是XHR,可以通过选择器选择仅显示XHR的文件,方便我们分析。

未使用选择器 使用选择器筛选 我们多向下滑动几次,请求到更多的Ajax,以便发现其规律。

可以看到,这些Ajax的请求头都有两个参数, page和per_page。

page表示第几页,per_page表示每一页有多少张图片。

通过对比,我们发现只有page这个参数在变,而per_page一直等于12。因此,我们仅需要改变page参数的值,就可以访问到不同的Ajax。

这里主要的难点是如何构造出URL中特定的参数,这里主要有两种方式,一种是利用urllib中的urlencode(),另一种是requests中的params参数。

【requests的params参数】
   # 对ajax进行请求,返回JSON响应
   def __get_img_links(self, page):
       url = 'https://unsplash.com/napi/photos'
       data = {
           'page': page,
           'per_page': 12,
       }
       response = requests.get(url, headers=self.headers,params=data)
       if response.status_code == 200:
           return response.json()
       else:
           print(f'请求失败,状态码为{response.status_code}')
【urllib的urlencode()函数】
import requests
from urllib.parse import urlencode

def __get_img_links(self, page):
    params = {
         'page': page,
         'per_page': 12,
    }
    url = 'https://unsplash.com/napi/photos/?' + urlencode(params)
    response = requests.get(url,headers=self.headers)
    if response.status_code == 200:
        return response.json()
    else:
        print(f'请求失败,状态码为{response.status_code}')

2. 分析Ajax返回的JSON响应

我们可以在开发者选项中响应中查看返回的JSON响应。


也可以将其中一个Ajax的请求在新的标签页中打开,如下图。我个人是比较喜欢在新的标签页中打开,然后进行分析。(主要是受网页大小的限制) 既然已经获得了JSON的信息,我们开始对其进行分析,找到我们需要的信息,如下图。

这里,我们仅需要图片的ID和下载链接这两个信息。

至此,对unplash这个网页的Ajax分析就已经完成了,接下来就是代码实现了。

3. 代码实现

import requests, os, time
import aiohttp, asyncio

class Spider(object):
   # 初始化
   def __init__(self):
       self.headers = {
           'Referer':'https://unsplash.com/',
           'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'}
       self.num = 1
       if '图片' not in os.listdir('.'):
           os.mkdir('图片')
       self.path = os.path.join(os.path.abspath('.'), '图片')
       os.chdir(self.path)  # 进入文件下载路径

   # 获取ajax返回的JSON中链接,并进行请求
   async def __get_content(self, link):
       async with aiohttp.ClientSession() as session:
           response = await session.get(link,headers=self.headers)
           content = await response.read()# 读取图片的二进制数据
           return content

   # 对ajax进行请求,返回JSON响应
   def __get_img_links(self, page):
       url = 'https://unsplash.com/napi/photos'
       data = {
           'page': page,
           'per_page': 12,
       }
       response = requests.get(url, headers=self.headers,params=data)
       if response.status_code == 200:
           return response.json()
       else:
           print(f'请求失败,状态码为{response.status_code}')

   # 下载图片
   # img:tuple元组
   # img[0]:图片的id
   # img[1]:图片的下载链接
   async def __download_img(self, img):
       # 调用__get_content()方法,返回图片的二进制数据
       content = await self.__get_content(img[1])
       with open(img[0]+'.jpg', 'wb') as f:
           f.write(content)
       print(f'下载第{self.num}张图片成功')
       self.num += 1

   # 调度函数,整合前面的函数
   def run(self):
       start = time.time()
       for x in range(1, 101):  # 下载一百页的图片就可以了,或者自己更改页数
           links = self.__get_img_links(x)
           tasks = [asyncio.ensure_future(self.__download_img((link['id'], link['links']['download']))) for link in links]
           loop = asyncio.get_event_loop()
           loop.run_until_complete(asyncio.wait(tasks))
           # 测试速度使用,如需要下载多张图片可以注释这段代码
           if self.num >= 10: 
               break
       end = time.time()
       print(f'共运行了{end-start}秒')

def main():
   spider = Spider()
   spider.run()

if __name__ == '__main__':
   main()

4. 结果展示


写在最后:

参考资料:

python爬虫涉及的东西相对较多,需要不断地积累、总结,可以成为我们学习python的一个不错的方法。

限于篇幅,代码无法一一细讲,还望各位大佬多包涵。

人生苦短,重拾Python。
Per Week,Hard Work!

上一篇 下一篇

猜你喜欢

热点阅读