<p>自从上年年底了解到数据分析和挖掘这个概念之后,今年1月底毕业前夕,让自己完全沉下心来学习Python的数据,截止到目前也有4个多月了。我称这条路叫做数据之路,我一直在想,我们现在身处在这个被数据充满的世界里面,学会挖掘数据以及分析数据,就像是学会游泳一样。即使我不是一个数据分析师,甚至不是程序员,我仅仅是作为在一家小小的电商公司的计划工程师,学会数据分析一定可以提高我的工作效率(当然最开始的时候是考虑能不能将这个当成自己的副业)。</p>
数据之路:
<p>废话不说,先来聊聊我1月底到现在整个数据之路的经过。决定了要用Python进行数据分析之后,我是先通过这个Codecademy来进行Python学习的。这个网址好像主要是针对网页编程的。迅速的过完里面的Python练习,有了基本概念,就开始考虑怎么用Python来进行数据分析了。</p>
<p>这个时候,第二个网站映入了我的眼帘,叫做Dataquest。这个网址不错,里面有所有关于数据分析的课程,他有三个Level,分别叫做Data Analyst,Data Scientist,以及Data Engineer(这个目前还没有开放)。</p>
- Data Analyst的课程包括:基本Python的用法,Pandas的使用,数据可视化,Linux系统命令行操作基础,简单的爬虫网络,统计原理,R语言。
- Data Scientist可以认为是Data Analyst 的升级版,加入了:机器学习,数据结构和算法,高级Python用法,大数据的处理方式。
<p>其中部分课程是要收费才能做,我就充了两个月的Basic会员,将Data Analyst的课程完成了个85%之后感觉就差不多了。</p>
<p>除此之外,还看了些书,最重要当然要看是Python各个库的Document。</p>
第一个实例:
立项
<p>第一个实例做什么我还是想了好久,想做点实用点的,就选了淘宝的一个门店,来做数据每天的销量以及评论更新状态分析吧。任务包括:</p>
- 收集产品的基本信息;
- 每天定点收集最新的销量以及推送的评论;
<p>随便选了个门店,先打开主页浏览下,发现是卖伞的,仔细看一下,有5个类别:Black、Air、Bon、Joli、Moma(X系列进去看好像都停售,就不管了)。</p>
Request扒内容
<p>采用Requests模块,采用get
(说起来get
函数和post
函数是什么区别,什么时候要用get
函数或者post
函数,到现还没有搞懂)函数将url里面的内容扒下来,同时加了headers
,将自己伪装成浏览器。将扒下来来的内容用BeautifulSoup进行中‘lxml’解析器进行解析,这样网页的内容就像是被扒光衣服一样展示在我们面前。</p>
<p>另外,在BeautifulSoup中有四种解析器,分别是自带的、'lxml'、‘xml’以及‘html5lib’四种解析器,具体区别可以看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的内容之后,快速在Console上Crtl+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" href="//bananaumbrella.tmall.com/category-819110723.htm?search=y&catName=Black">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" href="//bananaumbrella.tmall.com/category-819110724.htm?search=y&catName=Air">Air</a>
</h4>
</li>
<p>其中,每个tag里面的href
中的url就是我们需要的。利用BeautifulSoup中的find_all
函数将这些标签抓出来,先通过find_all
寻找所有a标签,返回一个含有a
标签的list,再通过list的链表推导式将其中的href
抓取出来。其中get()
是个好东西,例如这句话:</p>
<a class="cat-name fst-cat-name" href="//bananaumbrella.tmall.com/category-1184183711.htm?search=y&catName=Moma">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数据库
<p> 我也不知道为什么我对Sql如此的喜欢,其中项目一开始我是导出为csv函数的,后来大概是可能想到之前在dataquest上学了Sql的语法,就想在这个任务里面尝试吧。(从最终的结果来看,其实我不太能分别我最后得到db好还是csv好,求大神指点呀。。)</p>
<p>在Sql上学过db中几个表格的关联,经过无数次的尝试,我知道每次采集的数据中有很多相同的参数,例如产品的ID(skuid),名称(name),链接地址(href),原售价(oldprice),活动售价(price)。而变化的参数有两个:</p>
- 销量(scount)
- 评论(cite)
<p>评论和销量每次更新时,都会以当前时间增加一列,加入新内容。</p>
<p>因此我就想到了用采用表格的键约束,制作一个主表用于存放变化不变的参数,制作两个附表分别存放销量和评论,主表的附表之间通过键(key)来连接。</p>
<p>所以首先当然是要建立一个数据库db文件了,然后建立表格,建表的时候一开始我是采用CREATE TABLE UB(skuid int primary key , name text, href text, oldprice real, price real);
,后来发现如果重复调试这个程序的时候,必须要先将久的db文件删除之后,才能执行,不然回报错。所以后面我就将这句话改成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)
解析每个类别网址的内容
<p>首先继续采用Requests函数以及BeautifulSoup函数去将页面的内容解析出来,过程和我们一开始做的一样,不做重复。</p>
<p>结合浏览器的Inspect功能进行搜索,不难找到每个类似的这样一段内容(谁可以告诉我像这种html正确的排版应该是怎样的。。。):</p>
<div class="popitem" style="width:167px;">
<a class="pic" href="//item.taobao.com/item.htm?id=24875700842" style="width:167px;" target="_blank">
<img data-ks-lazyload="//gdp.alicdn.com/bao/uploaded/i4/TB13q8EKpXXXXbzaXXXXXXXXXXX_!!0-item_pic.jpg_220x10000.jpg" src="//assets.alicdn.com/s.gif" title="【新品】BananaUmbrella蕉下小黑伞苏桃双层防晒女太阳伞遮阳伞" width="167"/>
</a>
<div class="expannel expannel-float">
<a class="mask" href="//item.taobao.com/item.htm?id=24875700842" style="display:block;visibility:visible;" target="_blank">
</a>
<div class="exinfo exi_2 ext_4 abs" style="bottom:0px;">
<div class="desc">
<a href="//item.taobao.com/item.htm?id=24875700842" 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 href="//item.taobao.com/item.htm?id=24875700842" 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="//wwc.alicdn.com/avatar/getAvatar.do?userId=0&width=24&height=24&type=sns" height="20" src="//assets.alicdn.com/s.gif"/>
<span>1***_</span> 质量很好,遮阳也超好,就是份量有点撑,因为是双层所以卷完也比较大,总体还是不错的
</div>
</div>
</div>
<p>里面涵盖了所有我们需要的信息。因此,先用find_all
函数将所有tag为div
,class
为popitem
的先扒出来。扒出来之后,发现list有重复的,通过set
函数删除重复的项目</p>
find_item = interial_item.find_all("div", "popitem")
find_item = list(set(find_item))
<p>对find_item
里面的内容进行循环,开始按照我们的需要将里面的内容一个个的剔除出来。</p>
<p>先是找出我们的primary key。在class
为desc
的标签中,里面就包含了产品的商品的名字以及对应的链接,再仔细看看链接回发现后面有id=24875700842
,这就是我们要找的。注意,找class
时候可以同时根据tag
和class
一起找,而class
的寻找除了可以用上面的方法,还可以通过关键字class_
(区别class
,用class
会报错)来寻找。找到href
之后,发现我们要的id就是一串数字,可以通过正则表达式re.search("\d+", href)
来搜索,并通过group()
返回搜索的字符串,再通过int
将字符串转为整型,最终写为:</p>
desc = item.find("div", class_="desc")
href = desc.a.get("href")
pri = int(re.search("\d+", href).group())
<p>这个时候,基本信息已经找到了,可以将相应的信息写入我们的主表中,写的时候需要判断,这个id在我们的表格中是否存在,如果存在我们就不继续添加信息到主表中:</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
就出来,但是剔除出来的是一个带括号的字符,写一个正则表达式的函数将括号剔除:</p>
def del_par(string):
patt = re.compile("\((.*)\)")
str_list = patt.findall(string)
return int(str_list[0])
<p>对应的scount和cite找到之后,就要考虑怎么将数据写入到我们的附表中,这个时候需要注意有两种情况,如果是第一次输入的时候,需要加入产品的skuid以及相应的内容,如果是判定该skuid是已经在附表的id列表中,那就直接更新对应列的数据即可:</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定时任务
<p>为了可以时刻监控数据,需要在Ubuntu里面设置任务,买了一个国外的服务DigitalOcean,并设置Ubuntu的定时任务:</p>
$ crontab -e
<p>通过nano设置任务:</p>
30 8,13,18,23 * * * [script address] >> [log record] 2<&1
结语:
<p>这样就基本完成自己写的第一个项目任务了,可以收集信息,可以储存数据,当然稍作改动就可以将数据保存为csv。除此之外,后续这个程序还有改进的空间,包括:</p>
- 每天晚上对一天采集的数据进行分析,分析出今天的最佳销量(用Sql就可以实现)
- 通过销量和单价计算每日的销售额(终于需要可以用Pandas了)
- 将上面的内容统一生成pdf报表(pdf生成目前还没有思路)
- 将报表发到邮箱中(发送邮箱应该是可以很轻松完成的)
<p>下一篇文章将会对这方面进行重点研究。</p>