Python爬虫---队列模拟递归遍历(广度遍历)

本文介绍了一个简单的网页爬虫程序,该程序利用Python的正则表达式(re)来从网页中提取URL链接及电子邮件地址。通过广度优先搜索(BFS)算法遍历页面链接,并使用urllib.request模块获取网页内容。

1.导入re模块:正则

2.导入urllib.request模块:爬虫

3.导入deque模块:双向队列

4.extend():列表末尾一次性追加另一个序列中的多个值

5.findall():相匹配的全部字串,返回形式为数组

6.compile():将一个字符串编译为字节

7.popleft():队列为先进后出,都是添加在列表最前面那个值

import urllib.request
import urllib
import re
from collections import deque
#http://bbs.tianya.cn/m/post-140-393974-4.shtml
#http://bbs.tianya.cn
#<a class="u-btn pre-btn" href="/m/post-140-393974-4.shtml"></a>
#s广度遍历使用队列

def  geteveryurl(data):
    alllist=[]
    mylist1=[]
    mylist2=[]

    mylist1=getallhttp(data)
    if len(mylist1) >0:
        mylist2=getabsurl(mylist1[0],data)

    alllist.extend(mylist1)
    alllist.extend(mylist2)
    return  alllist


#<a class="u-btn pre-btn" href="/m/post-140-393974-4.shtml"></a>
def  getabsurl(url,data):
    try:
        regex=re.compile("href=\"(.*?)\"",re.IGNORECASE)
        httplist=regex.findall(data)
        newhttplist=httplist.copy()#深拷贝
        for data  in  newhttplist:
            if  data.find("http://")!=-1:
                httplist.remove(data)
            if  data.find("javascript")!=-1:
                httplist.remove(data)
        hostname=gethostname(url)
        if hostname!=None:
            for  i  in range(len(httplist)):
                httplist[i]=hostname+httplist[i]

        return httplist
    except:
        return []


#http://bbs.tianya.cn/post-140-393974-1.shtml'
#http://bbs.tianya.cn
def  gethostname(httpstr):
    try:
        mailregex = re.compile(r"(http://\S*?)/", re.IGNORECASE)
        mylist = mailregex.findall(httpstr)
        if  len(mylist)==0:
            return None
        else:
            return mylist[0]
    except:
        return None


def  getallhttp(data):
    try:
        mailregex = re.compile(r"(http://\S*?)[\"|>|)]", re.IGNORECASE)
        mylist = mailregex.findall(data)
        return mylist
    except:
        return []





def  getallemail(data):
    try:
        mailregex = re.compile(r"([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4})", re.IGNORECASE)
        mylist = mailregex.findall(data)
        return mylist
    except:
        return []




def  getdata(url):
    try:
        data=urllib.request.urlopen(url).read().decode("utf-8")
        return data  #没有异常返回字符串
    except:
        return "" #发生异常返回空



def  BFS(urlstr):
    urlqueue=deque([]) #队列
    urlqueue.append(urlstr)
    while len(urlqueue)!=0:
        url=urlqueue.popleft()#队列弹出的数据
        print(url)#打印url链接
        pagedata=getdata(url)#获取网页源代码
        emaillist=getallemail(pagedata) #提取邮箱到列表
        if len(emaillist)!=0:  #邮箱不为空
            for  email in emaillist: #打印所有邮箱
                print(email)
        newurllist=geteveryurl(pagedata)#抓取所有的url
        if  len(newurllist)!=0:  #判断长度
            for urlstr in newurllist: #循环处理每一个url,
                if urlstr not in urlqueue: #判断存在或者不存在
                    urlqueue.append(urlstr)   #插入



#BFS("http://bbs.tianya.cn/m/post-140-393974-5.shtml")
BFS("http://www.baidu.com/")
<think>首先,用户的问题是关于深度优先遍历(DFS)和广度优先遍历(BFS)在算法上的区别。我需要确保我的回答基于系统级指令。 系统级指令: 1. 所有行内数学表达式必须使用 $...$ 格式。 2. 独立公式必须使用 $$...$$ 格式并单独成段。 3. LaTeX语法正确。 4. 使用中文回答。 5. 生成相关问题。 6. 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 - 参考站内引用:我有三个引用:[^1], [^2], [^3],这些是关于DFS和BFS的,我需要自然地融入引用。 用户的问题:"我想理解深度优先遍历广度优先遍历在算法上的区别 请问深度优先遍历广度优先遍历的区别 算法对比" 所以,我需要专注于区别和对比。 关键点: - **DFS (深度优先遍历)**:从起始节点开始,尽可能深地探索分支,直到无法继续,然后回溯。 - **BFS (广度优先遍历)**:从起始节点开始,先探索所有邻居节点,然后再探索邻居的邻居,依此类推。 区别: 1. **遍历顺序**:DFS 使用栈(或递归),BFS 使用队列。 2. **空间复杂度**:BFS 通常需要更多内存,因为它存储所有节点。 3. **时间复杂度**:两者在遍历所有节点时都是 O(V + E),但具体应用不同。 4. **应用场景**: - DFS:用于路径查找、拓扑排序、连通分量。 - BFS:用于最短路径(在未加权图中)、连通性测试。 结构清晰的回答: 1. **介绍**:简要定义DFS和BFS。 2. **核心区别**:对比算法实现、遍历方式、空间和时间复杂度。 3. **应用对比**:讨论不同场景下的适用性。 4. **总结**:概括主要区别。 5. **相关问题**:生成相关问题。 生成相关问题:在回答末尾,添加3-4个相关问题。 引用:自然地融入引用。例如: - 引用[^1]提到DFS和BFS用于图的遍历- 引用[^2]讨论它们的应用场景。 - 引用[^3]提到它们在拓扑排序、寻路等中的应用。 所以,在回答中,当提到应用时,可以添加引用标识。 数学表达式:由于用户的问题是关于算法的,可能涉及一些数学,比如时间复杂度。例如: - 时间复杂度:O(V + E),其中 V 是顶点数,E 是边数。使用行内格式:$O(V + E)$。 - 如果需要独立公式,但这里可能不需要,除非有复杂公式。 确保中文回答。 大纲回答: 1. **引言**:解释DFS和BFS是什么。 - 深度优先遍历(DFS)是一种递归或栈实现的遍历方式,优先深入探索分支。 - 广度优先遍历(BFS)是一种队列实现的遍历方式,优先广度探索邻居节点。 - 引用[^1]:DFS和BFS是图遍历的基本方法[^1]。 2. **算法实现对比**: - **数据结构**:DFS使用栈(或递归),BFS使用队列- 例如:DFS可以用递归实现,BFS用队列- **遍历过程**:DFS先探索深度,BFS先探索广度- 使用行内数学:在DFS中,空间复杂度在最好情况下是 $O(\log V)$(平衡树),但在最坏情况下是 $O(V)$;BFS总是 $O(V)$ 或 $O(V + E)$ 取决于实现。 - **时间复杂度**:两者都是 $O(V + E)$ 对于邻接表表示。 - **空间复杂度**:DFS在递归深度高时可能堆栈溢出,空间 $O(V)$;BFS存储所有节点,空间 $O(V)$。 3. **应用场景对比**: - DFS适合:路径查找、拓扑排序、检测环。 - 引用[^2]:DFS常用于查找路径和拓扑排序[^2]。 - BFS适合:最短路径(在未加权图)、连通性测试、最小生成树(在特定算法)。 - 引用[^3]:BFS用于寻路和爬虫[^3]。 4. **总结区别**: - 主要区别在遍历顺序:DFS深度优先,BFS广度优先- 空间使用:BFS更易消耗内存,DFS在深度大时可能递归问题。 - 适用性:DFS在深度探索更有效,BFS在广度探索更有效。 5. **相关问题**:生成3-4个相关问题。 - 例如: 1. 深度优先遍历在树结构中的应用是什么? 2. 广度优先遍历如何用于寻找最短路径? 3. 在什么情况下DFS比BFS更优? 4. DFS和BFS在空间复杂度上的具体差异是什么? 确保引用自然添加:在相关段落末尾添加 [^n]。 例如:"深度优先遍历常用于查找路径和拓扑排序[^2]。" 现在,撰写回答。</think>### 深度优先遍历广度优先遍历的区别:算法对比 深度优先遍历(DFS)和广度优先遍历(BFS)是图论中两种基础遍历算法,它们在遍历顺序、数据结构、空间和时间复杂度等方面有显著差异。下面我将逐步对比这些区别,帮助您清晰理解。核心区别在于:**DFS 优先深入探索分支(纵向),BFS 优先广度探索邻居(横向)**。我将从算法实现、性能和应用场景三方面展开对比,并融入相关引用支持分析。 #### 1. **算法实现对比** - **数据结构**: - **DFS**:使用栈(stack)或递归实现。从起始节点开始,尽可能深地探索一条路径,直到无法继续才回溯。例如,递归实现时,每次访问一个节点后立即递归访问其未访问的邻居。 - 伪代码示例: ```python def dfs(graph, node, visited): if node not in visited: visited.add(node) for neighbor in graph[node]: dfs(graph, neighbor, visited) ``` - **BFS**:使用队列(queue)实现。从起始节点开始,先访问所有直接邻居,再逐层扩展。例如,队列中存储节点,并按先进先出顺序处理。 - 伪代码示例: ```python def bfs(graph, start): queue = collections.deque([start]) visited = set([start]) while queue: node = queue.popleft() for neighbor in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor) ``` - **关键区别**:DFS 依赖栈(后进先出),适合深度探索;BFS 依赖队列(先进先出),适合广度扩展。这导致遍历路径不同:DFS 可能更快找到深层节点,但可能错过近邻;BFS 保证先访问近邻节点[^1]。 - **遍历过程**: - DFS 的遍历顺序:从根节点出发,选择一条路径深入到底(例如,在树结构中先访问左子树最深节点),再回溯探索其他分支。 - BFS 的遍历顺序:从根节点出发,按层次遍历(例如,先访问所有第一层邻居,再第二层邻居)。 - 示例:在图 $G$ 中,假设起始节点为 $A$,邻居为 $B$ 和 $C$。DFS 可能先访问 $A \to B \to D$(深度路径),而 BFS 访问 $A \to B \to C$(广度优先)[^2]。 #### 2. **性能对比:空间和时间复杂度** - **时间复杂度**:两者在遍历所有节点和边时,通常为 $O(V + E)$,其中 $V$ 是顶点数,$E$ 是边数(基于邻接表存储)。但实际效率取决于图的结构: - DFS 在稀疏图或深度优先路径搜索中可能更快收敛。 - BFS 在稠密图或需要最短路径时更高效。 - **空间复杂度**: - **DFS**:空间占用取决于递归深度或栈大小。在最好情况下(如平衡树),空间为 $O(\log V)$;但在最坏情况下(如线性链),空间为 $O(V)$,可能导致堆栈溢出。 - **BFS**:空间占用取决于队列大小,通常为 $O(V)$ 或 $O(V + E)$,因为它需要存储所有未访问节点。在宽图(如星形图)中,空间消耗较大。 - **关键区别**:DFS 在深度大的图中空间效率更高(但递归风险高),BFS 在广度大的图更稳定但内存消耗大[^3]。 #### 3. **应用场景对比** - **DFS 适用场景**: - 路径查找:如迷宫求解或连通分量检测,因为它能深入探索单一路径。 - 拓扑排序:用于有向无环图(DAG),因为它能处理依赖关系(例如,课程安排)。 - 检测环或回溯问题:如八皇后问题,DFS 能系统回溯。 - 引用支持:DFS 常用于查找路径和拓扑排序,在深度探索中更高效[^2]。 - **BFS 适用场景**: - 最短路径:在未加权图中,BFS 保证找到最短路径(例如,社交网络中的好友推荐)。 - 连通性测试:快速检查图是否连通,或寻找所有连通分量。 - 爬虫和搜索引擎:BFS 能均匀抓取网页,避免深度偏斜。 - 引用支持:BFS 广泛应用于寻路、爬虫和最短路径问题,因其广度优先特性[^3]。 - **总结核心区别**: | 方面 | 深度优先遍历 (DFS) | 广度优先遍历 (BFS) | |------|-------------------|-------------------| | 遍历顺序 | 深度优先(纵向) | 广度优先(横向) | | 数据结构 | 栈或递归 | 队列 | | 空间复杂度 | $O(V)$(最坏) | $O(V)$ 或更高 | | 时间复杂度 | $O(V + E)$ | $O(V + E)$ | | 最佳应用 | 路径查找、拓扑排序 | 最短路径、连通性测试 | | 缺点 | 可能堆栈溢出、错过近邻 | 内存消耗大、不适合深度探索 | 总之,选择 DFS 还是 BFS 取决于问题:**DFS 适合深度优先探索(如回溯问题),BFS 适合广度优先搜索(如最短路径)**。实际中,两者常结合使用,例如在图的遍历中互补[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值