python簡單框架實現爬取NBA球員資料
阿新 • • 發佈:2019-02-15
寒假期間就像爬取球員資料,一直 沒有動手寫。一下是程式碼和註釋,存在一些小小的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()
以上為全部程式碼,歡迎指出錯誤。(關於函式的訪問控制在此沒有涉及,這是個毛病)