1. 程式人生 > >python簡單框架實現爬取NBA球員資料

python簡單框架實現爬取NBA球員資料

寒假期間就像爬取球員資料,一直 沒有動手寫。一下是程式碼和註釋,存在一些小小的bug,就是檔案寫入問題,學習完寫入exel後再修改。

爬蟲總排程程式:

#-*-coding:UTF-8 -*- from Html_Download import HtmlDownload #從Html_Download.py檔案中匯入HtmlDownload類,以下相同 from Url_Manage import UrlManage from Html_Parse import HtmlParse from Output_Data import OutputData class NBASpiderMain(): #定義一個NBASpiderMain()類 def __init__(self): #個人理解對類進行初始化。關於更多__init__的理解詳見:http://www.crifan.com/summary_the_meaning_of_self_and___init___in_python_and_why_need_them/ self.urls = UrlManage() #建立urls,download,parser,oupter物件。分別對應爬蟲框架的 self.downloader = HtmlDownload() #url管理器,下載器,解析器,和對檔案的輸出 self.parser = HtmlParse() self.outputer = OutputData() def craw(self,root_url): #定義一個爬取的方法 self.urls.addUrl(root_url) #將原始URL通過urls的addUrl方法新增到待爬取的URL池中 while self.urls.hasUrl(): #urls物件呼叫hasUrl方法,判斷URL池中是否含有待爬取的URL url = self.urls.getUrl() #如果存在待爬取的URL,通過urls的getUrl方法獲取一個待爬取URL html_count = self.downloader.getHtmlCount(url) #通過呼叫下載器的getHtmlCount()方法去獲取網頁原始碼 playerUrls = self.parser.getPlayerUrls(html_count) #通過解析,得到第二層待爬取的URL(球員資訊所在的頁面)。返回一個列表 while playerUrls: #爬取後的得到的列表中的URL不為空 playerUrl = playerUrls.pop() #pop()方法,得到一個列表中元素,同時在列表中刪除這個元素。(拿出來不放回去) self.urls.addPlayerUrl(playerUrl) #將以爬取的球員資訊URL儲存在一個列表中 HtmlPlayer = self.downloader.getHtmlCount(playerUrl) #爬取球員資訊URL,得其原始碼 PlayerData = self.parser.getPlayerData(HtmlPlayer) #解析原始碼,得球員資訊 self.outputer.writeData(PlayerData) #將球員資訊寫入檔案 print(playerUrl+"資料爬取完成") if __name__ == "__main__": root_url = "http://nba.sports.sina.com.cn/players.php?dpc=1" obj_spider = NBASpiderMain() #建立一個爬取NBA資料的物件 obj_spider.craw(root_url) #呼叫物件的爬取方法 url管理器:
#-*-coding:UTF-8 -*-
class UrlManage(object):
	''' URL管理器,管理URL池'''
	def __init__(self):					#初始化此類
		self.new_url=set()				#建立一個new_url集合,用來儲存待爬取得URL
		#self.player_url = set()
		self.old_player_url = set()		#建立一個已經爬取的球員資訊URL
	def hasUrl(self):					#建立判斷待爬取的列表中是否存在URL
		if self.new_url:				#如果待爬取的列表不為空	
			return True
		else:
			False
	def addUrl(self,url):				#增加待爬取的URL到列表中
		if url not in self.new_url:
			self.new_url.add(url)
		else:
			return "該url已經爬取過"
	def getUrl(self):					#獲取待爬取的URL中的一個URL元素
		if self.new_url:
			return self.new_url.pop()
		else:
			return "已經沒有可爬取的URL"
	def addPlayerUrl(self,url):			#增加一個已經爬取的球員資訊URL到列表中
		self.old_player_url.add(url)
		return

下載器:
#-*-coding:UTF-8 -*-
import requests
class HtmlDownload(object):
	"""docstring for HtmlDownload"""
	def getHtmlCount(self,url):
		try:
			r = requests.get(url)
			r.raise_for_status()
			r.encoding = r.apparent_encoding
			return r.text
		except Exception as e :
			print(e)
			print("下載網頁原始碼失敗")
		

解析器:
#-*-coding:UTF-8-*-
import re
from bs4 import BeautifulSoup
class HtmlParse(object):
	"""docstring for HtmlParse用於解析HTML來得到球員資訊的類"""
	def __init__(self):											#初始化解析器
		self.playerUrl=set()
		self.HtmlData = []

	def getPlayerUrls(self,html_count):							#解析原始頁面中各個球員資訊的URL,返回一個集合
		soup = BeautifulSoup(html_count,"html.parser")
		playerUrlsPart = soup.find_all('a')
		for url in playerUrlsPart:

			path = re.findall(r'a href="player\.php?(.+)">.*</a>',str(url))
			if path:
				self.playerUrl.add("http://nba.sports.sina.com.cn/player.php"+"".join(i for i in path))
				print("http://nba.sports.sina.com.cn/player.php"+"".join(i for i in path))
			else:
				continue
		return self.playerUrl
	def getPlayerName(self,html_count):							#由於BeautifulSoup沒有解析出名字,使用之前的正則表示式解析
		player_name = re.findall(r'<strong style="font-size:14px;">(.+)</strong>',html_count)
		return player_name
	def getPlayerTeam(self,html_count):							#由於BeautifulSoup沒有解析出球隊名字,使用之前的正則表示式解析
		team_name = re.findall(r'<a href="http://nba\.sports\.sina\.com\.cn/team/.+\.shtml" target="_blank">(.+)</a>',html_count)
		return team_name[0]										#解析出該球員每個賽季雖所在的球隊名字,選取第一個就是今年現在所在的	
	def getPlayerData(self,html_count):							#通過BeautifulSoup解析球員資訊。解析出為下面註釋的列表內容,通過索引得到想要的資訊					
		soup = BeautifulSoup(html_count,"html.parser")
		player_data = {}										#通過字典來儲存球員資訊的物件
		tr  = soup.find_all(bgcolor=re.compile(r'#fcac08'))		#find_all()方法加正則來尋找球員資訊所在的<tr>標籤。加入正則目的是過濾掉其他<tr>的內容
		for i in tr:											#返回的是列表,所以遍歷
			for x in i.find_all('td'):							#對每個<tr>標籤,向下尋找子標籤<td>,返回還是列表,所以遍歷
				self.HtmlData.append(x.string)					#返回每個<td>標籤的String內容,並新增到一個列表中
		player_data["name"] = self.getPlayerName(html_count)
		player_data["team_name"] = self.getPlayerTeam(html_count)
		player_data["age"] = self.HtmlData[4]
		player_data["birthday"] = self.HtmlData[2]
		player_data["height"] = self.HtmlData[10]
		player_data["weight"] = self.HtmlData[12]
		player_data["veteran"] = self.HtmlData[-7]
		
		return player_data
		'''
		從網頁上通過BeautifulSoup上解析出來的資料,儲存在列表中
		[None, '生\u3000\u3000日', '1984-03-24', '年\u3000\u3000齡', '33歲', '出 生 地', '德克薩斯州達拉斯', '畢業學校', 'Georgi
a Tech', '身\u3000\u3000高', '\r\n\t2.11米(6英尺11英寸)\r\n\t', '體\u3000\u3000重', '\r\n\t107公斤(235磅)', '進入 NBA',
'2003年', 'NBA 球齡', '13年', '傷病情況', None, '停賽情況', None, '選秀情況', '\r\n\t2003年第1輪第4順位被猛龍隊選中']
		'''
	


	'''
	一下程式碼為當初分析時使用的正則,當爬取到體重身高資料時不會爬取了,改用BeautifulSoup網頁解析



	def getPlayerData_2(self,html_count):
		player_data = {}
		player_data["name"] = self.getPlayerName(html_count)
		player_data["age"] = self.getPlayerAge(html_count)
		player_data["birthday"] = self.getPlayerBirthday(html_count)
		player_data["height"] = self.getPlayerHeigth(html_count)
		player_data["weight"] = self.getPlayerWeight(html_count)
		player["veteran"] = self.getPlayerVeteran(html_count)
		return player_data
	def getPlayerName(self,html_count,soup):
		player_name = re.findall(r'<strong style="font-size:14px;">(.+)</strong>',html_count)
		return player_name
	def getPlayerTeam(self,html_count):
		team_name = re.findall(r'<a href="http://nba\.sports\.sina\.com\.cn/team/.+\.shtml" target="_blank">(.+)</a>',html_count)
		return TeamName
	def getPlayerAge(self,html_count):
		player_age = re.findall(r'<td width=197>(.*)</td>',html_count)[1]
		return player_age
	def getPlayerBirthday(self,html_count):
		player_birthday = re.findall(r'<td width=197>(.*)</td>',r.text)[0]
		return player_birthday
	def getPlayerHeigth(self,html_count):
		p
	def getPlayerWeight(self,html_count):
		pass
	def getPlayerVeteran(self,html_count):
		pass
	'''
	

輸出資料:
#-*-encoding:UTF-8  -*-
import os 
class OutputData(object):
    def __int__(self):
        self.path = r"D:\\NBAPlayerData"
    def writeData(self,player_data):
        self.path = r"D:\\NBAPlayerData"
        if os._exists(self.path):
            f = open(path+".txt","a")


            data = str(player_data["name"])+" "+str(player_data["team_name"])+" "+str(player_data["age"])+"  "+str(player_data["birthday"])+"  "+str(player_data["height"])+"  "+str(player_data["weight"])+"  "+str(player_data["veteran"])
            f.write(data)
            f.close()


以上為全部程式碼,歡迎指出錯誤。(關於函式的訪問控制在此沒有涉及,這是個毛病)