前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Chapter06 | 面向百度百科得深度与宽度优先爬虫

Chapter06 | 面向百度百科得深度与宽度优先爬虫

作者头像
不温卜火
发布2020-10-28 14:56:41
6010
发布2020-10-28 14:56:41
举报
文章被收录于专栏:不温卜火

一个爬虫程序得开发顺序: 需求分析 概念设计 详细设计 编码 测试 使用

一、需求分析

1.1、爬什么

  • 网站=>百度百科:网络爬虫词条开始得三层节点=>了解网站结构
  • 数据=>词条名称、URL、描述、关键字信息=>了解数据存放位置

1.2、存哪里

  • 位置=>本地磁盘文件=>确定存放位置、文件类型

1.3、怎么爬

  • 网站=>百度百科
  • 策略=>无更新(百度知识比较稳定)、深度/广度优先

1.4、怎么抽

  • 数据=>description、keyword、summary
  • 方法=>字符串截取

1.5、怎么存

  • 载体=>文件

二、详细设计

2.1、怎么爬

  • 选一个python库作为主力库
  • 无更新(百度百科较稳定) =>即爬即弃
  • 深度/广度优先=>数据结构设计
  • URL去重=>逻辑设计、方法选择

2.2、怎么抽

  • 字符串截取=>str自带方法
  • 正则表达式=>设计正则表达式
  • Python库抽取=>bs4

2.3、怎么存

  • 文件=>txt文件单存与批量存法

三、案例代码

3.1、抽取url

代码语言:javascript
复制
import urllib.request as ur
import re
r = ur.urlopen("https://baike.baidu.com/item/"
               "%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB") # 对百度百科关键词条为网络爬虫的URL的进行访问
content = r.read().decode('utf-8')
href = re.compile(r'href=[\'"]?(/item[^\'" >]+)') # 利用正则表达式将网页中所需的链接表达出来
new_urls=href.findall(content) # 使用findall方法将所有链接信息抽取出来
print(new_urls) # 打印该网页中的所有链接

3.2、获取新的url

经历了上一步的爬取后,我们获取了当前网页的所有链接的URL。但是我们是否可以对所爬取的链接再进行进一步的爬取呢?下面我们尝试对网页进行深度优先爬取。 在获得了第一层网页的链接信息后,对URL进行拼接,并不断得对新获取URL进行爬取

代码语言:javascript
复制
#实现深度优先爬取
count = 0
r = re.compile(r'href=[\'"]?(/item[^\'" >]+)') # 抽取所需链接信息的正则语言规则
seed = "/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB" # 这是网络爬虫词条
stack = [seed] # 设置种子链接的栈(使用列表模拟栈)
storage = {}
while count < 8:
    try:
        url = stack.pop(-1) # 取出栈的最后一条URL
        html = ur.urlopen("https://baike.baidu.com"+url).read().decode('utf-8') # 对URL进行拼接
        new_urls = r.findall(html) # 提取当前网页下的所有链接URL信息
        print(new_urls)
        stack.extend(new_urls) # 将新提取的链接信息入队列
        storage[url] = len(new_urls)
        count += 1
    except Exception as e:
        print(url)
        print(e)
代码语言:javascript
复制
#或者使用广度优先爬取
count = 0
r = re.compile(r'href=[\'"]?(/item[^\'" >]+)') # 抽取所需链接信息的正则语言规则
seed = "/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB" # 这是网络爬虫词条
queue = [seed] # 设置种子链接的队列(使用列表模拟队列)
storage = {}
while count < 8:
    try:
        url = queue.pop(0) # 取出队列最后一条URL
        html = ur.urlopen("https://baike.baidu.com"+url).read().decode('utf-8') # 对URL进行拼接
        new_urls = r.findall(html) # 提取当前网页下的所有链接URL信息
        print(new_urls)
        queue.extend(new_urls) # 将新提取的链接信息入队列
        storage[url] = len(new_urls)
        count += 1
    except Exception as e:
        print(url)
        print(e)

3.3 去重

1、使用set()方法进行URL去重和层数控制

从上一个爬虫爬取的结果可以看出,由于网页的最后一个链接都是相同的,所以在第一次爬取后再爬取的所有信息都将是同一个链接,即爬取的是重复信息,而且爬虫将陷入无限循环。 因此我们需要对爬虫进行改进,当网页链接中存在相同链接时,有必要对URL进行去重处理。即我们可以将爬取过的URL存放在一个元素集合中,在进行新的爬取之前将目标URL与爬取过的集合进行对比,只爬取元素集合中没有的URL,就可以完成去重处理了。并且设置限制爬取链接的层数。

代码语言:javascript
复制
#加层数控制的
count = 0 # 层数
floors = 1 # 限制爬取的层数
lastStep = []
r = re.compile(r'href=[\'"]?(/item[^\'" >]+)')
seed = '/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB' # 这是网络爬虫词条
queue = [[seed]]
for i in range(floors): # 限制爬取范围在设定的层数范围内
    queue.append([])
storage = {}
used = set() # 设置集合存放爬取过的url
while len(queue[0])>0 or count!=0: # 种子队列不为空或者层数不为零
    try:
        url = queue[count].pop(-1)
        print(url+" "+str(count)) # 打印当前链接和层数
        html = ur.urlopen('https://baike.baidu.com'+url).read().decode('utf-8')
        storage[url]=html
        used.add(url) # 将爬取过的URL放入集合中
        new_urls = r.findall(html)
        if count < floors:
            for new_url in set(new_urls):
                if new_url not in used and new_url not in queue:#判断新链接网址中的包含的链接是否为重复的
                    queue[count+1].append(new_url) # 将爬取的URL存入到队列中相应层数的列表
            count+=1
        else:
            if len(queue[count])==0:
                count -= 1
    except Exception as e:
        print(url)
        print(e)

2、使用bloom_filter去重

使用ScalableBloomFilter方法去重,设置了链接的限制爬取层数。并编写了getinfo函数以对数据进行格式化处理。 并且为了方便数据之后的使用,编写saveinfile函数,将爬取的数据存入到文件中。

代码语言:javascript
复制
#加bloom filter和文件的
from __future__ import unicode_literals
import urllib.request as ur
import re
from pybloom_live import ScalableBloomFilter
import json
import codecs

def getinfo(html,urls): # 格式化爬取的数据,并将其存入字典中
    dict={}
    if html.find("<title>") and html.find("</title>"):
        dict['title'] = (html[html.find("<title>") + 7:html.find("</title>")])
    if html.find("name=\"description\" content=\"") and html.find("..."):
        dict['description']=(html[html.find("name=\"description\" content=\"")+28:html.find("...")])
    if html.find("name=\"keywords\" content=\"") and html.find("<meta name=\"image\""):
        dict['keywords']=(html[html.find("name=\"keywords\" content=\"")+25:html.find("<meta name=\"image\"")-3])
    dict['html']=html
    dict['childurls']=list(urls)
    return dict

def saveinfile(data): # 创建文件存储爬取的数据
    fp = codecs.open('.\output.txt', 'a+', 'utf-8') # 初始化创建文件的方法
    fp.write(json.dumps(data, ensure_ascii=False)) # 将数据写入文件
    fp.close()
#初始化ScalableBloomFilter去重方法
urlbloomfilter=ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
count = 0 # 层数
floors = 1 # 设置爬取链接的层数限制
lastStep = []
r = re.compile(r'href=[\'"]?(/item[^\'" >]+)')
seed = '/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB' # 这是网络爬虫词条
urlbloomfilter.add(seed)
queue = [[seed]]
for i in range(floors): # 限制爬取范围在设置的层数范围内
	queue.append([])
storage = {}
used = set()
while len(queue[0])>0 or count!=0:
    try:
        url = queue[count].pop(-1)
        print(url+" "+str(count))
        html = ur.urlopen('https://baike.baidu.com'+url).read().decode('utf-8')
        new_urls = r.findall(html)
        storage[ur.unquote(url[6:])]=getinfo(html,set(new_urls)) # 使用unquote解码,并调用getinfo函数将格式化后的数据存入字典中
        if count < floors:
            for new_url in set(new_urls):
                if new_url not in urlbloomfilter:
                    urlbloomfilter.add(new_url) # 将爬取过的URL存入去重过滤器
                    queue[count+1].append(new_url)
            count+=1
        else:
            if len(queue[count])==0:
                count -= 1
    except Exception as e:
        print(url)
        print(e)
saveinfile(storage)

四、exp

4.1、可以控制格式的爬虫

初始化实现层数控制:实际上在到达控制层数之前都是执行深度优先,在控制层需要宽度优先

代码语言:javascript
复制
# 传统的不限制层数的操作
#转换成utf-8,网页不会被转码,可以直接观看
import urllib.request as ur
import re


#初始化参数
count = 3   # 控制层数
# 初始化数据存储结构
r = re.compile(r'href=[\'"]?(/item[^\'" >]+)')
seed = "/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB" # 这是网络爬虫词条
queue = [seed]  # 设置种子链接的队列(使用列表模拟队列)
storage = {}
# 初始化实现层数控制:实际上在到达控制层数之前都是执行深度优先,在控制层需要宽度优先
while len(queue) > 0:
    try:
        url = queue.pop(-1)  # 取出队列最后一条URL
        html = ur.urlopen("https://baike.baidu.com" + url).read().decode('utf-8')  # 对URL进行拼接
        print(html)
        new_urls = r.findall(html)  # 提取当前网页下的所有链接URL信息
        queue.extend(new_urls)
        storage[url] = len(new_urls)
    except Exception as e:
        print(url)
        print(e)
1
1

4.2、可以控制层数的深度优先爬虫

代码语言:javascript
复制
# 可以控制层数的深度优先爬虫
import urllib.request as ur
import re

#初始化参数
count = 0   # 控制层数
floors = 2 # 在程序开始时可以设定需要爬取的层数
# 初始化数据存储结构
r = re.compile(r'href=[\'"]?(/item[^\'" >]+)')
seed = "/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB" # 这是网络爬虫词条
queue = [[seed]]  # 设置种子链接的队列(使用列表模拟队列)
# 初始化实现层数控制:实际上在到达控制层数之前都是执行深度优先,在控制层需要宽度优先
# 嵌套list在不去改变爬虫的深度优先的策略基础上,保证队列处于一个堆栈的状态
for i in range(floors):
    queue.append([])  # 使用嵌套list存放不同层的数据
storage = {}     # 字典型,用于保存数据
used = set()     # 集合,自带去重的特性
while len(queue[0])>0 or count!=0:   # 所有层数爬取完毕+列表中没有url时结束
    try:
        # 取出队列最后一条URL
        url = queue[count].pop(-1)
        print(url+" "+str(count))
        html = ur.urlopen('https://baike.baidu.com'+url).read().decode('utf-8')  # 对URL进行拼接
        storage[url] = html
        used.add(url)
        new_urls = r.findall(html)  # 提取当前网页下的所有链接URL信息
        # 在没有达到控制层前,页面引入的URL都会被加入到该层的list中,完成深度优先
        if count < floors:
            for new_url in set(new_urls):
                if new_url not in used and new_url not in queue:
                    queue[count+1].append(new_url)
            count+=1
        # 到达控制层,不再增加url,直接通过list.pop完成开暗渡优先
        else:
            if len(queue[count]) == 0:
                count -= 1
    except Exception as e:
        print(url)
        print(e)
2
2
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/04/03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、需求分析
    • 1.1、爬什么
      • 1.2、存哪里
        • 1.3、怎么爬
          • 1.4、怎么抽
            • 1.5、怎么存
            • 二、详细设计
              • 2.1、怎么爬
                • 2.2、怎么抽
                  • 2.3、怎么存
                  • 三、案例代码
                    • 3.1、抽取url
                      • 3.2、获取新的url
                        • 3.3 去重
                          • 1、使用set()方法进行URL去重和层数控制
                          • 2、使用bloom_filter去重
                      • 四、exp
                        • 4.1、可以控制格式的爬虫
                          • 4.2、可以控制层数的深度优先爬虫
                          相关产品与服务
                          数据保险箱
                          数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档