世界杯來了,想分析一下各個球員的比賽數據,然后預測今年的世界杯金靴獎,根據經驗大家肯定普遍認為梅西,C羅,內馬爾等球星概率大些;但經驗畢竟是經驗,數據才是最靠譜的,通過分析數據,可以評估一個球員的價值(當然,球員的各方面的表現(特征),都會有一個權重,最終衡量權重*特征值之和最高者的金靴概率勝算大些)。那么,如何獲取這些數據呢?寫段簡單的爬取數據的代碼就是最好的獲取工具。本文以2014年的巴西世界杯球員為基礎進行實踐操作;
一、什么是爬數據?
網絡爬蟲(網頁蜘蛛),是一種按照一定的規則,自動的抓取萬維網信息的程序或者腳本;
學習一些爬數據的知識有什么用呢?
比如:大到大家經常使用的搜索引擎(Google, 搜狗);
當用戶在Google搜索引擎上檢索相應關鍵詞時,谷歌將對關鍵詞進行分析,從已“收錄”的網頁中找出可能的最符合用戶的條目呈現給用戶;那么,如何獲取這些網頁就是爬蟲需要做的,當然如何推送給用戶最有價值的網頁,也是需要結合相應算法的,這就涉及到數據挖掘的的知識了;
比較小一些的應用,比如我們統計測試工作的工作量,這就需要統計一周/一月的修改單數量,jira記的缺陷數以及具體內容;
還有就是最近火熱進行的世界杯,如果你想統計一下各個球員/國家的數據,並存儲這些數據以供其他用處;
還有就是根據自己的興趣愛好通過一些數據做一些分析等(統計一本書/一部電影的好評度),這就需要爬取已有網頁的數據了,然后通過獲取的數據做一些具體的分析/統計工作等。
二、學習簡單的爬蟲需要具備哪些基礎知識?
我把基礎知識分為兩部分:
1、前端基礎知識
HTML/JSON,CSS; Ajax
參考資料:http://www.w3school.com.cn/h.asp
http://www.w3school.com.cn/ajax/
http://www.w3school.com.cn/json/
2. python編程相關知識
(1)Python基礎知識
基本語法知識,字典,列表,函數,正則表達式,JSON等
參考資料:http://www.runoob.com/python3/python3-tutorial.html
(2)Python常用庫:
Python的urllib庫的用法 (此模塊我用的urlretrieve函數多一些,主要用它保存一些獲取的資源(文檔/圖片/mp3/視頻等))
Python的pyMysql庫 (數據庫連接以及增刪改查)
python模塊bs4(需要具備css選擇器,html的樹形結構domTree知識等,根據css選擇器/html標簽/屬性定位我們需要的內容)
python的requests(顧名思義,此模塊用於發送request請求的/POST/Get等,獲取一個Response 對象)
python的os模塊 (此模塊提供了非常豐富的方法用來處理文件和目錄。os.path.join/exists函數用的較多一些)
參考資料:這部分可以參考相關模塊的接口API文檔
三、簡單小項目上手實踐(附源碼)
(1).爬取Kugou網站音樂,以歌手id為輸入,下載歌手所有的專輯歌曲並以專輯名為文件夾存放下載的歌曲;
具體實現過程如下:
1.酷狗首頁搜索歌手,進入歌手主頁,獲取url中的singId,例如朴樹主頁:http://www.kugou.com/singer/3520.html,其中3520即為singId;
2.根據歌手singerId可以獲得歌手的所有專輯的albumId,例如 這是專輯的頁面,http://www.kugou.com/yy/album/single/962593.html,其中962593為albumId
3.酷狗播放歌曲的實現方式,是通過ajax請求獲取的服務器資源,點擊播放某歌曲,播放頁面打開F12,切至netWork,觀察Request URL請求,如下 例如http://www.kugou.com/yy/index.php?r=play/getdata&hash=89AB193EC33E2AE6AF04BD408F8F1083&album_id=962593&_=1529057140131
經過測試發現(建議使用截包工具截獲url請求),只需要(get請求)http://www.kugou.com/yy/index.php?r=play/getdata&hash=89AB193EC33E2AE6AF04BD408F8F1083
而每首歌有一個單獨的hash,只要找到每首歌的hash,即可獲取每首歌的ajax請求url,而這個hash存在於專輯頁面中,bs4提取專輯內所有歌曲的hash.
4.可以發現其ajax請求的response信息中存在該歌曲的MP3資源url,那么通過urllib.request.urlretrieve()函數即可保存該歌曲.
圖例過程:
# -*- coding: utf-8 -*- # @Time : 2018/6/8 # @Author : Torre # @Email : klyweiwei@163.com # 免費下載酷狗音樂:通過歌手singerId即可以專輯下載歌手的所有歌曲。 # 具體過程:1.酷狗首頁搜索歌手,進入歌手主頁,獲取url中的singId,例如朴樹主頁:http://www.kugou.com/singer/3520.html,其中3520即為singId; # 2.根據歌手singerId可以獲得歌手的所有專輯的albumId,例如 這是專輯的頁面,http://www.kugou.com/yy/album/single/962593.html,其中962593為albumId # 3.酷狗播放歌曲的實現方式,是通過ajax請求獲取的服務器資源,點擊播放某歌曲,播放頁面打開F12,切至netWork,觀察Request URL請求,如下 # 例如http://www.kugou.com/yy/index.php?r=play/getdata&hash=89AB193EC33E2AE6AF04BD408F8F1083&album_id=962593&_=1529057140131 # 經過測試發現(建議使用截包工具截獲url請求),只需要(get請求)http://www.kugou.com/yy/index.php?r=play/getdata&hash=89AB193EC33E2AE6AF04BD408F8F1083 # 而每首歌有一個單獨的hash,只要找到每首歌的hash,即可獲取每首歌的ajax請求url,而這個hash存在於專輯頁面中,bs4提取專輯內所有歌曲的hash. # 4.可以發現其ajax請求的response信息中存在該歌曲的MP3資源url,那么通過urllib.request.urlretrieve()函數即可保存該歌曲. import os import urllib.request import requests import re import json import getSoup # from urllib.request import urlretrieve headers = { 'origin': "http://www.kugou.com", 'x-devtools-emulate-network-conditions-client-id': "97C9BAA42BE5A8449EC4283F764B4D9E", 'user-agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36", 'content-type': "application/x-www-form-urlencoded", 'accept': "*/*", 'referer': "http://www.kugou.com/singer/3520.html", 'accept-encoding': "gzip, deflate", 'accept-language': "zh-CN,zh;q=0.9", 'cookie': "kg_mid=88665d81b7959ab3787c4976831a30f9; Hm_lvt_aedee6983d4cfc62f509129360d6bb3d=1528705681; Hm_lpvt_aedee6983d4cfc62f509129360d6bb3d=1528707581", 'cache-control': "no-cache", 'postman-token': "c717ef07-2b91-06f1-1d22-abcb47b0bce2" } # 獲取歌手的所有album信息 def getAlbumid(singerID): # 獲取歌單albumid url = "http://www.kugou.com/yy/" querystring = {"r": "singer/album", "sid": singerID} # headers = { # 'origin': "http://www.kugou.com", # 'x-devtools-emulate-network-conditions-client-id': "97C9BAA42BE5A8449EC4283F764B4D9E", # 'user-agent': "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36", # 'content-type': "application/x-www-form-urlencoded", # 'accept': "*/*", # 'referer': "http://www.kugou.com/singer/3520.html", # 'accept-encoding': "gzip, deflate", # 'accept-language': "zh-CN,zh;q=0.9", # 'cookie': "kg_mid=88665d81b7959ab3787c4976831a30f9; Hm_lvt_aedee6983d4cfc62f509129360d6bb3d=1528705681; Hm_lpvt_aedee6983d4cfc62f509129360d6bb3d=1528707581", # 'cache-control': "no-cache", # 'postman-token': "c717ef07-2b91-06f1-1d22-abcb47b0bce2" # } response = requests.request("POST", url, headers=headers, params=querystring) res = response.text # print(type(res)) jsonRes = json.loads(res) loadAlbumids = [] # 保存albumids到list loadAlbumname = [] albumids = jsonRes['data'] for albumid in albumids: albumid = albumid['albumid'] # print(albumid) loadAlbumids.append(albumid) # print(albumid) for albumname in albumids: albumname = albumname['albumname'] albumname = albumname[0] loadAlbumname.append(albumname) # print(albumname) return loadAlbumname, loadAlbumids # getAlbumid(2303) # 獲取該專輯內的所有歌曲的hash def getMp3Info(albumid): url = 'http://www.kugou.com/yy/album/single/'+str(albumid)+'.html' soup = getSoup.getSoup(url) hashs = soup.select('.songList a') loadMp3Hash = [] for hashss in hashs: hash = hashss.get('data') # 通過spilt('|')分割字符串,獲取hash mp3Hash = hash.split('|')[0] # print(hash.split('|')[0]) # hash = hash.spilt('|') loadMp3Hash.append(mp3Hash) # print(mp3Hash) return loadMp3Hash # mp3 = getMp3Info(1645030) # for i in range(len(mp3)): # print(mp3[i]) # 通過ajax請求獲取歌曲的PlayerUrl def getPlayUrl(hash, albumId): url = "http://www.kugou.com/yy/index.php" querystring = {"r": "play/getdata", "hash": hash, "album_id": albumId} response = requests.request("GET", url, headers=headers, params=querystring) response.raise_for_status() res = response.text # print(type(res)) jsonRes = json.loads(res) playUrl = jsonRes['data'] audioName = playUrl['audio_name'] playUrl = playUrl['play_url'] music = (audioName, playUrl) print('-'.join(music)) return audioName, playUrl # @test # mp3 = getMp3Info(1645030) # for i in range(len(mp3)): # print(mp3[i]) # getPlayUrl(mp3[i], '1645030') # 文件/文件夾的創建是不允許一些非法字符存在的,此函數過濾掉非法字符 def validateName(name): rstr = r"[\/\\\:\*\?\"\<\>\|]" # '/ \ : * ? " < > |' new_name = re.sub(rstr, "", name) return new_name # 進度信息 def cbk(a,b,c): per=100.0*a*b/c if per>100: per=100 print('%.2f%%' % per) # # 保存為MP3, 保存到特定文件夾下面:文件夾以專輯名字命名; 注意,在代碼的根目錄下創建mp3文件夾 def saveAudio(url, album, filename): filepath = os.getcwd()+'\\mp3\\'+album if os.path.exists(filepath): mp3 = os.path.join(filepath + '\\', '' + filename + '.mp3') if url == '': print('the url is NUll, pass') else: urllib.request.urlretrieve(url, mp3, cbk) else: os.makedirs(filepath) mp3 = os.path.join(filepath + '\\', '' + filename + '.mp3') if url == '': print('the url is NUll, pass') else: urllib.request.urlretrieve(url, mp3, cbk) # 運行主程序, 只需要填入 歌手ID即可(http://www.kugou.com/yy/html/singer.html, # 點擊任一歌手即可獲得其ID), 可以自動下載其所有專輯 : 比如3043 代表 許巍; 61874代表Sophia zelmani;朴樹2303;34450 Taylor Swift def downloadMp3(singerId): albumname, albumids = getAlbumid(singerId) # length = len(albumids) # print(albumids) for i in range(len(albumids)): hashs = getMp3Info(albumids[i]) for ii in range(len(hashs)): audioName, playUrl = getPlayUrl(hashs[ii], albumids[i]) saveAudio(playUrl, validateName(albumname[i]), validateName(audioName)) # 調用函數 ,下載歌曲 downloadMp3(34450)
(2).爬取2014年世界杯各個球員的參賽數據。
1.數據庫連接以及sql語句格式化
數據庫連接及其操作,我單獨封裝成一個類ConnectDatabase;
1.讀取本地的配置文件(Json文件:數據庫的連接地址、賬號、密碼、數據庫名等信息)
2.主要函數有數據庫連接、獲取數據庫的所有表、執行sql並提交、關閉數據庫連接等
2.數據爬取並存儲
1.通過requests.get()獲取response對象;
2.bs4.BeautifulSoup()獲取bs4對象;
3.通過select()方法,獲取bs4對象的表格數據並存儲到list中
4.執行sql並提交數據
-- 建表語句 CREATE TABLE `playertechsum` ( `id` int(255) NOT NULL AUTO_INCREMENT, `player` varchar(20) DEFAULT '' COMMENT '球員', `team` varchar(20) DEFAULT NULL COMMENT '球隊', `games` int(255) DEFAULT NULL COMMENT '出場', `minsPlayed` int(255) DEFAULT NULL COMMENT '出場時間', `goals` int(10) DEFAULT NULL COMMENT '進球數', `attPenGoal` int(10) DEFAULT NULL COMMENT '點球', `goalAssist` int(10) DEFAULT NULL COMMENT '助攻', `ontargetScoringAtt` int(20) DEFAULT NULL COMMENT '射正', `totalScoringAtt` int(20) DEFAULT NULL COMMENT '射門', `totalPass` int(10) DEFAULT NULL COMMENT '傳球', `totalCross` int(10) DEFAULT NULL COMMENT '傳中', `wonCorners` int(10) DEFAULT NULL COMMENT '角球', `totalOffside` int(10) DEFAULT NULL COMMENT '越位', `touchBall` int(10) DEFAULT NULL COMMENT '觸球', `fouls` int(10) DEFAULT NULL COMMENT '犯規', `outfielderBlock` int(10) DEFAULT NULL COMMENT '封堵', `yellowCard` int(10) DEFAULT NULL COMMENT '黃牌', `redCard` int(10) DEFAULT NULL COMMENT '紅牌', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 數據庫連接以及操作函數 # -*- coding: utf-8 -*- # @Time : 2018/5/24 20:02 # @Author : Torre # @Email : klyweiwei@163.com # Description :connect to database, return cursor and conn import json import pymysql import random import string import os import logging class ConnectDatabase: # def __init__(self, cur): # self.cur = cur # def get_conf(self, file='databases_conf.json'):讀取本地json文件 def get_conf(self, file): with open(file, "r", encoding="utf-8") as f: conf = json.load(f) return conf # 數據庫連接 def connect_db(self, host, user, password, db, port): conn = pymysql.connect(host, user, password, db, port, charset="utf8") # 最好加上utf-8 cur = conn.cursor() return conn, cur # 獲取列 def get_cols(self, table, cur): sql = 'desc ' + str(table) + '' cur.execute(sql) res = cur.fetchall() return res # 執行sql,獲取查詢結果 def get_res(self, cur, sql): cur.execute(sql) res = cur.fetchall() return res # 執行並提交 def get_fetch(self, conn, cur, sql): cur.execute(sql) conn.commit() # 關閉數據庫連接 def disconnect_db(self, conn, cur): cur.close() conn.close()
-- 獲取bs4對象 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : Torre Yang Edit with Python3.6 # @Email : klyweiwei@163.com # @Time : 2018/6/5 16:19 import requests from bs4 import BeautifulSoup as bs def getSoup(url): response = requests.get(url) response.raise_for_status() res = response.content soup = bs(res, 'html.parser') return soup
-- 爬取的數據插入到mariadb
# -*- coding: utf-8 -*- # @Time : 2018/6/18 18:59 # @Author : Torre # @Email : klyweiwei@163.com import getSoup import connect_dataBase import os import re from bs4 import BeautifulSoup as bs # db連接 connectDB = connect_dataBase.ConnectDatabase() get_conf = connectDB.get_conf('databases_conf.json') conn, cur = connectDB.connect_db(get_conf["brazilCup"]["host"], get_conf["brazilCup"]["user"], get_conf["brazilCup"]["password"], get_conf["brazilCup"]["database"], get_conf["brazilCup"]["port"]) url = 'http://worldcup.2014.163.com/playerrank/avg/attPenGoal/' soup = getSoup.getSoup(url) trs = soup.select('tbody tr') # print(tds) length = len(trs) # print(length) players = [] for tr in trs: # print(row) player = [] # print(len(tr)) for td in tr: tds = '\''+str(td.string.strip())+'\'' # print(tds) # player.append(str(td.string.strip())) player.append(tds) if "''" in player: player.remove("''") # print(player) # print(tuple(player)) # 球員排行榜 sql = 'insert into playertechsum(id,player,team,games,minsPlayed,goals,attPenGoal,goalAssist,ontargetScoringAtt,totalScoringAtt,totalPass,totalCross,wonCorners,totalOffside,touchBall,fouls,outfielderBlock,yellowCard,redCard) values('+\ ','.join(player)+')' connectDB.get_fetch(conn, cur, sql)
四、結后語
當然,想深入學習爬蟲,最好還是要學習一個爬蟲框架。常見python爬蟲框架參考如下:
(1)Scrapy:很強大的爬蟲框架,可以滿足簡單的頁面爬取(比如可以明確獲知url pattern的情況)。用這個框架可以輕松爬下來如亞馬遜商品信息之類的數據。但是對於稍微復雜一點的頁面,如weibo的頁面信息,這個框架就滿足不了需求了。
(2)Crawley: 高速爬取對應網站的內容,支持關系和非關系數據庫,數據可以導出為JSON、XML等
(3)Portia:可視化爬取網頁內容
(4)newspaper:提取新聞、文章以及內容分析
(5)python-goose:java寫的文章提取工具
(6)mechanize:優點:可以加載JS。缺點:文檔嚴重缺失。不過通過官方的example以及人肉嘗試的方法,還是勉強能用的。
附件:資料下載地址 鏈接:https://pan.baidu.com/s/179RtOxk4CsnjqjChW0nljw 密碼:lczh