python requests, 记遇到的一个问题: [Errno 104] Connection reset by peer

2016-11-14 23:03:36 +08:00
 Nisenasdf

记遇到的一个问题:[Errno 104] Connection reset by peer

今天工作上有个需求,数据库有个表有将近 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 次,最后成功地取下了全部图片。

10000 次点击
所在节点    Python
9 条回复
hastelloy
2016-11-15 07:37:14 +08:00
除了睡觉还有什么不简单的解决方式呢?楼下的说说?
Nisenasdf
2016-11-15 09:01:01 +08:00
@hastelloy 没有什么是睡一觉不能解决的,如果有,那就睡两觉
kevin8096
2016-11-15 11:08:47 +08:00
。。。。。
mayne95
2016-11-15 14:10:46 +08:00
整个 ip 池,头部也多整几个。换来换去应该就可以了。
sakwu
2016-11-16 00:18:15 +08:00
最近在做一个类似的爬虫,遇到一个问题就系假若数据库里面有个图片 url 被屏蔽了, 404 ,怎么跳过这条 URL 或者重新下载这个图片。

我目前状况是要么卡死,设置 timeout 也是抛出异常然后就中断了,没法进去进行下载
Nisenasdf
2016-11-16 10:36:52 +08:00
@mayne95 有道理啊,改天找机会试试
Nisenasdf
2016-11-16 10:38:25 +08:00
@sakwu 不太明白你遇到的问题,既然知道 404 了,是没做相应的异常处理?
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 下载成功
Nisenasdf
2016-11-18 00:18:32 +08:00
@sakwu 代码没锁紧看起来--!, 你可以 debug 看一下, 404 能不能走到 exception 的异常处理里面, 404 是直接 exception 还是 直接 timeout 5 秒继续后面的, 调试看一下应该不难解决;)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/320523

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX