看了好多文章,发送请求的代码都是aiohttp.request,但是这个似乎是老版本的。
新版本的改成了要用在aiohttp.ClientSession环境下使用session来发送请求。
import requests
import asyncio
import aiohttp
import random
import sys
import os
def get_proxy():
resp = requests.get("http://127.0.0.1:5010/get/")
return resp.text
ua_list = [
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv: 11.0) like Gecko",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
]
base_url = "http://fm.shiyunjj.com/2015/306/{}.jpg"
headers = {
"Referer": "http://www.mmjpg.com/tag/rosi",
"User-Agent": random.choice(ua_list),
}
async def get_img(url):
for i in range(15):
try:
filename = url.split("/")[-1]
# 设置总的请求时长,因为是免费ip,其有效性虽说经过判断有用,但是还是值得商榷的。所以设置了请求总的时长为3秒
# 默认的是5min,不设置这个,而用免费获得ip,会由于ip的并不有效,而浪费很长时间。
timeout = aiohttp.ClientTimeout(total=3)
async with aiohttp.ClientSession(
headers=headers, timeout=timeout
) as session:
resp = await session.get(url, proxy="http://" + get_proxy())
# # 获取响应的头部信息
# print('resp is ', type(resp.headers['Content-Length']))
# 若状态码不为200,则下载的不是图片
# 如若不添加这个判断条件,会出现很多文件存储为图片格式,但是不是图片。。。
if resp.status != 200:
continue
with open(filename, "wb") as fd:
while True:
# 这个地方通过对流的处理,而不是一下子整体读取,会更好些
# 一下子整个的读取,会导致下载图片的时候,一开始会出现资源浪费,几个协程均处于i/o状态。
chunk = await resp.content.read(1024)
if not chunk:
break
fd.write(chunk)
# 这个是由于,之前下载的图片中一部分,小于正常数值,不是图片。我就通过这种方式判断
# 起始应该是不需要的,因为加上前面的对状态码的识别,这个部分就应该没问题了
# if os.path.getsize(filename) != int(resp.headers["Content-Length"]):
# # print("{}文件下载不完整,重新下载".format(filename))
# continue
show("{}完成下载".format(filename))
return
except asyncio.TimeoutError:
pass
# print("第{}次访问网站{}时, 超时".format(url, i+1))
except aiohttp.client_exceptions.ClientProxyConnectionError:
pass
# print("第{}次访问网站{}时, 代理错误".format(url, i+1))
except Exception as e:
pass
# print("第{}次访问网站{}时, {}".format(url, i+1, e.args[0]))
print("{}未完成下载".format(url))
def show(text):
print(text)
sys.stdout.flush()
loop = asyncio.get_event_loop()
to_do = [get_img(base_url.format(cc)) for cc in range(1, 30)]
res, _ = loop.run_until_complete(asyncio.wait(to_do))
loop.close()
其中用到了,一个获取免费代理ip的方法。这个方法在之后会写上。
这个异步的方式下载图片和requests库下载图片的方式差别是很大的。
刚开始看异步的方式,将下载图片似乎分为两步,一个是返回resp,一个是对resp进行读取(resp.read())。
只用requests库的时候,就一步,那就是requests.get(url)(似乎也是两步?不是的,这个resp.content是可以直接返回的,而不是通过网络获取)。
直到有的图片加载很慢的时候,我才发现确实可以大致分为两步,因为有的时候,图片还没加载出来,但是通过chome查看response的头部信息,发现是已经获取了的。
(虽说经过对response的状态码进行了判断,但是有的时候,返回的并不是图片,而是html文件,这个就很无语了)
这个后来可以通过添加头部信息来解决,设置headers['Accept']='image/webp,image/apng,image/*;'、
然后就下载的全是图片。
到此,完美解决。。。