爬虫之广度优先&深度优先

广度优先算法介绍

  整个的广度优先爬虫过程就是从一系列的种子节点开始,把这些网页中的”子节点”(也就是超链接)提取出来,放入队列中依次进行抓取。被处理过的链接需要放 入一张表(通常称为Visited表)中。每次新处理一个链接之前,需要查看这个链接是否已经存在于Visited表中。如果存在,证明链接已经处理过, 跳过,不做处理,否则进行下一步处理。

  初始的URL地址是爬虫系统中提供的种子URL(一般在系统的配置文件中指定)。当解析这些种子URL所表示的网页时,会产生新的URL(比如从页面中的http://www.admin.com “中提取出http://www.admin.com 这个链接)。然后,进行以下工作:

1.把解析出的链接和Visited表中的链接进行比较,若Visited表中不存在此链接,表示其未被访问过。
2.获取子链接。
3.处理完毕后,将链接直接放入Visited表中。
针对这个链接所表示的网页,继续上述过程。如此循环往复。
广度优先遍历是爬虫中使用最广泛的一种爬虫策略,之所以使用广度优先搜索策略,主要原因有三点:

1.重要的网页往往离种子比较近,例如我们打开新闻网站的时候往往是最热门的新闻,随着不断的深入冲浪,所看到的网页的重要性越来越低。
2.万维网的实际深度最多能达到17层,但到达某个网页总存在一条很短的路径。而广度优先遍历会以最快的速度到达这个网页。
3.广度优先有利于多爬虫的合作抓取,多爬虫合作通常先抓取站内链接,抓取的封闭性很强。

代码实现:

from bs4 import BeautifulSoup
import requests
import re

#自定义队列类
class linkQuence:
    def __init__(self):
        # 已访问的url集合
        self.visted = []
        # 待访问的url集合
        self.unVisited = []

    # 获取访问过的url队列
    def getVisitedUrl(self):
        return self.visted

    # 获取未访问的url队列
    def getUnvisitedUrl(self):
        return self.unVisited

    # 添加到访问过得url队列中
    def addVisitedUrl(self, url):
        self.visted.append(url)

    # 移除访问过得url
    def removeVisitedUrl(self, url):
        self.visted.remove(url)

    # 未访问过得url出队列
    def unVisitedUrlDeQuence(self):
        try:
            return self.unVisited.pop()
        except:
            return None

    # 保证每个url只被访问一次
    def addUnvisitedUrl(self, url):
        if url != "" and url not in self.visted and url not in self.unVisited:
            self.unVisited.insert(0, url)

    # 获得已访问的url数目
    def getVisitedUrlCount(self):
        return len(self.visted)

    # 获得未访问的url数目
    def getUnvistedUrlCount(self):
        return len(self.unVisited)

    # 判断未访问的url队列是否为空
    def unVisitedUrlsEnmpy(self):
        return len(self.unVisited) == 0


class MyCrawler:
    def __init__(self, seeds):
        # 初始化当前抓取的深度
        self.current_deepth = 1
        # 使用种子初始化url队列
        self.linkQuence = linkQuence()
        if isinstance(seeds, str):
            self.linkQuence.addUnvisitedUrl(seeds)
        if isinstance(seeds, list):
            for i in seeds:
                self.linkQuence.addUnvisitedUrl(i)
        print("Add the seeds url %s to the unvisited url list" % str(self.linkQuence.unVisited))

        # 抓取过程主函数
    def crawling(self, seeds, crawl_deepth):
            # 循环条件:抓取深度不超过crawl_deepth
            while self.current_deepth <= crawl_deepth:
                # 循环条件:待抓取的链接不空
                while not self.linkQuence.unVisitedUrlsEnmpy():
                    # 队头url出队列
                    visitUrl = self.linkQuence.unVisitedUrlDeQuence()
                    print("Pop out one url \"%s\" from unvisited url list" % visitUrl)

                    if visitUrl is None or visitUrl == "":
                        continue
                    # 获取超链接
                    links = self.getHyperLinks(visitUrl)
                    print("Get %d new links" % len(links))

                    # 将url放入已访问的url中
                    self.linkQuence.addVisitedUrl(visitUrl)
                    print("Visited url count: " + str(self.linkQuence.getVisitedUrlCount()))

                    print("Visited deepth: " + str(self.current_deepth))

                # 未访问的url入列
                for link in links:
                    self.linkQuence.addUnvisitedUrl(link)
                print("%d unvisited links:" % len(self.linkQuence.getUnvisitedUrl()))

                self.current_deepth += 1

    # 获取源码中得超链接
    def getHyperLinks(self, url):
        links = []
        data = self.getPageSource(url)

        soup = BeautifulSoup(data,'html.parser')
        a = soup.findAll("a", {"href": re.compile('^http|^/')})
        for i in a:
            if i["href"].find("http://") != -1:
                links.append(i["href"])
        return links

    # 获取网页源码
    def getPageSource(self, url):
        try:
            r = requests.get(url)
            r.raise_for_status()
            r.encoding = 'utf-8'
            return r.text
        except:
            return ''

def main(seeds, crawl_deepth):
    craw = MyCrawler(seeds)
    craw.crawling(seeds, crawl_deepth)

if __name__ == '__main__':
    main("http://www.sina.com.cn", 3)

爬虫深度优先搜索

深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链的HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。优点是能遍历一个Web 站点或深层嵌套的文档集合;缺点是因为Web结构相当深,,有可能造成一旦进去,再也出不来的情况发生。

代码实现

from bs4 import BeautifulSoup
import requests
import re

class linkQuence:
    def __init__(self):
        # 已访问的url集合
        self.visted = []
        # 待访问的url集合
        self.unVisited = []

    # 获取访问过的url队列
    def getVisitedUrl(self):
        return self.visted

    # 获取未访问的url队列
    def getUnvisitedUrl(self):
        return self.unVisited

    # 添加到访问过得url队列中
    def addVisitedUrl(self, url):
        self.visted.append(url)

    # 移除访问过得url
    def removeVisitedUrl(self, url):
        self.visted.remove(url)

    # 未访问过得url出队列
    def unVisitedUrlDeQuence(self):
        try:
            return self.unVisited.pop()
        except:
            return None

    # 保证每个url只被访问一次
    def addUnvisitedUrl(self, url):
        if url != "" and url not in self.visted and url not in self.unVisited:
            self.unVisited.insert(0, url)

    # 获得已访问的url数目
    def getVisitedUrlCount(self):
        return len(self.visted)

    # 获得未访问的url数目
    def getUnvistedUrlCount(self):
        return len(self.unVisited)

    # 判断未访问的url队列是否为空
    def unVisitedUrlsEnmpy(self):
        return len(self.unVisited) == 0


class MyCrawler:
    def __init__(self, seeds):

        # 使用种子初始化url队列
        self.linkQuence = linkQuence()
        if isinstance(seeds, str):
            self.linkQuence.addUnvisitedUrl(seeds)
        if isinstance(seeds, list):
            for i in seeds:
                self.linkQuence.addUnvisitedUrl(i)
        print("Add the seeds url %s to the unvisited url list" % str(self.linkQuence.unVisited))

        # 抓取过程主函数
    def crawling(self, seeds):

                # 循环条件:待抓取的链接不空
                while not self.linkQuence.unVisitedUrlsEnmpy():
                    # 队头url出队列
                    visitUrl = self.linkQuence.unVisitedUrlDeQuence()
                    print("Pop out one url \"%s\" from unvisited url list" % visitUrl)

                    if visitUrl is None or visitUrl == "":
                        break
                    # 获取超链接
                    links = self.getHyperLinks(visitUrl)
                    print("Get %d new links" % len(links))

                    # 将url放入已访问的url中
                    self.linkQuence.addVisitedUrl(visitUrl)
                    print("Visited url count: " + str(self.linkQuence.getVisitedUrlCount()))


                    # 未访问的url入列
                    for link in links:
                        self.linkQuence.addUnvisitedUrl(link)
                    print("%d unvisited links:" % len(self.linkQuence.getUnvisitedUrl()))



    # 获取源码中得超链接
    def getHyperLinks(self, url):
        links = []
        data = self.getPageSource(url)

        soup = BeautifulSoup(data,'html.parser')
        a = soup.findAll("a", {"href": re.compile('^http|^/')})
        for i in a:
            if i["href"].find("http://") != -1:
                links.append(i["href"])
        return links

    # 获取网页源码
    def getPageSource(self, url):
        try:
            r = requests.get(url)
            r.raise_for_status()
            r.encoding = 'utf-8'
            return r.text
        except:
            return ''

def main(seeds):
    craw = MyCrawler(seeds)
    craw.crawling(seeds)

if __name__ == '__main__':
    main("http://www.sina.com.cn")
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值