深度优先和广度优先
admin
2023-07-15 06:42:05
0

准备数据结构

在进行广度优先和深度优先查找前,先定义一个数据结构。这里我用二叉树,每个节点的定义如下:

class Node(object):
    def __init__(self, item, left=None, right=None):
        self.item = item
        self.left = left
        self.right = right

    def __str__(self):
        return '%s' % self.item

生成二叉树

下面的代码从命令行接收参数,递归的生成一个指定深度的满二叉树。

counter = 0

def create_tree(deep):
    if deep < 0:
        return None
    global counter
    counter += 1
    root = Node(counter, deep)
    root.left = create_tree(deep-1)
    root.right = create_tree(deep-1)
    return root

if __name__ == '__main__':
    import sys
    s = sys.argv[1]
    n = s.isdigit() and int(s)  # 不是数字就是False,False就是0
    r = create_tree(n)
    print(r, r.left, r.right)

示例尽量简单,这里就用了全局变量。

深度优先

深度优先可以用递归的方法来实现。上面创建的时候也是使用递归来创建的,所以创建节点的顺序也是深度优先。
所以这里再写一个递归函数,遍历每个节点并且打印出来:

def print_tree(root):
    if root is None:
        return
    print(root)
    print_tree(root.left)
    print_tree(root.right)

if __name__ == '__main__':
    import sys
    s = sys.argv[1]
    n = s.isdigit() and int(s)  # 不是数字就是False,False就是0
    r = create_tree(n)
    print_tree(r)

这里节点是按创建的顺序输出的,因为创建的时候也是这样的一个递归的逻辑。

前序遍历
这个是二叉树的概念,上面的例子就是前序遍历。
前序遍历:根结点 ---> 左子树 ---> 右子树
有前序遍历,就还有中序和后序

中序遍历
中序遍历:左子树---> 根结点 ---> 右子树

def print_tree(root):
    if root is None:
        return
    print_tree(root.left)
    print(root)  # 根节点这句移到中间
    print_tree(root.right)

后序遍历
后序遍历:左子树 ---> 右子树 ---> 根结点

def print_tree(root):
    if root is None:
        return
    print_tree(root.left)
    print_tree(root.right)
    print(root)

广度优先

实现广度优先,只需要操作列表就可以了。遍历列表里的每一个元素,输出该元素并把子元素添加到一个新的列表里,给下一次遍历来操作:

def print_tree(l):
    work = [l]
    while len(work) > 0:
        items, work = work, []  # 复制一份用于遍历,并把自己清空,接收下一次要遍历的元素
        for i in items:
            if i is None:
                continue
            print(i)
            work.append(i.left)
            work.append(i.right)

层次遍历
相对于二叉树的前序遍历,这个遍历的方法叫层次遍历。

爬虫示例

网页爬虫的核心是解决图的遍历。对于网络爬虫,一般使用的就是广度优先的遍历。
下面的示例,从命令行接收url,把这个url下的所有链接打印出来。然后继续对这些链接发起请求,打印链接,一直循环下去:

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

def extract(url):
    """向给定的url发起GET请求,
    解析HTML,返回其中存在的链接
    """
    try:
        response = requests.get(url, timeout=0.1)  # 注意这里设置了超时时间
    except Exception:
        return False
    if not response.ok:
        return False
    global deep
    print("DEEP:", deep, url)
    soup = BeautifulSoup(response.text, features='html.parser')
    targets = soup.find_all('a')
    links = []
    for i in targets:
        links.append(urljoin(url, i.attrs.get('href')))
    return links

def breadth_first(urls):
    seen = {}  # 记录去重的字典
    global deep
    while len(urls) > 0:
        deep += 1
        items, urls = urls, []
        for i in items:
            if not seen.get(i):
                seen.setdefault(i, True)
                links = extract(i)
                if links:
                    urls.extend(links)  # 向列表尾部添加多个元素

deep = 0

if __name__ == '__main__':
    import sys
    breadth_first(sys.argv[1:])  # http://lab.scrapyd.cn/ 这个站点用来做实验不错

部分执行结果:

$ python 5crawl.py http://lab.scrapyd.cn/
DEEP: 1 http://lab.scrapyd.cn/
DEEP: 2 http://lab.scrapyd.cn/archives/57.html
DEEP: 2 http://lab.scrapyd.cn/tag/%E8%89%BA%E6%9C%AF/
DEEP: 2 http://lab.scrapyd.cn/tag/%E5%90%8D%E7%94%BB/
DEEP: 2 http://lab.scrapyd.cn/archives/55.html
DEEP: 2 http://lab.scrapyd.cn/archives/29.html
DEEP: 2 http://lab.scrapyd.cn/tag/%E6%9C%A8%E5%BF%83/
DEEP: 2 http://lab.scrapyd.cn/archives/28.html
DEEP: 2 http://lab.scrapyd.cn/tag/%E6%B3%B0%E6%88%88%E5%B0%94/
DEEP: 2 http://lab.scrapyd.cn/tag/%E7%94%9F%E6%B4%BB/
DEEP: 2 http://lab.scrapyd.cn/archives/27.html
DEEP: 2 http://lab.scrapyd.cn/tag/%E6%99%BA%E6%85%A7/
DEEP: 2 http://lab.scrapyd.cn/page/1/
DEEP: 2 http://lab.scrapyd.cn/page/2/
DEEP: 2 http://lab.scrapyd.cn/page/3/
DEEP: 2 http://lab.scrapyd.cn/page/4/
DEEP: 2 http://lab.scrapyd.cn/page/6/
DEEP: 2 http://lab.scrapyd.cn/tag/%E4%BA%BA%E7%94%9F/
DEEP: 2 http://lab.scrapyd.cn/tag/%E5%8A%B1%E5%BF%97/
DEEP: 2 http://lab.scrapyd.cn/tag/%E7%88%B1%E6%83%85/
DEEP: 2 http://lab.scrapyd.cn/tag/%E7%8E%8B%E5%B0%94%E5%BE%B7/
DEEP: 2 http://lab.scrapyd.cn/tag/%E7%BB%9D%E4%B8%96%E5%A5%BD%E8%AF%8D/
DEEP: 2 http://lab.scrapyd.cn/tag/%E8%AF%8D/
DEEP: 2 http://lab.scrapyd.cn
DEEP: 2 http://www.scrapyd.cn
DEEP: 3 http://lab.scrapyd.cn/archives/26.html
DEEP: 3 http://lab.scrapyd.cn/archives/25.html
DEEP: 3 http://lab.scrapyd.cn/archives/24.html
DEEP: 3 http://lab.scrapyd.cn/archives/23.html
DEEP: 3 http://lab.scrapyd.cn/archives/22.html
DEEP: 3 http://lab.scrapyd.cn/page/5/

理论上这个程序会把所有可达的网页都访问到,或者内存耗尽。
现在的内存也没那么块能耗尽。然后互联网也是无限延伸的,基本上就是没完没了了。
不过实际上,遇到某个下载的链接就会卡住了。这个不是这篇的重点,就不深究了。

相关内容

热门资讯

特朗普:正致力于与伊朗达成协议... 特朗普在《纽约邮报》一档播客访谈节目中称,他正与伊朗磋商一项协议,伊朗已同意不再谋求拥有核武器。他表...
不接壤的日菲为何偷划海界? 日菲近日发表联合声明,宣称就“划定两国专属经济区和大陆架的海洋边界”启动正式谈判。两个隔海相望的国家...
凤凰晚报丨从钳工到老戏骨,魏宗... 今日人物【从钳工到老戏骨,魏宗万用一生诠释“戏比天大”】6月1日,表演艺术家魏宗万在上海逝世,享年8...
科威特称伊朗袭击致63人受伤 科威特卫生部门3日称,伊朗当天对科威特的袭击已造成63人受伤,相关部门已启动紧急应对预案,并在全国范...
日本标榜“和平国家”却行扩军备... 今年是东京审判开庭80周年,世界正回望历史、反思战争罪责、捍卫二战后来之不易的国际秩序之际,日本却迈...
浙江杨梅即将大规模上市,如何破... “我们现在的压力很大。”5月底,浙江余姚杨梅产区丈亭镇副镇长林宇站在一片杨梅林前对第一财经表示,当地...
致5死2伤!韩国就韩华航空航天... 【环球网报道 记者 姜蔼玲】据韩联社6月1日报道,针对位于韩国大田的韩华航空航天公司发生爆炸致7人伤...
黄河科技学院2026年招生简章 长按图片识别二维码或点击 “阅读原文” 查看电子招生简章。
医路起航,从“心” 开始!黄河... 6月1日上午,黄河科技学院附属医院2022级临床医学本科实习生入院岗前培训在大医讲堂顺利举办。院领导...
问题居然在实体卡槽上!美版iP... 6月2日消息,日前,又有博主提前把还没发布的iPhone 18 Pro电池参数给曝光了出来,根据爆料...