• 项目要求:

内容完整程度
可用性(可操作、易操作、美观)

  • 确定项目目标:

分析某二手手机交易论坛上的帖子,从中得出其用户行为的描述,为用户进行分类,输出洞察报告。

  • 从互联网获取数据:

使用python构建一个定向爬虫,爬取太平洋电脑网的二手手机交易论坛http://itbbs.pconline.com.cn/es/f240027.html的数据。
首先观察网页,

可以看到网页中有很多有价值的信息,包括分类信息,标题,用户,时间等,因此我们确信通过对该网页的爬取可以获得我们想要的数据。
下面开始分析网页源码,经过分析我们可以发现每个帖子的信息都在如下的结构中:

每个帖子的信息都在class=’cf’的<li>标签中,下面关注我们需要爬取的信息:用户uid在class=’area-author’的<div>标签的data-uid属性中,分类信息在class=’tit-sub’的<a>标签的string中,标题在class=’tit-toe’的<a>标签的string中,时间在class=’time’的<span>标签的string中。
经过分析,我们已经可以构建爬虫了,就像平常一样使用request库和beautifulsoup4库即可,下面是爬虫部分源码的截图,完整源码见附录1。

目标网页论坛像大部分论坛一样是分页的,某一页只显示特定数量的帖子,因此需要爬取多页数据,但是由于目标网页内部逻辑,当爬取页数过多会出现很多重复数据,经过试验发现爬取小于100页时数据没有重复,因此我们爬取100页的数据,爬取结果有1032条数据。数据的形态如下:

  • 数据清洗/整理:

首先观察发现,该网站中大于95%的帖子是用来出售手机的,因此放弃对买/卖/换的分类,全部认为是出售二手手机。
由于数据是以自然语言的形式存在的,需要对数据进行清洗和整理,判断出所交易手机的品牌。
首先我们使用到了python的第三方库——jieba库,来进行中文的分词。再对分出的词进行词频统计,统计的部分结果如下:

在这里我们采用了‘符合关键词——打标签’的方法,把所有词中能够反映出手机品牌的,按词频从高到低在python字典中对应到手机品牌,整理出的关键词结果如下:

然后我们根据人们发帖写标题的规律——重要的信息写在前面,把分类与标题拼成一个字符串,分词后对每个词寻找对应的品牌信息,若有对应则直接打上标签。
与此同时,整个帖子中都不存在关键词的,最后的标签结果会是None,这类数据直接删除,这样很多人成交后会把帖子改成《已出》等标题的无效信息就被清洗掉了。
清洗整理过后的数据再加上用户uid和时间信息,即完成了数据的清洗和整理,整理后的数据形态如下所示:

数据清洗和整理的源代码见附录2.

  • 描述统计&洞察结论:

有了上面的数据,我们已经可以各厂商在该论坛占比的基本情况进行统计了,统计结果如下:

可见苹果手机在二手市场中占有绝对优势的份额达到了2/3;另人欣喜的是,国产手机华为、小米分列二、三位,而如魅族,vivo等国产手机也占有一定的份额,国产手机总共占有约1/4的份额,而当年的手机巨头三星、诺基亚等正逐步没落。

  • 选择变量&算法:

因为要研究的是这些用户与二手交易相关的行为,因此选择变量为各品牌手机拥有台数。
我们的目标是将用户分群,因此选择聚类,方法选择最简单的 K-means 算法。

  • 设定参数 & 加载算法:

我们对每个用户的手机台数进行统计,得到的部分结果如下:

统计的源码见附录3.
然后就可以用k-means算法进行分类了,经过多次测试,发现当聚类数k=3时,分类结果的可解释性良好,分类结果如下(聚类源码见附录4)。

我们可以根据这张表对不同类别的人进行解读了,测试通过!

  • 报告撰写:

聚类模型结果可归为下图:

可见该论坛中大部分还是普通用户,人均交易约为1次,大致就是把不用的手机放到论坛上换点小钱的那种。
平台中的苹果商人数量很多,有110人,占比达到16.9%,由于人数较多导致人均交易次数被分流而很有限,只有3.4次。于此同时我们可以发现二手苹果商人对苹果手机执着程度很高,其他品牌的手机人均拥有数量均不到0.1台,应该是钟情于苹果手机的高利润了。
安卓商人这一类人数很少,仅有6人,占比仅仅1%,但是人均交易次数高达8.2次,可见由于人数较少,交易的机会也较大。其中二手三星手机交易比重最大,达3.5次,或许是因为三星作为外国企业在国内的利润较高。于此同时我们可以发现安卓商人可谓是爱好广泛,苹果、华为、小米手机人均拥有1台以上,不像二手苹果商人那样钟情于苹果市场。
以上为对用户的聚类结果,可见由于二手安卓商人人数较少,交易次数较多,在该论坛中倒卖二手安卓机的商机还是很大的。

最后附上词云图片(制作图片的源码见附录5):

  • 附录:

附录1

#爬取数据
import requests
from bs4 import BeautifulSoup
allUniv=[]
def getHTML(url):try:r=requests.get(url)r.raise_for_status()r.encoding='gb2312'return r.textexcept:return ''
def getInfoList(soup):lils=soup.find_all('li',{'class':'cf'})ils=[]for li in lils:title=li.find('a',{'class':'tit-sub'}).string.replace('[','').replace(']','')content=li.find('a',{'class':'tit toe'}).stringtime=li.find('span',{'class':'time'}).stringuid=li.find('div',{'class':'area-author'}).attrs['data-uid']ils.append([title,content,time,uid])return ils
def getOnePageInfo(url):html=getHTML(url)soup=BeautifulSoup(html,'html.parser')ils=getInfoList(soup)return ils
def rowFmt(row):f_row=[]for e in row:t=e.replace(',',',')f_row.append(t)return f_row
def main(num):info=getOnePageInfo('http://itbbs.pconline.com.cn/es/f240027.html')for i in range(2,num):info.extend(getOnePageInfo('http://itbbs.pconline.com.cn/es/f240027_'+str(i)+'.html'))fw=open('info__'+str(num)+'.csv','w')for row in info:f_row=rowFmt(row)try:fw.write(','.join(f_row)+'\n')except:print(row)fw.close()print('pages:',num,len(info),'finished')
main(100)

附录2:

#清洗,整理数据
import jieba
#[用户,品牌]
rep={'苹果':'Apple','国行':'Apple','6':'Apple','x':'Apple','iphone':'Apple','7p':'Apple','三星':'SAMSUNG','小米':'小米','8':'Apple','8p':'Apple','7':'Apple','港版':'Apple','华为':'华为','6sp':'Apple','荣耀':'华为','s8':'SAMSUNG','note8':'SAMSUNG','iphone6':'Apple','6s':'Apple','iphone7':'Apple','xs':'Apple','魅族':'魅族','vivo':'vivo','5s':'Apple','5':'Apple','7plus':'Apple','iphone6s':'Apple','iphonex':'Apple','魅蓝':'魅族','note10':'SAMSUNG','note9':'SAMSUNG','x23':'vivo','s9':'SAMSUNG','8plus':'Apple','mix2s':'小米','iphone7p':'Apple','oppo':'oppo','6p':'Apple','红米':'小米','iphone4':'Apple','mate9':'华为','iphone7plus':'Apple','诺基亚':'Nokia','粪':'Apple','note6':'SAMSUNG','x20a':'vivo','p20':'华为','mate10pro':'华为','mate10':'华为','p9':'华为','s7edge':'SAMSUNG','mix2':'小米','xsm':'Apple','xsmax':'Apple','mx5':'魅族','7p128':'Apple','note3':'SAMSUNG','r15':'oppo','i7p':'Apple','i7':'Apple','6sp128g':'Apple','pius':'Apple','pro6s':'魅族','mate8':'华为','r9':'oppo','6x':'小米','4x':'小米','p10plus':'华为','r17':'oppo','oppor11':'oppo','oppor11s':'oppo','oppaa73':'oppo','8p64g':'Apple','iphone5s':'Apple','10pro':'华为','mate9pro':'华为','iphone8p':'Apple','ip6':'Apple','i6':'Apple','iphone6sp':'Apple','iphone5':'Apple','iphone8':'Apple','vivonex':'vivo','6s128g':'Apple','6sp5.5':'Apple','nexs':'vivo'}
def listWords(filename):fo=open(filename,'r')words=[]counts={}for line in fo:txt=line.split(',')[1]t=txt.lower()word=jieba.lcut(t)words.extend(word)for word in words:counts[word]=counts.get(word,0)+1lst=sorted(counts.items(),key=lambda x:x[1],reverse=True)count=0for l in lst:count+=1print(l)print(count)
def getBrand(txt):words=jieba.lcut(txt)for word in words:if rep.get(word,0)!=0:return rep[word]return 0def getInfo(filename):ils=[]fo=open(filename,'r')count=0for line in fo:line=line.replace('\n','')content=line.split(',')txt=content[0]+content[1].lower()time=content[2]uid=content[3]brand=getBrand(txt)if brand!=0:ils.append([uid,brand,time])count+=1print(count)return ils
def main(filename):ils=getInfo(filename)fw=open('clr_'+filename,'w')for row in ils:fw.write(','.join(row)+'\n')fw.close()
main('info__100.csv')

附录3:

#统计用户信息
#[apple,sunsung,huawei,oppo,vivo,meizu,mi,nokia]
def getBrandCount(filename):brandCount={}fo=open(filename,'r')for line in fo:line=line.replace('\n','')content=line.split(',')brandCount[content[1]]=brandCount.get(content[1],0)+1print(brandCount)return brandCount
def getUserCount(filename):userCount={}fo=open(filename,'r')for line in fo:line=line.replace('\n','')content=line.split(',')uid=content[0]brand=content[1]value=userCount.get(uid,{'Apple':0,'SAMSUNG':0,'华为':0,'oppo':0,'vivo':0,'魅族':0,'小米':0,'Nokia':0})value[brand]+=1userCount[uid]=valuereturn userCount
def main(filename):uc=getUserCount(filename)fw=open('uc_'+filename,'w')count=0for item in uc.items():ls=[item[0]]values=list(item[1].values())val=[]for v in values:val.append(str(v))ls.extend(val)fw.write(','.join(ls)+'\n')count+=1fw.close()print(count,'finished')
main('clr_info__100.csv')

附录4:

#用户聚类
from numpy import *
def loadData(filename):data=[]fo=open(filename)for line in fo:line=line.replace('\n','')l1=line.split(',')ls=[]for i in range(1,9):ls.append(float(l1[i]))#print(ls)data.append(ls)fo.close()return data
def dist(va,vb):#calc distancereturn sqrt(sum(power((va-vb),2)))def randCents(dataSet,k):#random centersn=shape(dataSet)[1]cents=mat(zeros((k,n)))for i in range(n):mini=min(dataSet[:,i])maxi=max(dataSet[:,i])rangei=float(maxi-mini)cents[:,i]=mini+rangei*random.rand(k,1)return centsdef kMeans(dataSet,k,getDist=dist,getCents=randCents):m=shape(dataSet)[0]cluster=mat(zeros((m,2)))cents=getCents(dataSet,k)flag=Truewhile flag:flag=Falsefor i in range(m):minDist=infminIdx=-1for j in range(k):dist=getDist(cents[j,:],dataSet[i,:])if dist<minDist:minDist=distminIdx=jif cluster[i,0]!=minIdx:flag=Truecluster[i,:]=minIdx,minDist**2#print(cents)for cent in range(k):pts=[]for data in range(m):if cluster[data,0]==cent:pts.append(dataSet[data,:])#pts=mat(pts)cents[cent,:]=mean(pts,axis=0)return cents,cluster
def writeMat(m,filename):fw=open(filename,'w')for i in range(shape(m)[0]):row=[]for j in range(shape(m)[1]):row.append(str(m[i,j]))fw.write(','.join(row)+'\n')fw.close()
def main(filename,k):data=loadData(filename)dataSet=mat(data)cents0,cluster0=kMeans(dataSet,k)print(cents0)writeMat(cents0,str(k)+'cents__'+filename)writeMat(cluster0,str(k)+'cluster__'+filename)
main('uc_clr_info__100.csv',3)

附录5:

#作图
import numpy as np
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import PIL
import jiebadef drawPie(filename):fo=open(filename)fracs={}explode=[0.03,0.1,0.1,0.1,0.1,0.1,0.1,0.1]plt.axes(aspect=1)count=0for line in fo:line=line.replace('\n','')ls=line.split(',')fracs[ls[1]]=fracs.get(ls[1],0)+1count+=1print(fracs)val=fracs.values()key=list(fracs.keys())for i in range(len(key)):if key[i]=='华为':key[i]='HUAWEI'continueelif key[i]=='魅族':key[i]='MEIZU'continueelif key[i]=='小米':key[i]='MI'continueplt.pie(x=val,labels=key,explode=explode,autopct='%3.1f %%',shadow=False,labeldistance=1.1)plt.show()print(count)for i in fracs.items():print(i[0],i[1]/1032*100)def wordcloudplot(txt):path = r'C:\Windows\Fonts\FZSTK.TTF'alice_mask = np.array(PIL.Image.open('bg.jpg'))wordcloud = WordCloud(font_path=path,background_color="white",margin=5, width=1800, height=800, mask=alice_mask, max_words=80, max_font_size=60,random_state=42).generate(txt)wordcloud.to_file('dora.jpg')plt.imshow(wordcloud)plt.axis("off")plt.show()def main():fo=open('info__100.csv')ls=[]count=0for line in fo:txt=line.split(',')[1]t=txt.lower()word=jieba.lcut(t)for w in word:if len(w)>1:ls.append(w)print(len(ls))words=' '.join(ls)#print(words)wordcloudplot(words)
main()

成为数据科学家的第一个project相关推荐

  1. vue取数据第一个数据_我作为数据科学家的第一个月

    vue取数据第一个数据 A lot. 很多. I landed my first job as a Data Scientist at the beginning of August, and lik ...

  2. LinkedIn首席科学家:创业公司如何善用数据,加入下一个千亿美元市场?

    LinkedIn首席科学家:创业公司如何善用数据,加入下一个千亿美元市场? 据麦肯锡大数据行业研究报告,欧元区的大数据行业将通过改善公司运行效率.减少出错和增加税收,为公共部门创造2500亿欧元左右的 ...

  3. pinterest数据科学家访谈

    介绍 (Introduction) Pinterest, Inc. is a social media web and mobile application company founded in 20 ...

  4. 011 《数据科学家访谈录》读后感

    真的万万没有想到,读这样一本访谈录会记录下来这么多的令自己比较有感触的内容.25位各行各业的专家,通过他们的言谈内容,发现其中的通性--强大的撸码能力.扎实的统计数学知识以及沟通交流能力 . 反观自身 ...

  5. “数据科学家”或许不再性感,但“数据团队”的产业化才刚开始 | 专访领英全球数据科学团队负责人...

    来源:大数据文摘 本文约5750字,建议阅读6分钟. 本文为清华大学大数据研究中心联合大数据文摘发起的年度白皮书<顶级数据团队建设全景报告>系列专访的第二篇内容.<报告>囊括专 ...

  6. 中国的数据科学家阶层正在形成

    有人给予了大数据专家许多美好的称号,比如"数据开采者"."数据建筑师"等,但其中最时髦的当属"数据科学家".当记者在互联网上搜索" ...

  7. 数据科学家为什要用Git?怎么用?

    也许你在别的地方听说过Git.也许有人告诉过你,Git只适合软件开发人员.如果你是数据科学家,那么Git其实对你很重要.本文作者希望能够通过经验分享让你了解Git的重要性,以及如何在你的数据科学工作中 ...

  8. 软件工程模块开发卷宗_数据科学家应该了解的软件工程实践

    作者 |  Kurtis Pykes  译者 | 刘志勇策划 | 李冬梅我一直与朋友们热切地研究并尝试一些能使我成为更好的数据科学家的方法.如果不与那些帮助过我的人们分享目前的情况,我是不可能在事业上 ...

  9. 如何区分大数据下的三大利器:数据科学家,数据工程师与数据分析师

    与其他一些相关工程职位一样,数据科学家的影响力与互联网同进同退.数据工程师和数据分析师与数据科学家携手共同完成这幅"大数据时代"巨作.他们共同努力拟定数据平台要求,基础和高级算法, ...

最新文章

  1. GUI菜单——菜单条、菜单、子条目之间关系
  2. 解决webpack打包后-webkit-box-orient: vertical ;消失问题
  3. oracle 补丁打不上_oracle 打补丁常见问题
  4. c# 无法将类型隐式转换_C#中的隐式类型数组
  5. vs2010跟vs2008比较增加了哪些功能
  6. Linux驱动实现灯循环闪烁,TQ2440上LED灯闪烁效果的驱动程序实现
  7. 03-01 appium架构介绍与环境安装
  8. 深度学习 --- BP算法详解(误差反向传播算法)
  9. datagrid数据导出到excel文件给客户端下载的几种方法(转)
  10. BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )
  11. 【5月8 】NVIDIA 迁移学习工具包(TLT)最佳实践
  12. word转PDF时,英文单词的字母间距问题
  13. Threejs中使用A*算法寻路导航,Threejs室内室外地图导航
  14. C#进阶之路(四):拉姆达
  15. //一张纸的厚度是0.0001米,将纸对折,对折多少次厚度超过珠峰高度8848米
  16. 同步异步+阻塞非阻塞-三述
  17. Leetcode466.统计重复个数——掐头去尾寻找循环点
  18. CSDN VIP年卡大放送!中国大数据技术大会超值福利,等你来拿!
  19. 遍身罗绮者 不是养蚕人
  20. python ImportError: cannot import name ' ×××'解决方法

热门文章

  1. UE4-(光照)光照贴图大小及环境光遮蔽
  2. 阿里云ECS服务器实例是什么?关于实例的介绍
  3. 光脚丫学LINQ(006):投影
  4. 光脚丫学ASP.NET MVC(0008):非操作方法
  5. 轻松搭建--类似微信的聊天工具
  6. OSI七层参考模型是什么
  7. 【Python爬虫案例学习9】python爬取免费优质IP归属地查询接口
  8. JS逆向加密-Cryptojs库AES/DES/RSA等代码
  9. C语言结构体大小计算
  10. 一起“玩转”微信公众号营销