<p>自從上年年底了解到數(shù)據(jù)分析和挖掘這個概念之后拉宗,今年1月底畢業(yè)前夕峦树,讓自己完全沉下心來學(xué)習(xí)Python的數(shù)據(jù),截止到目前也有4個多月了簿废。我稱這條路叫做數(shù)據(jù)之路空入,我一直在想络它,我們現(xiàn)在身處在這個被數(shù)據(jù)充滿的世界里面族檬,學(xué)會挖掘數(shù)據(jù)以及分析數(shù)據(jù),就像是學(xué)會游泳一樣化戳。即使我不是一個數(shù)據(jù)分析師单料,甚至不是程序員,我僅僅是作為在一家小小的電商公司的計劃工程師点楼,學(xué)會數(shù)據(jù)分析一定可以提高我的工作效率(當(dāng)然最開始的時候是考慮能不能將這個當(dāng)成自己的副業(yè))扫尖。</p>
數(shù)據(jù)之路:
<p>廢話不說,先來聊聊我1月底到現(xiàn)在整個數(shù)據(jù)之路的經(jīng)過掠廓。決定了要用Python進(jìn)行數(shù)據(jù)分析之后换怖,我是先通過這個Codecademy來進(jìn)行Python學(xué)習(xí)的。這個網(wǎng)址好像主要是針對網(wǎng)頁編程的蟀瞧。迅速的過完里面的Python練習(xí)沉颂,有了基本概念条摸,就開始考慮怎么用Python來進(jìn)行數(shù)據(jù)分析了。</p>
<p>這個時候铸屉,第二個網(wǎng)站映入了我的眼簾钉蒲,叫做Dataquest。這個網(wǎng)址不錯彻坛,里面有所有關(guān)于數(shù)據(jù)分析的課程顷啼,他有三個Level,分別叫做Data Analyst昌屉,Data Scientist钙蒙,以及Data Engineer(這個目前還沒有開放)。</p>
- Data Analyst的課程包括:基本Python的用法间驮,Pandas的使用仪搔,數(shù)據(jù)可視化,Linux系統(tǒng)命令行操作基礎(chǔ)蜻牢,簡單的爬蟲網(wǎng)絡(luò)烤咧,統(tǒng)計原理,R語言抢呆。
- Data Scientist可以認(rèn)為是Data Analyst 的升級版煮嫌,加入了:機器學(xué)習(xí),數(shù)據(jù)結(jié)構(gòu)和算法抱虐,高級Python用法昌阿,大數(shù)據(jù)的處理方式。
<p>其中部分課程是要收費才能做恳邀,我就充了兩個月的Basic會員懦冰,將Data Analyst的課程完成了個85%之后感覺就差不多了。</p>
<p>除此之外谣沸,還看了些書刷钢,最重要當(dāng)然要看是Python各個庫的Document。</p>
第一個實例:
立項
<p>第一個實例做什么我還是想了好久乳附,想做點實用點的内地,就選了淘寶的一個門店,來做數(shù)據(jù)每天的銷量以及評論更新狀態(tài)分析吧赋除。任務(wù)包括:</p>
- 收集產(chǎn)品的基本信息阱缓;
- 每天定點收集最新的銷量以及推送的評論;
<p>隨便選了個門店举农,先打開主頁瀏覽下荆针,發(fā)現(xiàn)是賣傘的,仔細(xì)看一下,有5個類別:Black航背、Air秸妥、Bon、Joli沃粗、Moma(X系列進(jìn)去看好像都停售粥惧,就不管了)。</p>
Request扒內(nèi)容
<p>采用Requests模塊最盅,采用get
(說起來get
函數(shù)和post
函數(shù)是什么區(qū)別突雪,什么時候要用get
函數(shù)或者post
函數(shù),到現(xiàn)還沒有搞懂)函數(shù)將url里面的內(nèi)容扒下來涡贱,同時加了headers
咏删,將自己偽裝成瀏覽器。將扒下來來的內(nèi)容用BeautifulSoup進(jìn)行中‘lxml’解析器進(jìn)行解析问词,這樣網(wǎng)頁的內(nèi)容就像是被扒光衣服一樣展示在我們面前督函。</p>
<p>另外,在BeautifulSoup中有四種解析器激挪,分別是自帶的辰狡、'lxml'、‘xml’以及‘html5lib’四種解析器垄分,具體區(qū)別可以看Document文件宛篇。</p>
import requests
from bs4 import BeautifulSoup
url = "https://bananaumbrella.tmall.com/?"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36"}
response = requests.get(url, headers = headers)
interial = BeautifulSoup(response.content, "lxml")
<p>獲得HTML的內(nèi)容之后,快速在Console上Crtl+F尋找上面的幾個類別薄湿,很快就找到了這個這段代碼(每個類別都類似叫倍,就復(fù)制兩段),每個href
中的catName
很明顯就是我們要找的東西:</p>
<li class="cat fst-cat">
<li class="cat fst-cat">
<h4 class="cathd fst-cat-hd ">
<i class="cat-icon fst-cat-icon active-trigger"></i>
<a class="cat-name fst-cat-name" >Black</a>
</h4>
</li>
<li class="cat fst-cat">
<h4 class="cat-hd fst-cat-hd ">
<i class="cat-icon fst-cat-icon active-trigger"></i>
<a class="cat-name fst-cat-name" >Air</a>
</h4>
</li>
<p>其中豺瘤,每個tag里面的href
中的url就是我們需要的吆倦。利用BeautifulSoup中的find_all
函數(shù)將這些標(biāo)簽抓出來,先通過find_all
尋找所有a標(biāo)簽坐求,返回一個含有a
標(biāo)簽的list蚕泽,再通過list的鏈表推導(dǎo)式將其中的href
抓取出來。其中get()
是個好東西赛糟,例如這句話:</p>
<a class="cat-name fst-cat-name" >Moma</a>
<p>通過get('href')
就可以直接獲得其中的鏈接,對獲得的list再通過list slice
就可以得到每個類別的url砸逊。</p>
categroys = interial.find_all("a")
categroy = [item.get("href") for item in categroys if item.get("class") == ['cat-name', 'fst-cat-name']]
categroy = categroy[1:]
建立Sqlite數(shù)據(jù)庫
<p> 我也不知道為什么我對Sql如此的喜歡,其中項目一開始我是導(dǎo)出為csv函數(shù)的掌逛,后來大概是可能想到之前在dataquest上學(xué)了Sql的語法师逸,就想在這個任務(wù)里面嘗試吧。(從最終的結(jié)果來看豆混,其實我不太能分別我最后得到db好還是csv好篓像,求大神指點呀动知。。)</p>
<p>在Sql上學(xué)過db中幾個表格的關(guān)聯(lián)员辩,經(jīng)過無數(shù)次的嘗試盒粮,我知道每次采集的數(shù)據(jù)中有很多相同的參數(shù),例如產(chǎn)品的ID(skuid)奠滑,名稱(name)丹皱,鏈接地址(href),原售價(oldprice)宋税,活動售價(price)摊崭。而變化的參數(shù)有兩個:</p>
- 銷量(scount)
- 評論(cite)
<p>評論和銷量每次更新時,都會以當(dāng)前時間增加一列杰赛,加入新內(nèi)容呢簸。</p>
<p>因此我就想到了用采用表格的鍵約束,制作一個主表用于存放變化不變的參數(shù)乏屯,制作兩個附表分別存放銷量和評論根时,主表的附表之間通過鍵(key)來連接。</p>
<p>所以首先當(dāng)然是要建立一個數(shù)據(jù)庫db文件了辰晕,然后建立表格啸箫,建表的時候一開始我是采用CREATE TABLE UB(skuid int primary key , name text, href text, oldprice real, price real);
,后來發(fā)現(xiàn)如果重復(fù)調(diào)試這個程序的時候伞芹,必須要先將久的db文件刪除之后忘苛,才能執(zhí)行,不然回報錯唱较。所以后面我就將這句話改成CREATE TABLE IF NOT EXISTS UB(skuid int primary key , name text, href text, oldprice real, price real)扎唾;
。</p>
<p>建立主表時候南缓,一個要約定哪個是鍵(primary key)胸遇,這里采用每個商品的id作為鍵(key),而主表UB里面的是primary key,附表 scount 和 cite 里面的是foreign key汉形。</p>
import datetime, sqlite3
conn = sqlite3.connect("data.db")
time = datetime.datetime.now()
c = conn.cursor()
time = time.strftime("%Y%m%d-%H%M")
c.execute("CREATE TABLE IF NOT EXISTS UB(skuid int primary key , name text, href text, oldprice real, price real);")
c.execute("CREATE TABLE IF NOT EXISTS CITE(citeskuid int, FOREIGN KEY(citeskuid) REFERENCES UB(skuid));")
c.execute("CREATE TABLE IF NOT EXISTS SCOUNT(scountskuid int, FOREIGN KEY(scountskuid) REFERENCES UB(skuid));")
c.execute("ALTER TABLE CITE ADD COLUMN '%s' 'text';" % time)
c.execute("ALTER TABLE SCOUNT ADD COLUMN '%s' 'int';" % time)
解析每個類別網(wǎng)址的內(nèi)容
<p>首先繼續(xù)采用Requests函數(shù)以及BeautifulSoup函數(shù)去將頁面的內(nèi)容解析出來纸镊,過程和我們一開始做的一樣,不做重復(fù)概疆。</p>
<p>結(jié)合瀏覽器的Inspect功能進(jìn)行搜索逗威,不難找到每個類似的這樣一段內(nèi)容(誰可以告訴我像這種html正確的排版應(yīng)該是怎樣的。岔冀。凯旭。):</p>
<div class="popitem" style="width:167px;">
<a class="pic" style="width:167px;" target="_blank">
<img data-ks-lazyload="http://gdp.alicdn.com/bao/uploaded/i4/TB13q8EKpXXXXbzaXXXXXXXXXXX_!!0-item_pic.jpg_220x10000.jpg" src="http://assets.alicdn.com/s.gif" title="【新品】BananaUmbrella蕉下小黑傘蘇桃雙層防曬女太陽傘遮陽傘" width="167"/>
</a>
<div class="expannel expannel-float">
<a class="mask" style="display:block;visibility:visible;" target="_blank">
</a>
<div class="exinfo exi_2 ext_4 abs" style="bottom:0px;">
<div class="desc">
<a target="_blank">【新品】BananaUmbrella蕉下小黑傘蘇桃雙層防曬女太陽傘遮陽傘</a>
</div>
<div class="scount" style="float:left;">
<i>已售:</i>
<em>(218520)</em>
</div>
<div class="simple-sns sns-widget" data-like='{"title":"http://item.taobao.com/item.htm?id=24875700842","key":"24875700842","type":2}' style="float:left;"></div>
<div class="simple-sns sns-widget" data-sharebtn='{"type":"item","key":"24875700842","client_id":"68"}' style="float:right;" title="微博分享"></div>
<div style="clear:both;"></div>
</div>
</div>
<div class="itembox" style="width:167px;">
<div class="bottom-sprice">
<a target="_blank">
<span class="rmb">RMB: </span><em>249.00</em>
<s class="oldprice"><span class="rmb">RMB: </span><em>599</em></s>
</a>
</div>
</div>
<div class="rates" style="width:167px;">
<div class="feedback">
<img data-ks-lazyload="http://wwc.alicdn.com/avatar/getAvatar.do?userId=0&width=24&height=24&type=sns" height="20" src="http://assets.alicdn.com/s.gif"/>
<span>1***_</span> 質(zhì)量很好,遮陽也超好,就是份量有點撐罐呼,因為是雙層所以卷完也比較大鞠柄,總體還是不錯的
</div>
</div>
</div>
<p>里面涵蓋了所有我們需要的信息。因此嫉柴,先用find_all
函數(shù)將所有tag為div
厌杜,class
為popitem
的先扒出來。扒出來之后计螺,發(fā)現(xiàn)list有重復(fù)的夯尽,通過set
函數(shù)刪除重復(fù)的項目</p>
find_item = interial_item.find_all("div", "popitem")
find_item = list(set(find_item))
<p>對find_item
里面的內(nèi)容進(jìn)行循環(huán),開始按照我們的需要將里面的內(nèi)容一個個的剔除出來危尿。</p>
<p>先是找出我們的primary key呐萌。在class
為desc
的標(biāo)簽中,里面就包含了產(chǎn)品的商品的名字以及對應(yīng)的鏈接谊娇,再仔細(xì)看看鏈接回發(fā)現(xiàn)后面有id=24875700842
肺孤,這就是我們要找的。注意济欢,找class
時候可以同時根據(jù)tag
和class
一起找赠堵,而class
的尋找除了可以用上面的方法,還可以通過關(guān)鍵字class_
(區(qū)別class
法褥,用class
會報錯)來尋找茫叭。找到href
之后,發(fā)現(xiàn)我們要的id就是一串?dāng)?shù)字半等,可以通過正則表達(dá)式re.search("\d+", href)
來搜索揍愁,并通過group()
返回搜索的字符串,再通過int
將字符串轉(zhuǎn)為整型杀饵,最終寫為:</p>
desc = item.find("div", class_="desc")
href = desc.a.get("href")
pri = int(re.search("\d+", href).group())
<p>這個時候莽囤,基本信息已經(jīng)找到了,可以將相應(yīng)的信息寫入我們的主表中切距,寫的時候需要判斷朽缎,這個id在我們的表格中是否存在,如果存在我們就不繼續(xù)添加信息到主表中:</p>
c.execute("SELECT skuid FROM UB;")
if (pri,) not in c.fetchall():
name = desc.string
prices = item.find("div", class_="bottom-sprice").find_all("em")
price = float(prices[0].string)
oldprice = float(prices[1].string)
href = "https:" + href
c.execute("INSERT INTO UB VALUES(?, ?, ?, ?, ?);", [pri, name, href, oldprice, price])
<p>后面就是將sount和cite扒出來谜悟,scount容易通過item.em.string
就出來话肖,但是剔除出來的是一個帶括號的字符,寫一個正則表達(dá)式的函數(shù)將括號剔除:</p>
def del_par(string):
patt = re.compile("\((.*)\)")
str_list = patt.findall(string)
return int(str_list[0])
<p>對應(yīng)的scount和cite找到之后葡幸,就要考慮怎么將數(shù)據(jù)寫入到我們的附表中最筒,這個時候需要注意有兩種情況,如果是第一次輸入的時候礼患,需要加入產(chǎn)品的skuid以及相應(yīng)的內(nèi)容是钥,如果是判定該skuid是已經(jīng)在附表的id列表中掠归,那就直接更新對應(yīng)列的數(shù)據(jù)即可:</p>
scount = del_par(item.em.string)
feedback = item.find("div", "feedback").contents
user = feedback[2].string
cite = user + ":" +feedback[3]
c.execute("SELECT scountskuid FROM SCOUNT;")
if (pri,) not in c.fetchall():
c.execute("INSERT INTO SCOUNT(scountskuid, '%s') VALUES('%s', '%s');" % (time, pri, scount))
c.execute("INSERT INTO CITE(citeskuid, '%s') VALUES('%s', '%s');" % (time, pri, scount))
else:
c.execute("UPDATE SCOUNT SET '%s' = '%s' WHERE scountskuid = '%s';" % (time, scount, pri))
c.execute("UPDATE CITE SET '%s' = '%s' WHERE citeskuid = '%s';" % (time, cite, pri))
最后缅叠,寫完之后悄泥。還要將所有我們的命令提交才叫完成:
c.close()
conn.commit()
Ubuntu定時任務(wù)
<p>為了可以時刻監(jiān)控數(shù)據(jù),需要在Ubuntu里面設(shè)置任務(wù)肤粱,買了一個國外的服務(wù)DigitalOcean弹囚,并設(shè)置Ubuntu的定時任務(wù):</p>
$ crontab -e
<p>通過nano設(shè)置任務(wù):</p>
30 8,13,18,23 * * * [script address] >> [log record] 2<&1
結(jié)語:
<p>這樣就基本完成自己寫的第一個項目任務(wù)了,可以收集信息领曼,可以儲存數(shù)據(jù)鸥鹉,當(dāng)然稍作改動就可以將數(shù)據(jù)保存為csv。除此之外庶骄,后續(xù)這個程序還有改進(jìn)的空間毁渗,包括:</p>
- 每天晚上對一天采集的數(shù)據(jù)進(jìn)行分析,分析出今天的最佳銷量(用Sql就可以實現(xiàn))
- 通過銷量和單價計算每日的銷售額(終于需要可以用Pandas了)
- 將上面的內(nèi)容統(tǒng)一生成pdf報表(pdf生成目前還沒有思路)
- 將報表發(fā)到郵箱中(發(fā)送郵箱應(yīng)該是可以很輕松完成的)
<p>下一篇文章將會對這方面進(jìn)行重點研究单刁。</p>