利用Python对天猫店铺销售进行分析.上

<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为divclasspopitem的先扒出来。扒出来之后,发现list有重复的,通过set函数删除重复的项目</p>

find_item = interial_item.find_all("div", "popitem")
find_item = list(set(find_item))

<p>对find_item里面的内容进行循环,开始按照我们的需要将里面的内容一个个的剔除出来。</p>

<p>先是找出我们的primary key。在classdesc的标签中,里面就包含了产品的商品的名字以及对应的链接,再仔细看看链接回发现后面有id=24875700842,这就是我们要找的。注意,找class时候可以同时根据tagclass一起找,而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>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容