【问题标题】:Avoiding a stack overflow in Python 3在 Python 3 中避免堆栈溢出
【发布时间】:2015-11-14 10:37:05
【问题描述】:

免责声明:我对计算机科学一无所知,也不了解幕后发生的任何事情的内部运作。使用互联网上的所有内容自学编码。

Python 版本:

Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit(Intel)] on win32

使用普通解析器,其主要目的是获取图像的完整大小的 url,将其保存到文件中以供稍后下载,然后移动到行中的下一个图像,这几乎是强制性的,因为相关网站的不良网络架构。当我完成程序时,在第 976 次执行时遇到了一个错误。

  RuntimeError: maximum recursion depth exceeded in comparison

经过研究,我发现问题是由于“堆栈溢出”引起的。但是,目前我不知道如何在不造成任何显着性能下降的情况下解决问题。 (不过,这不是问题,因为我只是为了学习。)

这让我想到了我的问题,我该如何解决这个问题,我在哪里可以了解更多关于这些事情的信息,比如 Stack Overflow 是从什么开始的?

(程序运行良好,但堆栈溢出停止)

import requests
from bs4 import BeautifulSoup

def somesite_parsing(url):

    connection = requests.get(url)
    html = connection.text
    soup = BeautifulSoup(html, "html.parser")

    # The exception is necessary due to the web architecture.
    # Images that don't have different versions by size have an img tag.
    # Returns "http://www.somesite.net/tag_tag_tag.full.jpg"
    try:
        semi_link = soup.select("html > body > #wrapper > #body > #content > #large > a")
        full_link = semi_link[0].get("href")
        print(full_link)

    except IndexError:
        semi_link = soup.select("html > body > #wrapper > #body > #content > #large > img")
        full_link = semi_link[0].get("src")
        print(full_link)

    # File was created during testing so I switched to appending.
    # Saves link into folder.
    fx = open("list_file.txt", "a")
    fx.write(full_link + "\n")
    fx.close()

    # Fetches the next url.
    # Returns "/id_number"
    next_link = soup.select("html > body > #wrapper > #body > #menu > .smallthumbs > li > a")
    next_link = next_link[0].get("href")
    next_link = "http://www.somesite.net" + next_link
    print(next_link)

    print()
    somesite_parsing(next_link)


somesite_parsing("http://www.somesite.net/1905220")

【问题讨论】:

  • 我假设最后一个电话,zerochan_parsing,实际上应该是somesite_parsing
  • 糟糕,没注意到 XD
  • 每次再次调用该函数时,您总是调用somesite_parsing。您需要确定一种停止呼叫somesite_parsing 的方法。因此,也许可以尝试检查您是否仍然获得 id_number。如果您没有获得 id_number,则在再次调用 somesite_parsing 之前从函数中获得 return

标签: python python-3.x stack beautifulsoup overflow


【解决方案1】:

“while”循环确实是您所需要的。

这就是我的做法,虽然我没有运行代码。

import requests
import json

start_url = "http//your_start_url"

def save_data(data):
    """Or however you want to save your data.
     I like .jsonl, see http://jsonlines.org/"""
    data = json.dumps(data)
    fx = open("data_file.jsonl", "a") #see 
    fx.write(data + "\n")
    fx.close()

def get_url(url):
    "This returns something similar to an 'option type'."
    r = requests.get(url)
    return {"success":  r.ok,
            "next_url": parse_your_response_for_next_url(r.text),
            "page":     r.text,
            "url":      url}

##################################


response = get_url(start_url)

while respose["success"]:
    save_data(response)
    response = get_url(response["next_url"])

(我使用的是伪“选项类型”和 jsonl 文件。但这只是一种风格决定。请参阅 https://en.wikipedia.org/wiki/Option_typehttp://jsonlines.org/

此外,如果您发出足够多的请求以达到最大递归深度,则最好将响应存储为 @functools.lru_cache 或其他磁盘支持的替代方案。

【讨论】:

    【解决方案2】:

    嵌套函数调用过多时会发生堆栈溢出。这主要发生在函数继续无休止地调用自身时。

    在您的情况下,您在内部调用somesite_parsing。这最终会导致堆栈溢出。

    有几种方法可以避免这种情况。我建议您在解析过程中进行循环。

    更改somesite_parsing 以返回下一个链接,而不是调用自身,您可以这样做:

    next_link = "http://www.somesite.net/1905220"
    while next_link:
        next_link = somesite_parsing(next_link)
    

    这将允许您从 somesite_parsing 返回一个 falsy 值以停止循环。

    【讨论】:

    • 修复了将所有内容转换为 while 循环并使 url 等于 next_link 的问题。您能否详细说明为什么会这样?似乎没有什么不同。
    • 我可以给你的最好的答案是我已经给出的答案:嵌套函数调用过多时会发生堆栈溢出。每次一个函数在结束之前调用任何其他函数(包括它自己)时都会添加另一层嵌套。 --- 更技术性的答案是,运行时只有一定数量的堆栈帧(google it)的空间,并且在调用函数时将帧添加到堆栈中,并在结束时从堆栈中弹出。如果您继续添加帧而不弹出任何帧,您将获得并溢出(帧太多)。
    • 你最初所做的在带有尾调用优化的语言中会很好(谷歌它):一些语言比其他语言处理递归更好。
    猜你喜欢
    • 2020-03-08
    • 2010-11-30
    • 1970-01-01
    • 2013-10-29
    • 2016-07-12
    • 2010-12-04
    • 2011-11-23
    • 2011-09-21
    • 2014-04-13
    相关资源
    最近更新 更多