使用Python抓取歐洲足球聯賽數據進行大數據分析

jopen 10年前發布 | 174K 次閱讀 Python 網絡爬蟲

背景

Web Scraping

在大數據時代,一切都要用數據來說話,大數據處理的過程一般需要經過以下的幾個步驟

  • 數據的采集和獲取

  • 數據的清洗,抽取,變形和裝載

  • 數據的分析,探索和預測

  • 數據的展現

其中首先要做的就是獲取數據,并提煉出有效地數據,為下一步的分析做好準備。

數據的來源多種多樣,以為我本身是足球愛好者,而世界杯就要來了,所以我就想提取歐洲聯賽的數據來做一個分析。許多的網站都提供了詳細的足球數據,例如:

這些網站都提供了詳細的足球數據,然而為了進一步的分析,我們希望數據以格式化的形式存儲,那么如何把這些網站提供的網頁數據轉換成格式化的數據呢?這就要用到Web scraping的技術了。簡單地說,Web Scraping就是從網站抽取信息, 通常利用程序來模擬人瀏覽網頁的過程,發送http請求,從http響應中獲得結果。

Web Scraping 注意事項

在抓取數據之前,要注意以下幾點:

  • 閱讀網站有關數據的條款和約束條件,搞清楚數據的擁有權和使用限制

  • 友好而禮貌,使用計算機發送請求的速度飛人類閱讀可比,不要發送非常密集的大量請求以免造成服務器壓力過大

  • 因為網站經常會調整網頁的結構,所以你之前寫的Scraping代碼,并不總是能夠工作,可能需要經常調整

  • 因為從網站抓取的數據可能存在不一致的情況,所以很有可能需要手工調整

Python Web Scraping 相關的庫

Python提供了很便利的Web Scraping基礎,有很多支持的庫。這里列出一小部分

當然也不一定要用Python或者不一定要自己寫代碼,推薦關注import.io

Web Scraping 代碼

下面,我們就一步步地用Python,從騰訊體育來抓取歐洲聯賽13/14賽季的數據。

首先要安裝Beautifulsoup

pip install beautifulsoup4

我們先從球員的數據開始抓取。

球員數據的Web請求是http://soccerdata.sports.qq.com/playerSearch.aspx?lega=epl&pn=2 ,返回的內容如下圖所示:

使用Python抓取歐洲足球聯賽數據進行大數據分析

該web服務有兩個參數,lega表示是哪一個聯賽,pn表示的是分頁的頁數。

首先我們先做一些初始化的準備工作

from urllib2 import urlopen
import urlparse
import bs4

BASE_URL = "http://soccerdata.sports.qq.com"
PLAYER_LIST_QUERY = "/playerSearch.aspx?lega=%s&pn=%d"
league = ['epl','seri','bund','liga','fran','scot','holl','belg']
page_number_limit = 100
player_fields = ['league_cn','img','name_cn','name','team','age','position_cn','nation','birth','query','id','teamid','league']

urlopen,urlparse,bs4是我們將要使用的Python庫。

BASE_URL,PLAYER_LIST_QUERY,league,page_number_limit和player_fields是我們會用到的一些常量。

下面是抓取球員數據的具體代碼:

def get_players(baseurl):
    html = urlopen(baseurl).read()
    soup = bs4.BeautifulSoup(html, "lxml")
    players = [ dd for dd in soup.select('.searchResult tr') if dd.contents[1].name != 'th']
    result = []
    for player in players:
        record = []
        link = ''
        query = []
        for item in player.contents:
            if type(item) is bs4.element.Tag:
                if not item.string and item.img:
                    record.append(item.img['src'])
                else :
                    record.append(item.string and item.string.strip() or 'na')
                try:
                    o = urlparse.urlparse(item.a['href']).query
                    if len(link) == 0:
                        link = o
                        query = dict([(k,v[0]) for k,v in urlparse.parse_qs(o).items()])
                except:
                    pass
             
        if len(record) != 10:
            for i in range(0, 10 - len(record)):
                record.append('na')
        record.append(unicode(link,'utf-8'))
        record.append(unicode(query["id"],'utf-8'))
        record.append(unicode(query["teamid"],'utf-8'))
        record.append(unicode(query["lega"],'utf-8'))
        result.append(record)
    return result
    
result = []
for url in [ BASE_URL + PLAYER_LIST_QUERY % (l,n) for l in league for n in range(page_number_limit) ]:
    result = result +  get_players(url)

我們來看看抓取球員數據的詳細過程:

首先我們定義了一個get_players方法,該方法會返回某一請求頁面上所有球員的數據。為了得到所有的數據,我們通過一個for循環,因為要循環各個聯賽,每個聯賽又有多個分頁,一般情況下是需要一個雙重循環的:

for i in league:
    for j in range(0, 100):
        url = BASE_URL + PLAYER_LIST_QUERY % (l,n)
        ## send request to url and do scraping

Python的list comprehension可以很方便的通過構造一個列表的方式來減少循環的層次。

另外Python還有一個很方便的語法來合并連個列表: list = list1 + list2

好我們再看看如何使用BeautifulSoup來抓取網頁中我們需要的內容。

首先調用urlopen讀取對應url的內容,通常是一個html,用該html構造一個beautifulsoup對象。

beautifulsoup對象支持很多查找功能,也支持類似css的selector。通常如果有一個DOM對象是<xx class='cc'>,我們使用以下方式來查找:

obj = soup.find("xx","cc")

另外一種常見的方式就是通過CSS的selector方式,在上述代碼中,我們選擇class=searchResult元素里面,所有的tr元素,過濾掉th也就是表頭元素。

for dd in soup.select('.searchResult tr') if dd.contents[1].name != 'th'

使用Python抓取歐洲足球聯賽數據進行大數據分析

對于每一行記錄tr,生成一條球員記錄,并存放在一個列表中。所以我們就循環tr的內容tr.contents,獲得對應的field內容。

對于每一個tr的content,我們先檢查其類型是不是一個Tag,對于Tag類型有幾種情況,一種是包含img的情況,我們需要取出球員的頭像圖片的網址。

使用Python抓取歐洲足球聯賽數據進行大數據分析

另一種是包含了一個鏈接,指向其他數據內容

使用Python抓取歐洲足球聯賽數據進行大數據分析

所以在代碼中要分別處理這些不同的情況。

對于一個Tag對象,Tag.x可以獲得他的子對象,Tag['x']可以獲得Tag的attribute的值。

所以用item.img['src']可以獲得item的子元素img的src屬性。

對已包含鏈接的情況,我們通過urlparse來獲取查詢url中的參數。這里我們利用了dict comprehension的把查詢參數放入一個dict中,然后添加到列表中。

dict([(k,v[0]) for k,v in urlparse.parse_qs(o).items()])

對于其它情況,我們使用Python 的and or表達式以確保當Tag的內容為空時,我們寫入‘na’,該表達式類似C/C++或Java中的三元操作符 X ? A : B

然后有一段代碼判斷當前記錄的長度是否大于10,不大于10則用空值填充,目的是避免一些不一致的地方。

if len(record) != 10:
    for i in range(0, 10 - len(record)):
        record.append('na')

最后,我們把query中的一些相關的參數如球員的id,球隊的id,所在的聯賽代碼等加入到列表。

record.append(unicode(link,'utf-8'))
record.append(unicode(query["id"],'utf-8'))
record.append(unicode(query["teamid"],'utf-8'))
record.append(unicode(query["lega"],'utf-8'))

最后我們把本頁面所有球員的列表放入一個列表返回。

好了,現在我們擁有了一個包含所有球員的信息的列表,我們需要把它存下來,以進一步的處理,分析。通常,csv格式是一個常見的選擇。

import csv
def write_csv(filename, content, header = None): 
    file = open(filename, "wb")
    file.write('\xEF\xBB\xBF')
    writer = csv.writer(file, delimiter=',')
    if header:
        writer.writerow(header)
    for row in content:
        encoderow = [dd.encode('utf8') for dd in row]
        writer.writerow(encoderow)

write_csv('players.csv',result,player_fields)

這里需要注意的就是關于encode的問題。因為我們使用的時utf-8的編碼方式,在csv的文件頭,需要寫入\xEF\xBB\xBF,詳見這篇文章

好了現在大功告成,抓取的csv如下圖:

使用Python抓取歐洲足球聯賽數據進行大數據分析

因為之前我們還抓取了球員本賽季的比賽詳情,所以我們可以進一步的抓取所有球員每一場比賽的記錄

使用Python抓取歐洲足球聯賽數據進行大數據分析

抓取的代碼如下

def get_player_match(url):
    html = urlopen(url).read()
    soup = bs4.BeautifulSoup(html, "lxml")
    matches = [ dd for dd in soup.select('.shtdm tr') if dd.contents[1].name != 'th']
    records = []
    for item in [ dd for dd in matches if len(dd.contents) > 11]: ## filter out the personal part
        record = []
        for match in [ dd for dd in item.contents if type(dd) is bs4.element.Tag]:
            if match.string:
                record.append(match.string)
            else:
                for d in [ dd for dd in match.contents if type(dd) is bs4.element.Tag]:
                    query = dict([(k,v[0]) for k,v in urlparse.parse_qs(d['href']).items()])
                    record.append('teamid' in query and query['teamid'] or query['id'])   
                    record.append(d.string and d.string or 'na')                    
        records.append(record)
    return records[1:]  ##remove the first record as the header

def get_players_match(playerlist, baseurl = BASE_URL + '/player.aspx?'):
    result = []
    for item in playerlist:
        url =  baseurl + item[10]
        print url
        result = result + get_player_match(url)
    return result
match_fields = ['date_cn','homeid','homename_cn','matchid','score','awayid','awayname_cn','league_cn','firstteam','playtime','goal','assist','shoot','run','corner','offside','foul','violation','yellowcard','redcard','save']    
write_csv('m.csv',get_players_match(result),match_fields)

抓取的過程和之前類似。

下一步做什么

現在我們擁有了詳細的歐洲聯賽的數據,那么下一步要怎么做呢,我推薦大家把數據導入BI工具來做進一步的分析。有兩個比較好的選擇:

Tableau在數據可視化領域可謂無出其右,Tableau Public完全免費,用數據可視化來驅動數據的探索和分析,擁有非常好的用戶體驗

Splunk提供一個大數據的平臺,主要面向機器數據。支持每天免費導入500M的數據,如果是個人學習,應該足夠了。

當然你也可以用Excel。

來自:http://my.oschina.net/taogang/blog/271060


 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!