Python3写爬虫(三)利用BSF算法实现整个网站的爬取

广度优先搜索(BFS)算法,该部分内容涉及到图相关的算法,如果不了解图是什么,请参考书《零基础学数据结构》(C语言版)或清华大学严蔚敏《数据结构》,当然这需要一定的C语言基础,Python相关的数据结构和算法的书我还不知道,如果有,请在评论中告诉我,先谢了。你可能疑问,问什么不学算法而看数据结构相关的书籍呢,因为数据结构和算法是分不开的。

当然,不了解BFS,并不影响我们理解本文的内容,因为不过用了一个队列和一个集合而已。将要访问的网址放入队列中,访问后将新读取的网址进入队列,原网址从队列中弹出放入集合里(队列是前进后出)。只要集合里存在的网址,就不再访问。直到队列控、集合满,遍历结束。这就实现了遍历全网并不重复访问的作用。在这里我们并不讨论该算法的空间效率和时间效率。

下面我们就看下《零基础自学用Python 3开发网络爬虫(二): 用到的数据结构简介以及爬虫Ver1.0 alpha》,哎,不要笑我摘抄代码,学习阶段吗:

import re
import urllib.request
import urllib
from collections import deque
 
queue = deque()
visited = set()
 
url = 'http://news.dbanotes.net'  # 入口页面, 可以换成别的
 
queue.append(url)
cnt = 0
 
while queue:
    url = queue.popleft()  # 队首元素出队
    visited |= {url}       # 存入集合(标记为已访问)
 
    print('已经抓取: ' + str(cnt) + '   正在抓取 <---  ' + url)
    cnt += 1
    urlop = urllib.request.urlopen(url)
    if 'html' not in urlop.getheader('Content-Type'):
        continue
 
    # 避免程序异常中止, 用try..catch处理异常
    try:
        data = urlop.read().decode('utf-8')
    except:
        continue
 
    # 正则表达式提取页面中所有队列, 并判断是否已经访问过, 然后加入待爬队列
    linkre = re.compile('href=\"(.+?)\"')
    for x in linkre.findall(data):
        if 'http' in x and x not in visited:
            queue.append(x)
            print('加入队列 --->  ' + x)

urllib.request和urllib.parse,以及re三个模块的作用我们在前一篇文章中已经讲解过了,这里涉及两种没有接触过的数据结构:set和queue,也就是文章前面说的集合和队列,set数据结构保证‘元素’的唯一性,队列数据结构前进后出(跟生活中的‘队列’是一个含义,当然也有双端队列,但这显然不在这篇文章的讨论范围)

-------------------------------华丽分割----------------------------

整个循环内部处理流程:

(1)弹出队首元素(一个网址,一个字符串),将该字符串放入集合中(标记为已经访问)

    url = queue.popleft() # 队首元素出队 

    visited |= {url} # 存入集合(标记为已访问)

(2)抓取弹出的字符串(url)的网页,提取该网页中包含的网址。但是这里有个问题,每个链接对应的不一定的HTML页面(从前一篇文章《Python3写爬虫(二)爬取页面图片》中可以了解,其实提取的链接有可能对应图片,当然对应视频,flash,word等等都有可能),所以需要排除一下,如果页面不是HTML页面,直接舍弃。当然在这里如果我们添加一个if分之,就可以区分该该链接指向的文件类型,分类下载,这不是本文讨论重点,不赘述。

    urlop = urllib.request.urlopen(url)#抓取url指向的页面,不赘述

    if 'html' not in urlop.getheader('Content-Type'):#判断下页面是否为HTML页面,不是,不理会

        continue

    data = urlop.read().decode('utf-8')#调整抓取数据的编码格式,这个我一直头疼啊

在windows控制台CMD中输出的页面一直存在乱码的问题,调试了两台,还是如此,后来放弃了,反正以后的数据都是存储在文件和数据库中,输出内容也是在可视化页面或者HTML中,安啦。据说相比Python2,Python3在字符编码中有很大的调整,所有新手入门很多都在字符编码上头疼,当然我入门的是Python3.X。

    linkre = re.compile('href=\"(.+?)\"')

    linkre.findall(data)

然后,利用上一篇文章中提到的,正则表达式,对字符串进行检索,得到所有的网址数据存入一个字符串列表中(list  of string),当然,这个字符串列表中的每一项,都是一个网页链接(href=""),findall()函数相关的内容,详见:https://docs.python.org/3/library/re.html#re.findal

(3)将新得到的字符串列表存入队列中,然后继续下一次循环

    queue.append(x)

---------------------------华丽分割----------------------------

当然,这个小程序只是个雏形,便于讲解,Not Elegance。

例如,爬取一段时间页面后,程序退出,控制台显示:

urllib.error.HTTPError: HTTP Error 403: Forbidden

之所以出现上面的异常,是因为如果用 urllib.request.urlopen 方式打开一个URL,服务器端只会收到一个单纯的对于该页面访问的请求,但是服务器并不知道发送这个请求使用的浏览器,操作系统,硬件平台等信息,而缺失这些信息的请求往往都是非正常的访问,例如爬虫有些网站为了防止这种非正常的访问,会验证请求信息中的UserAgent(它的信息包括硬件平台、系统软件、应用软件和用户个人偏好),如果UserAgent存在异常或者是不存在,那么这次请求将会被拒绝(如上错误信息所示)(以上文字摘自《Python 3.x 中"HTTP Error 403: Forbidden"问题的解决方案》)

要想解决这一问题,就需要利用伪浏览器的方式浏览网页页码,也就是在请求中加入UserAgengt的信息,后面内容中,详细讲解。




你可能感兴趣的:(Python3写爬虫(三)利用BSF算法实现整个网站的爬取)