今天工作上有个需求,数据库有个表有将近 3 万条 url 记录,每条记录都是一个图片,我需要请求他们拿到每个图片存到本地。一开始我是这么写的(伪代码):
import requests
for url in urls:
try:
r = requests.get(url).content
save_image(r)
except Exception, e:
print str(e)
然而在服务器上运行时, 会发现每隔一些请求会报类似下面的错误:
HTTPConnectionPool(host='wx.qlogo.cn', port=80): Max retries exceeded with url: /mmopen/aTVWntpJLCAr2pichIUx8XMevb3SEbktTuLkxJLHWVTwGfkprKZ7rkEYDrKRr5icyDGIvU4iasoyRrqsffbe3UUQXT5EfMEbYKg/0 (Caused by <class 'socket.error'>: [Errno 104] Connection reset by peer)
这让我想起了之前通过hacker news api 在自己电脑上请求一条一条数据时,为了加快处理速度,采用多进程的方式请求接口,也会出现这样的错误。之前我是做了错误记录直接 pass 了,这次情况下因为需要请求所有图片,在 google 查了相关原因,大概是因为我频繁请求,服务器关闭了部门请求连接。参见这里, 这里, 这里。 所以我粗暴地这么做,还真解决了:
import requests
for url in urls:
for i in range(10):
try:
r = requests.get(url).content
except Exception, e:
if i >= 9:
do_some_log()
else:
time.sleep(0.5)
else:
time.sleep(0.1)
break
save_image(r)
代码很简陋,但可以说明大体解决方案,在每个请求间增加延时可以减少大部分请求拒绝,但还是存在一些请求被拒绝的,所以在那部分请求被拒绝后,发起重试,在被拒 10 次后才善罢甘休(记录到日志)。在实际的请求中,加了 0.1s 的延迟被拒绝的情况明显少了很多,被拒绝重试的次数最多为 3 次,最后成功地取下了全部图片。
1
hastelloy 2016-11-15 07:37:14 +08:00 via Android
除了睡觉还有什么不简单的解决方式呢?楼下的说说?
|
3
kevin8096 2016-11-15 11:08:47 +08:00
。。。。。
|
4
mayne95 2016-11-15 14:10:46 +08:00 via Android
整个 ip 池,头部也多整几个。换来换去应该就可以了。
|
5
sakwu 2016-11-16 00:18:15 +08:00
最近在做一个类似的爬虫,遇到一个问题就系假若数据库里面有个图片 url 被屏蔽了, 404 ,怎么跳过这条 URL 或者重新下载这个图片。
我目前状况是要么卡死,设置 timeout 也是抛出异常然后就中断了,没法进去进行下载 |
8
sakwu 2016-11-16 22:30:00 +08:00
@Nisenasdf 额我是新手, try.except 了,但是依然中止了。
for img in range(len(img_name)): try: img_data = request.urlopen(img_add[img], timeout=5).read() except Exception as e: print(img_name[img] + '下载失败' + e) fout_img = open('images/' + img_name[img] + '.jpg', 'wb') fout_img.write(img_data) fout_img.close() print(img_name[img] + '下载成功') 如果 img_add 里有一个 URL 是错误的,下载就会中断,不懂为啥 期待是这样的输出的: img_name1 下载成功 img_name1 下载成功 img_name1 下载失败: time out img_name1 下载成功 img_name1 下载成功 |
9
Nisenasdf OP @sakwu 代码没锁紧看起来--!, 你可以 debug 看一下, 404 能不能走到 exception 的异常处理里面, 404 是直接 exception 还是 直接 timeout 5 秒继续后面的, 调试看一下应该不难解决;)
|