维基百科爬虫实战中,将采用的技术如下:
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
维基百科首页地址: 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 -