博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python3写爬虫(三)利用BSF算法实现整个网站的爬取
阅读量:6270 次
发布时间:2019-06-22

本文共 2797 字,大约阅读时间需要 9 分钟。

hot3.png

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

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

下面我们就看下《》,哎,不要笑我摘抄代码,学习阶段吗:

import reimport urllib.requestimport urllibfrom 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页面(从前一篇文章《》中可以了解,其实提取的链接有可能对应图片,当然对应视频,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()函数相关的内容,详见:

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

    queue.append(x)

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

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

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

urllib.error.HTTPError: HTTP Error 403: Forbidden

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

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

转载于:https://my.oschina.net/lifeisshort/blog/395203

你可能感兴趣的文章
MFC应用程序向导生成的文件
查看>>
Oracle体系结构之oracle密码文件管理
查看>>
【leetcode】Remove Element (easy)
查看>>
mysql多表查询及其 group by 组内排序
查看>>
alsa的snd_pcm_readi()函数和snd_pcm_writei()
查看>>
Android学习网站推荐(转)
查看>>
嵌入式根文件系统的移植和制作详解
查看>>
MEF部件的生命周期(PartCreationPolicy)
查看>>
LCD的接口类型详解
查看>>
nginx 基础文档
查看>>
LintCode: Unique Characters
查看>>
Jackson序列化和反序列化Json数据完整示例
查看>>
.net 中的DllImport
查看>>
nyoj 517 最小公倍数 【java睑板】
查看>>
include与jsp:include区别
查看>>
ftp的20 21端口和主动被动模式
查看>>
MySQL存储引擎选型
查看>>
Java中的statickeyword具体解释
查看>>
Linux车载系统的开发方向
查看>>
并发编程之五--ThreadLocal
查看>>