用Python抓取HTML时遇到Errno 10054:如何重新连接

2 投票
2 回答
1936 浏览
提问于 2025-04-18 04:15

我是一名初学Python的程序员,想用Python从fanfiction.net上抓取大量页面,并把页面HTML源代码中的特定行保存到一个.csv文件里。我的程序运行得不错,但最终会遇到一个问题,导致它停止运行。我的开发环境(IDE)告诉我,程序遇到了“Errno 10054:远程主机强制关闭了一个现有连接”。

我想找到一种方法,让我的代码在每次出现这个错误时能够重新连接并继续运行。我的代码每次运行时会抓取几十万个页面;这是不是对网站来说太多了?网站似乎并没有阻止抓取。我已经对这个问题做了不少研究,并尝试实现一个重试装饰器,但这个装饰器似乎没有效果。以下是我代码中相关的部分:

def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None):

    def deco_retry(f):

        @wraps(f)
        def f_retry(*args, **kwargs):
            mtries, mdelay = tries, delay
            while mtries > 1:
                try:
                    return f(*args, **kwargs)
                except ExceptionToCheck as e:
                    msg = "%s, Retrying in %d seconds..." % (str(e), mdelay)
                    if logger:
                        logger.warning(msg)
                    else:
                        print(msg)
                    time.sleep(mdelay)
                    mtries -= 1
                    mdelay *= backoff
            return f(*args, **kwargs)

        return f_retry  # true decorator

    return deco_retry


@retry(urllib.error.URLError, tries=4, delay=3, backoff=2)
def retrieveURL(URL):
    response = urllib.request.urlopen(URL)
    return response


def main():
    # first check: 5000 to 100,000 
    MAX_ID = 600000
    ID = 400001
    URL = "http://www.fanfiction.net/s/" + str(ID) + "/index.html"
    fCSV = open('buffyData400k600k.csv', 'w')
    fCSV.write("Rating, Language, Genre 1, Genre 2, Character A, Character B, Character C,     Character D, Chapters, Words, Reviews, Favorites, Follows, Updated, Published, Story ID, Story Status, Author ID, Author Name" + '\n')    

    while ID <= MAX_ID:

        URL = "http://www.fanfiction.net/s/" + str(ID) + "/index.html"
        response = retrieveURL(URL)

每当我在IDE外运行这个.py文件时,它大约一个小时后就会卡住,停止抓取新页面。我在IDE中运行同一个文件的不同版本,那个版本似乎已经运行了将近12个小时,甚至更久——这是否意味着在IDE中运行的文件可以正常工作,但独立运行时却不行?

我是不是把我的装饰器设置错了?还有什么其他方法可以让Python重新连接?我还看到有人说,SQL本地客户端过时可能会导致像我这样的Windows用户出现问题——这是真的吗?我尝试更新过,但没有成功。

谢谢!

2 个回答

1

你的重连代码看起来不错,只有一个地方需要注意,就是你想要捕捉的异常。根据这个StackOverflow的问题Errno 10054 是一种socket.error。你只需要import socket,然后在你的重试处理程序中加上except socket.error这句话就可以了。

2

你正在捕捉的是URLErrors,但错误代码10054并不是URLErrors,所以你的@retry装饰器不会进行重试。试试这个。

@retry(Exception, tries=4)
def retrieveURL(URL):
    response = urllib.request.urlopen(URL)
    return response

这个应该会在遇到任何异常时重试4次。你的@retry装饰器定义得是正确的。

撰写回答