2.3 基于宽度优先搜索的网页爬虫原理讲解

Posted lvmememe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.3 基于宽度优先搜索的网页爬虫原理讲解相关的知识,希望对你有一定的参考价值。

  上一节我们下载并使用了宽度优先的爬虫,这一节我们来具体看一下这个爬虫的原理。

  首先,查看html.py的源代码。

第一个函数:

def get_html(url):
    try:
        par = urlparse(url)
        Default_Header = {\'X-Requested-With\': \'XMLHttpRequest\',
                          \'Referer\': par[0] + \'://\' + par[1],
                          \'User-Agent\': \'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36\',
                          \'Host\': par[1]}
        html = requests.get(url, headers=Default_Header, timeout=10)
        if html.status_code != 200:
            return None
        return html.content
    except Exception as e:
        print(e)
        return None

这个函数的作用是抓取url的内容(二进制内容,可以直接传进beautifulsoup里分析)。之所以显得比较复杂,是因为加入了一些异常处理,使得函数的可靠性更强一些。另外也加入了一些反爬虫的考虑,尽量模拟真实的浏览器(Referer和User-Agent等参数)。

 

第二个函数:

def full_link(url1, url2, flag_site=True):
    try:
        if url2[0] == \'#\':
            return None
        filepat = re.compile(r\'(.*?)\\.(.*?)\')
        htmpat = re.compile(r\'(.*?)\\.htm$|(.*?)\\.html$|(.*?)\\.php$|(.*?)\\.aspx$\')
        u1 = urlparse(url1)
        if filepat.match(u1.path) and not htmpat.match(u1.path):
            return None
        if url1[-1] == \'/\':
            url1 = url1+"index.html"
        elif filepat.match(u1.path) is None:
            url1 = url1+"/index.html"
        url2 = urljoin(url1,url2)
        u2 = urlparse(url2)
        if u1.netloc!=u2.netloc and flag_site:
            return None
        return url2
    except Exception as e:
        print(e)
        return None

这个函数其实是一个很关键的函数。因为宽度优先要想让while循环运转起来,就需要对队列的每一个元素都有一个通用的处理方法。这也是这个函数很关键的原因。它的作用是对于已知url1页面中,有一个<a>标签的href属性里面是url2,返回url2的真正完整链接是什么。当然,如果url2本身是一个完整链接,就直接返回它本身。但是如果它只是一个相对路径的链接,就需要经过处理之后再返回。比如,http://www.cnblogs.com/itlqs页面里面,链接出了一个./p/136810721.html,那么经过这个函数处理之后,返回的就是http://www.cnblogs.com/itlqs/p/6810721.html。其实Python自带的urljoin函数做的就是这个事情,但是经过试验发现内置的这个不是很完善,所以在这里修改了一下。在遇到异常,或者链接不符合要求的时候会返回None。

 

第三个函数:

def premake(url):  # 建立url所需要的目录
    if url[-1] == \'/\':
        url = url[:-1]
    up = urlparse(url)
    pat = re.compile(r\'(.*?)\\.htm$|(.*?)\\.html$|(.*?)\\.php$|(.*?)\\.aspx$\')
    path = up.path.split(\'/\')
    name = \'index.html\'
    if pat.match(up.path) is not None:
        name = path[-1]
        path = path[:-1]
    dirn = \'/\'.join(path)
    if up.query!=\'\':
        name = up.query+\' - \'+name
    os.makedirs(up.netloc + dirn, exist_ok=True)
    return up.netloc + dirn + \'/\' + name

这个函数的作用是建立url所需要的本地文件夹,这一步主要是为了在本地也保存原始的目录结构。而且把query的信息也在文件名中体现出来了。

 

第四个函数:

def save(url):
    url = url.replace(\'\\n\',\'\')
    fn = premake(url)
    html = get_html(url)
    if html is not None:
        with open(fn, \'wb\') as f:
            f.write(html)
    return html

把一个链接抓取并保存到本地。就是在前面三个函数的基础上写的。

 

这就是HTML.py。下面再来看一下crawler.py。前面的一些设置参数的部分就不看了,直接看宽搜的核心代码。

now = 0
while not q.empty():
    try:
        front = q.get()
        link = front[0]
        depth = front[1]
        print(\'crawling:\', link)
        html = HTML.save(link)
        if html is None:
            continue
        soup = BeautifulSoup(html, \'html.parser\', from_encoding=\'gb18030\')
        for a in soup.find_all(\'a\'):
            try:
                url2 = a[\'href\']
                fl = HTML.full_link(link, url2, flag_site)
                if fl is None:
                    continue
                if (fl not in pool) and (depth + 1 <= flag_depth):
                    pool.add(fl)
                    q.put((fl, depth + 1))
                    print(\'in queue:\', fl)
            except Exception as e:
                print(e)
        now += 1
        if now >= flag_most:
            break
    except Exception as e:
        print(e)

其实有了上面四个函数作为基础,就很容易了。每次从队头取一个链接。抓取并保存。然后提取出这个页面的所有href,然后用full_link函数得到完整链接,判断一下是否已经出现过,如果没有,加入队列中。有一个细节就是,为了避免gbk编码的网页和utf-8编码的网页分开处理,统一采用gb18030编码。

 

这就是这个程序的原理,一些实现细节可以揣摩一下代码,当然代码也有可能有不完善的地方,但是对于一些简单的抓取需求来说基本够用了。

 

以上是关于2.3 基于宽度优先搜索的网页爬虫原理讲解的主要内容,如果未能解决你的问题,请参考以下文章

网络爬虫——基于JAVA的宽度优先遍历互联网结点

网络爬虫的基本原理

人工智能基于八数码问题对比搜索算法的性能

人工智能基于八数码问题对比搜索算法的性能

Java 网络爬虫获取网页源代码原理及实现

第三百三十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—深度优先与广度优先原理