爬虫实践 | 维基百科深度优先与广度优先的开展
维基百科爬虫实战中,将采用的技术如下:
- 爬取网页:静态网页
- 解析网页;正则表达式
- 存储数据:txt文本存储
- 扩展:深度优先的递归爬虫和广度优先的多线程爬虫
1.项目描述
1.1项目目标
本爬虫目标为爬取维基百科上词条的链接,注意一点,在运行爬虫时注意不要过快,过频密的请求爬取维基百科网页,以免对服务器产生大量负荷。
1.2项目描述
如果需要爬取一个网站上的所有链接,采取什么方法比较好呢?
可以找到该网站上的一个网页,如主页,获取主页的内容,分析网页内容并找到网页上所有本站链接,然后爬取这些获得的链接,再分析这些链接网页上的内容,找到上面所有本站链接,并不断重复直到没有新的链接为止。
本次用于实践一个维基百科爬虫,不需要全站爬取,所以设定爬取深度为2,如果有兴趣,你们可以爬取更大的深度。
1.3深度优先和广度优先
如何把整个网站所有网页爬取一遍呢?这里说到两种算法:基于深度优先饿遍历和基于广度优先的遍历。
深度优先的遍历:可以描述为“不撞南墙不回头”,具体一点就是首先访问第一个邻接节点,然后以这个被访问的邻接节点作为初始节点,访问它的第一个邻接节点。访问策略是优先往纵向挖掘深入,直到到达指定的深度或该节点不存在邻接节点,才回掉头访问第二条路。
就像维基百科为例,假设现在的深度为3,深度优先遍历,如下:
基于深度优先的爬虫路径为:1->2->6->7->8->3->4->5
广度优先的遍历:可以描述为“一层一层地剥开我的心”,具体点就是,从某个顶点出发,首先访问这个顶点,然后找出这个节点的所有未被访问的邻接节点访问完后再访问这些节点中第一个邻接节点的所有节点,重复此方法,直到所有节点都被访问完为止。访问策略采用先访问完一个深度的所有节点,再访问更深一层的所有节点,并采用FIFO(先进先出)的策略。
基于广度优先的爬虫路径为:1->2->3->4->5->6->7->8
2 网站分析
维基百科首页地址: https://en.wikipedia.org/wiki/Wikipedia ,也就是Wikipedia词条的页面。
取出本页面所有链接,代码如下:
import requests
from bs4 import BeautifulSoup
import time
headers = {'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
r = requests.get("https://en.wikipedia.org/wiki/Wikipedia", headers= headers)
html = r.text
bsObj = BeautifulSoup(html, "lxml")
for link in bsObj.findAll("a"):
if 'href' in link.attrs:
print (link.attrs['href'])
3 项目实施(深度优先的递归爬虫)
使用深度优先爬虫,爬取所有词条链接,爬虫深度为2,代码如下:
import requests
import re
import time
exist_url = []
news_ids = []
g_writecount = 0
def scrappy(url, depth = 1):
global g_writecount
try:
headers = {'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
r = requests.get("https://en.wikipedia.org/wiki/" + url, headers= headers)
html = r.text
except Exception as e:
print ('Failed downloading and saving', url)
print (e)
exist_url.append(url)
return None
exist_url.append(url)
link_list = re.findall('<a href="/wiki/([^:#=<>]*?)".*?</a>',html)
unique_list = list(set(link_list) - set(exist_url))
for eachone in unique_list:
g_writecount += 1
output = "No." + str(g_writecount) + "t Depth:" + str(depth) + "t"+ url + ' -> ' + eachone + 'n'
#print (output)
with open('link_12-3.txt', "a+") as f:
f.write(output)
f.close()
if depth < 2:
scrappy(eachone, depth+1)
scrappy("Wikipedia")
4 项目进阶(广度优先的多线程爬虫)
import threading
import requests
import re
import time
g_mutex = threading.Condition()
g_pages = [] #从中解析所有url链接
g_queueURL = [] #等待爬取的url链接列表
g_existURL = [] #已经爬取过的url链接列表
g_writecount = 0 #找到的链接数
class Crawler:
def __init__(self,url,threadnum):
self.url=url
self.threadnum=threadnum
self.threadpool=[]
def craw(self): #爬虫的控制大脑,包括爬取网页,更新队列
global g_queueURL
g_queueURL.append(url)
depth=1
while(depth < 3):
print ('Searching depth ',depth,'...n')
self.downloadAll()
self.updateQueueURL()
g_pages = []
depth += 1
def downloadAll(self): #调用多线程爬虫,在小于线程最大值和没爬完队列之前,会增加线程
global g_queueURL
i=0
while i<len(g_queueURL):
j=0
while j<self.threadnum and i+j < len(g_queueURL):
threadresult = self.download(g_queueURL[i+j],j)
j+=1
i += j
for thread in self.threadpool:
thread.join(30)
threadpool=[]
g_queueURL=[]
def download(self,url,tid): #调用多线程爬虫
crawthread=CrawlerThread(url,tid)
self.threadpool.append(crawthread)
crawthread.start()
def updateQueueURL(self): #完成一个深度的爬虫之后,更新队列
global g_queueURL
global g_existURL
newUrlList=[]
for content in g_pages:
newUrlList+=self.getUrl(content)
g_queueURL=list(set(newUrlList)-set(g_existURL))
def getUrl(self,content): #从获取的网页中解析url
link_list = re.findall('<a href="/wiki/([^:#=<>]*?)".*?</a>',content)
unique_list = list(set(link_list))
return unique_list
class CrawlerThread(threading.Thread): #爬虫线程
def __init__(self,url,tid):
threading.Thread.__init__(self)
self.url=url
self.tid=tid
def run(self):
global g_mutex
global g_writecount
try:
print (self.tid, "crawl ", self.url)
headers = {'User-Agent' : 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
r = requests.get("https://en.wikipedia.org/wiki/" + self.url, headers= headers)
html = r.text
link_list2 = re.findall('<a href="/wiki/([^:#=<>]*?)".*?</a>',html)
unique_list2 = list(set(link_list2))
for eachone in unique_list2:
g_writecount += 1
content2 = "No." + str(g_writecount) + "t Thread" + str(self.tid) + "t"+ self.url + '->' + eachone +'n'
with open('title2.txt', "a+") as f:
f.write(content2)
f.close()
except Exception as e:
g_mutex.acquire()
g_existURL.append(self.url)
g_mutex.release()
print ('Failed downloading and saving',self.url)
print (e)
return None
g_mutex.acquire()
g_pages.append(html)
g_existURL.append(self.url)
g_mutex.release()
if __name__ == "__main__":
url = "Wikipedia"
threadnum = 5
crawler = Crawler(url,threadnum)
crawler.craw()
- End -
- python使用正则表达式
- python在租房过程中的应用
- python爬虫反爬取---设置IP代理自动变换requests.get()中proxy的IP
- 【译】TensorFlow实现Batch Normalization
- 关于Python语言规范你需要知道的一些小tips
- R语言可视化——REmap(路径图)
- python面向对象
- 字符串hash入门
- R语言数据处理——数据合并与追加
- python爬取链家租房之获取房屋页面的详细信息(房名,地址,房价,面积,url)
- 信用卡“坏账”客户分析(二)
- Mac上提升python运算速度-PyPy初体验
- python处理json数据(复杂的json转化成嵌套字典并处理)
- Pointer-network理论及tensorflow实战
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 面向对象视角下的前端工程体系
- 使用 Python破解大众点评字体加密(SVG反爬虫)
- Python爬虫练习:爬取高清4K桌面壁纸
- 爬取B站18000条《黑神话:悟空》实机演示弹幕,做成词云
- Python爬虫实战:自动化登录网站,爬取商品数据
- 符合自己的工作难找?取招聘网站数据,让你找到心仪的工作
- 虽然现在有可以去码的软件了,可视频是如何自动跟踪打码的?
- Python爬取NBA虎扑球员数据
- 发现一个好看的手机壁纸网站,撸代码的手已经饥渴难耐了
- 手把手用python教你拿即时的卫星影像装饰你的桌面
- PyCharm2019亲测破解方式
- :: 是什么语法?
- 支付宝二面:Mybatis接口Mapper内的方法为啥不能重载吗?我直接懵逼了...
- Windows系统中使用Linux命令(可以批量下载Modis数据)
- Python制作桑基图(我承认我低估了这个教程)