Python零基礎,如何快速學爬蟲技術

學習任何新事物,都須要先搭建起學習框架。php

一般咱們會從「基礎知識」、「案例實操」兩個板塊切入,構建起本身的學習閉環。html


1、關於「基礎知識」python

對於想快速瞭解知識關鍵要點的新手,如下是比較推薦的學習素材:編程

MOOC課程:Python語言程序設計Python網絡爬蟲與信息提取瀏覽器

參考書籍:《利用Python進行數據分析》《Python編程:從入門到實踐》bash


2、關於「案例實操」微信

驗證本身是否真正理解知識並能應用的最佳方式,就是經過案例完成實踐操做。網絡

分享案例

做爲爬蟲新手,如何利用Python爬取NBA球員數據。
app


一、搭建工做環境框架

傳統方式:對於Win用戶,須要本身在本地搭建運行環境。

準備工做:

搭建Python3.6環境

  • 用Anaconda

安裝Package

  • Requests庫
  • BeautifulSoup4(bs4)庫
  • Numpy和Pandas庫(用於數據存儲和清洗)

說明:

以上工具,除Python環境外,皆爲Python的第三方庫。Windows環境下經過cmd的pip install命令安裝。對於Mac用戶,雖沒那麼複雜,但一樣會須要花費時間與精力。


你們會發現,按以上步驟搭建工做環境,是個特別費時費力的事兒,P二、P3的兼容性問題也很突出。

那麼有沒有更好的方法呢?答案是,有的。

科賽網,針對數據科學家人羣,重點打造了K-Lab在線數據分析協做平臺。它涵蓋了Python、R等多種主流語言,完成90%以上數據分析&挖掘相關庫的部署,避免了搭建本地環境配置時遇到的各類問題,幫助數據人才真正實現「在線」作數據分析,提升效率,專一價值創造。


二、搭建工做流

1)選取目標官網:NBA中文數據網站


2)導入庫

#引入主要的爬取工具:
import requests
from bs4 import BeautifulSoup

#如下是數據的清洗和存儲所需的輔助工具
import re
import numpy as np
import pandas as pd
複製代碼

完成導入後,獲取每一個球員各個賽季單場比賽的基礎數據(CSV文檔格式):


3)找到頁面,批量爬取

樣例:LeBron James的詳情頁,該網站數據源相對規整,URL設置易找規律,方便抓取。

說明:爲了實現自動批量抓取,爬取時通常須要讓爬蟲先爬取列表頁,再根據列表頁的索引連接爬取相應的詳情頁。但在該項目並不須要如此,從連接1連接2能夠看出其中規律。


將須要關注的參數用粗體表示:

  • page:表示數據的頁碼,每頁顯示20條數據。
  • GameType:season表示常規賽,playoff表示季後賽。
  • Player id:網站自定義的球員編號。

爬取時,咱們只須要改動這些數據就能實現全站資源的爬取,很是適合新手。


4)解析頁面

推薦在Chrome瀏覽器的開發者模式下解析頁面,它能快速定位到所需內容。

圖中藍標顯示的Table標籤即是須要抓取的表格。

關於table標籤:

table標籤有兩個子標籤thead、tbody。

  • thead子標籤:存儲表頭信息,包含一個tr子標籤(表頭只有一行)。tr又包含多個th子標籤,每一個th存儲一格表頭信息。
  • tbody子標籤:存儲表格主題,包含多個tr子標籤。tr又包含多個td子標籤,每一個td存儲一格表格信息。


5)將表格內容搬運到本地

首先,經過fillframe函數,將網頁上的表格轉換成一個pandas的DataFrame表格對象。

def fillframe(tbody,index):   
#這裏只使用tbody標籤,表頭經過index傳入,index是一個list
    frame=pd.DataFrame()
    if tbody:
        all_tr=tbody.find_all('tr')
        for tr in all_tr:
            dic={}
            all_td=tr.find_all('td')
            i=-1
            for td in all_td:
                if i==-1:    
#能夠發現,網頁表格中每行的第一格都是空的,因此咱們須要將其跳過。
                    i+=1
                    continue
                else:
                    dic[index[i]]=td.string
                i+=1
            frame=pd.concat([frame,pd.DataFrame(dic,index=[0])],ignore_index=True)
        return frame
複製代碼

其次,用fillindex函數將fillframe函數生成index。

def fillindex(thead):
    index=[]
    if thead:
        all_th=thead.tr.find_all('th')
        i=-1
        for th in all_th:
            if i==-1:
                i+=1
                continue
            else:
                index.append(th.string)
            i+=1
        return index
複製代碼

通過以上操做,不難發現,網頁上表格的表頭可能有歧義。好比它將投籃的命中數和三分的命中數兩項數據的索引都設置爲了命中。爲了保險起見,建議手動設置index。

index=['球員','賽季','結果','比分','首發','時間','投籃','命中','出手','三分', '三分命中','三分出手','罰球','罰球命中','罰球出手','籃板','前場','後場','助攻','搶斷','蓋帽','失誤','犯規','得分']
複製代碼


6)開始爬取頁面,並提取table

提取過程當中,因爲每一個球員的數據條目數不一樣,不能肯定球員數據的頁數。

能夠考慮給spider函數設置爲「在頁面沒法正常讀取或讀取的頁面中找不到table標籤時返回False值」。

def spider(page,player_id,gametype,index):
    url='http://www.stat-nba.com/query.php?page='+str(page)+'&QueryType=game&GameType='+str(gametype)+'&Player_id='+str(player_id)+'&crtcol=season&order=1'
    r=requests.get(url,timeout=30)
    if r.status_code==200:
        demo=r.text
        soup=BeautifulSoup(demo,"html.parser")

        data=soup.find('div',{'class':'stat_box'})
        if not data:           #找不到數據表格時退出
            return False

        table=data.find('table')
        if table:
            tbody=table.find('tbody')
            return fillframe(tbody,index)
        else:    #數據表格爲空時退出
            return False
    else:   #頁面讀取失敗時退出
        return False
複製代碼

其次,用update函數保存更新已爬取好的DataFrame。

def update(frame,path,filename):
    try:  #嘗試讀取文件filename
        frame0=pd.read_csv(path+'\\'+filename)
    except: #若是文件已經存在,則更新它
        frame.to_csv(path+'\\'+filename,index=False,encoding='utf-8')
    else: #不然建立名爲filename的文件
        frame0=pd.concat([frame0,frame],ignore_index=True)
        frame0.to_csv(path+'\\'+filename,index=False,encoding='utf-8')
複製代碼

最後,着手設計主函數,來控制循環以及存儲數據。

frame_player=pd.DataFrame()
gametype='season'
for player_id in range(1862,1863):  
#這裏僅爬取一位球員(James)測試,須要正式爬取請改成range(1,4450)
    page=0
    flag=True
    while flag:
        result=spider(page,player_id,gametype,index)
        if type(result)==bool and not result:   #返回False時
            flag=False
            break
        else:   #爬取成功時讀取表格
            frame_player=pd.concat([frame_player,result],ignore_index=True)
        page+=1
複製代碼


7)測試階段:爬取一位球員數據,結果以下:

frame_player.head()
複製代碼

接下來,就可使用以前定義的update函數將frame_player保存到本地,也能夠在每次while循環結束時運行一次update函數以起到實時更新的效果。

注意:實時更新完之後需執行frame_player=DataFrame()語句,將frame_player清空以免數據重複。

以上即是完整的爬取數據操做過程。


8)常見問題解答

因爲網站自身的編碼緣由,爬取時可能會遇到全部中文字符都成爲亂碼,以下:

frame_player.head()
複製代碼

對該數據集,能夠手動輸入Index來解決表頭亂碼。

爬取球員英文姓名解決球員姓名亂碼,具體的函數以下:

def getname(player_id):
    r2=requests.get('http://www.stat-nba.com/player/'+str(player_id)+'.html',timeout=30)
    if r2.status_code==200:
        demo2=r2.text
        soup2=BeautifulSoup(demo2,"html.parser")
        name_source=soup2.find('div',{'class':"playerinfo"}).find('div',{'class':'name'})
        if re.findall('[A-z]* [A-z]*',name_source.contents[0]):
            name=re.findall('[A-z]* [A-z]*',name_source.contents[0])[0]
        else:
            name=np.nan
    else:
        name=np.nan
    return name
複製代碼

PS:爬蟲還有不少能夠優化的空間,好比控制每次發送請求的時間間隔防止IP被封,爬取球隊名稱作映射來解決比分中的亂碼。


咱們已在科賽網上建立該爬蟲項目,能夠看到完整的實現代碼。

建議新手萌登錄官網在線實現運行,不只僅只停留在閱讀層面。


操做步驟

登陸「kesci.com」- 點擊「K-Lab」- 點擊「項目」- 在「關鍵詞」框內輸入「爬取」便可查看該項目的爬蟲完整代碼,一鍵Fork、在線運行。


操做過程當中有任何疑問,可添加科賽網工做人員微信號(小科:kesci_jack),備註:掘金

相關文章
相關標籤/搜索