Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/284.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Can';看不到无限循环_Python_Beautifulsoup_Web Crawler_Urllib3 - Fatal编程技术网

Python Can';看不到无限循环

Python Can';看不到无限循环,python,beautifulsoup,web-crawler,urllib3,Python,Beautifulsoup,Web Crawler,Urllib3,我试图写一个webcrawler,但我被卡住了,因为我看不到代码中的无限循环 class Crawler(object): def __init__(self, url, query, dir = os.path.dirname(__file__)): self.start_url = url self.start_parsed = urllib3.util.parse_url(url) self.query = re.compile(qu

我试图写一个webcrawler,但我被卡住了,因为我看不到代码中的无限循环

class Crawler(object):
    def __init__(self, url, query, dir = os.path.dirname(__file__)):
        self.start_url = url
        self.start_parsed = urllib3.util.parse_url(url)
        self.query = re.compile(query, re.IGNORECASE)
        self.dir = dir
        self.__horizon = set()
        self.log = []

        self.__horizon.add(url)
        self.log.append(url)
        print("initializing crawler....")
        print(locals())

    def start(self, depth= 5, url = '/'):
        print(url, depth)
        self.log.append(url)
        if depth > 0:
            pool = urllib3.PoolManager()
            data = pool.request("GET", self.start_url if url == '/' else url).data.decode('utf-8')

            valid_list = []
            self.add_horizon(parser_soup.get_links(data), valid_list)

            if re.search(self.query, parser_soup.get_text(data)):
                self.output(data)

            for u in valid_list:
                self.start(depth = (depth-1), url = u)

    def output(self, data):
        with open(os.path.join(self.dir, get_top_domain(self.start_parsed.host) + '.' + str(time.time()) + '.html'), 'w+') as f:
            f.write(data)

    def add_horizon(self, url_list, valid_list = []):
        for url in url_list:
            if get_top_domain(url) == get_top_domain(self.start_parsed.host)  \
                    and (not str(url) in self.log or not str(url) in self.__horizon):
                valid_list.append(str(url))

        self.__horizon.update(valid_list)

它永远运行。如何确保消除重复链接?

在爬虫程序中添加一个
已访问的
属性

from collections import defaultdict
class Crawler:
    def __init__(self, url, query, dir = os.path.dirname(__file__)):
        self.visited = defaultdict(bool)
        # Rest of code...

    def start(self, depth= 5, url = '/'):
        if self.visited[url]:
            return True
        self.visited[url] = True
        # Rest of code...
老实说,我也看不到无限循环。如果您发布一些输出,这将有所帮助

编辑:注意,在上面的回答中,我写道使用
defaultdict
是错误的解决方案。我的意思是说使用列表是错误的解决方案

编辑2:@Jona Christopher Sahnwald提出了一个比我更为正确的观点(见他在OP问题下的评论)。在类中添加
max\u visit
current\u visit
属性(设置为1000左右)可能会更有效率。从0处的
current\u visit
开始,每次访问站点时,递增
current\u visit
。当
当前访问
大于
最大访问
时,中止爬网。请注意,与其使用递归对访问过的网站进行递归,不如实现某种堆栈,以便暂停/恢复爬网,而不是中止爬网。像这样:

from collections import defaultdict

class Crawler:
    def __init__(self, url, query, dir = os.path.dirname(__file__)):
        self.visited = defaultdict(bool)
        self.current_visit = 0
        self.max_visit = 1000
        self.to_visit = []
        # Rest of code...

    def start(self, depth=5, url = '/'):
        self.to_visit.append((url, 1))
        while len(self.to_visit) > 0:
            url, current_depth = self.to_visit.pop()
            if current_depth > depth:
                continue
            elif visited[url]:
                continue
            elif self.current_visited > self.max_visited:
                break

            self.current_visited += 1
            visited[url] = True

            # Code that does something for each page (like download it, etc)

            # Code that finds links on page...

            for link in links_on_page:
                self.to_visit.append((link, current_depth + 1))

这样,您可以暂停爬网一次
current_visit
超过
max_visit
,允许您批量爬网
max_visit
,改编自Giogian的代码:

class Crawler(object):
    def __init__(self, url, query, dir=os.path.dirname(__file__)):
        self.visited = set()
        # Rest of code...

    def start(self, depth=5, url='/'):
        if url in self.visited:
            return True
        self.visited.add(url)
defaultdict
是一个具有默认值的字典,如果索引不存在,则使用该字典。然而,这是错误的解决方案。如我的代码所示,一个集合将更高效、更优雅

一个集合使用O(1)时间-与@Giorgian的答案一样快


当程序处于无限循环时,使用
Ctrl-C
中断程序。这将打印一个回溯,显示程序中断时正在执行的命令。这样做几次,你就会对它发生的地方有一个很好的了解。或者,使用调试器并在无限循环中暂停,然后使用“step”功能运行到下一行执行,以便跟踪程序的执行。PyCharm是一个包含调试器的优秀编辑器。它有很好的自动完成功能,而且综合性很好。它是免费的,请查看。

你说“看不到无限循环”是什么意思?@uoɥpʎzɐzɹC他不明白为什么他的代码会运行到无限循环中。这与你的问题无关,但有一个建议:让池管理器在
\uu init\uuuu
中使用它以从中获得最大的好处。可能没有无限循环,这个程序只是运行了很长时间。粗略计算:如果每个爬网页面包含20个链接(对于大多数网站来说,这是一个较低的估计),那么您必须加载20^4=160K个页面。以每页10毫秒的速度,这将需要4个多小时。每页40个链接,大约需要70个小时。等等指数增长。如果将默认深度设置为1而不是5,会发生什么情况?@uoɥPʎzɹC当然,这不是完整的!它只是用来指导OP的。看我的答案,一个列表要好得多,虽然它不符合PEP 8,但DefaultDict是错误的解决方案!检查值是否在列表中需要O(n)个时间,而使用defaultdict只需要O(1)个时间!@如果你想在
成语中使用
,你可以使用set。@uoɥPʇzɹC至少,你能从我的答案中删除否决票吗,因为你的代码基本上是复制粘贴我的答案?唯一的区别是您使用了
set()
,这是我的建议@乔治·博卡·塔斯尤克,我自己想出来的。没有看到你的评论。取消了否决票。好吗?@GiorgianBorca Tasciuc和我有个建议。