一个爬虫程序得开发顺序: 需求分析 概念设计 详细设计 编码 测试 使用
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) # 打印该网页中的所有链接
经历了上一步的爬取后,我们获取了当前网页的所有链接的URL。但是我们是否可以对所爬取的链接再进行进一步的爬取呢?下面我们尝试对网页进行深度优先爬取。 在获得了第一层网页的链接信息后,对URL进行拼接,并不断得对新获取URL进行爬取
#实现深度优先爬取
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)
#或者使用广度优先爬取
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)
从上一个爬虫爬取的结果可以看出,由于网页的最后一个链接都是相同的,所以在第一次爬取后再爬取的所有信息都将是同一个链接,即爬取的是重复信息,而且爬虫将陷入无限循环。 因此我们需要对爬虫进行改进,当网页链接中存在相同链接时,有必要对URL进行去重处理。即我们可以将爬取过的URL存放在一个元素集合中,在进行新的爬取之前将目标URL与爬取过的集合进行对比,只爬取元素集合中没有的URL,就可以完成去重处理了。并且设置限制爬取链接的层数。
#加层数控制的
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)
使用ScalableBloomFilter方法去重,设置了链接的限制爬取层数。并编写了getinfo函数以对数据进行格式化处理。 并且为了方便数据之后的使用,编写saveinfile函数,将爬取的数据存入到文件中。
#加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)
初始化实现层数控制:实际上在到达控制层数之前都是执行深度优先,在控制层需要宽度优先
# 传统的不限制层数的操作
#转换成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)
# 可以控制层数的深度优先爬虫
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)