作者丨躲猫猫的猫
链接:
/zhaww/p/9636383.html
大家好,希望各位能怀着正直、严谨、专业的心态观看这篇文章。ヾ(๑╹◡╹)ノ"
老天真的对我不薄,让我终于有了女友,自从有了女友后,我为了能替女朋友买上一件心怡的内衣,接下来我们尝试用 Python 抓取天猫内衣销售数据,并分析得到中国女性普遍的罩杯数据、最受欢迎的内衣颜色是什么、评论的关键字。我们先看看分析得到的成果是怎样的?(讲的很详细,推荐跟着敲一遍)
(买个内衣这么开心)
图片看不清楚的话,可以把图片单独拉到另一个窗口。这里是分析了一万条数据得出的结论,可能会有误差,但是还是希望单身的你们能找到 0.06% 那一批妹纸。下面我会详细介绍怎么抓取天猫内衣销售数据,存储、分析、展示。
研究天猫网站
抓取天猫评论数据
存储、分析数据
可视化
研究天猫网站
我们随意进入一个商品的购买界面(能看到评论的那个界面),F12 开发者模式 -- Network 栏 -- 刷新下界面 -- 在如图的位置搜索 list_ 会看到一个 list_detail_rate.htm?itemId= ….
如下图:【单击】这个url 能看到返回的是一个 Json 数据 ,检查一下你会发现这串 Json 就是商品的评论数据 ['rateDetail']['rateList']
【双击】这个url 你会得到一个新界面,如图:
看一下这个信息:
这里的路径 就是获取评论数据的 url了。这个 URL 有很多参数你可以分析一下每个值都是干嘛的。
itemId 对应的是商品id, sellerId 对应的是店铺id,currentPage 是当前页。这里 sellerId 可以填任意值,不影响数据的获取。
抓取天猫评论数据
写一个抓取天猫评论数据的方法。getCommentDetail
#获取商品评论数据defgetCommentDetail(itemId,currentPage):url='/list_detail_rate.htm?itemId='+str(itemId)+'&sellerId=2451699564&order=3¤tPage='+str(currentPage)+'&append=0callback=jsonp336'# itemId 产品id ;sellerId 店铺id 字段必须有值,但随意值就行html=common.getUrlContent(url)#获取网页信息#删掉返回的多余信息html=html.replace('jsonp128(','')#需要确定是不是jsonp128html=html.replace(')','')html=html.replace('false','"false"')html=html.replace('true','"true"')#将string转换为字典对象tmalljson=json.loads(html)returntmalljson
这里需要注意的是 jsonp128 这个值需要你自己看一下,你那边跟我这个应该是不同的。
在上面的方法里有两个变量,itemId 和 currentPage 这两个值我们动态来控制,所以我们需要获得 一批 商品id号 和 评论的最大页数 用来遍历。
写个获取商品评论最大页数的方法 getLastPage
#获取商品评论最大页数defgetLastPage(itemId):tmalljson=getCommentDetail(itemId,1)returntmalljson['rateDetail']['paginator']['lastPage']#最大页数
那现在怎么获取 产品的id 列表呢?我们可以在天猫中搜索商品关键字 用开发者模式观察
这里观察一下这个页面的元素分布,很容易就发现了 商品的id 信息,当然你可以想办法确认一下。
现在就写个 获取商品id 的方法 getProductIdList
#获取商品iddefgetProductIdList():url='/search_product.htm?q=内衣'#q参数是查询的关键字html=common.getUrlContent(url)#获取网页信息soup=BeautifulSoup(html,'html.parser')idList=[]#用BeautifulSoup提取商品页面中所有的商品IDproductList=soup.find_all('div',{'class':'product'})forproductinproductList:idList.append(product['data-id'])returnidList
现在所有的基本要求都有了,是时候把他们组合起来。
在 main 方法中写剩下的组装部分
if__name__=='__main__':productIdList=getProductIdList()#获取商品idinitial=0whileinitial<len(productIdList)-30:#总共有60个商品,我只取了前30个try:itemId=productIdList[initial]print('----------',itemId,'------------')maxPage=getLastPage(itemId)#获取商品评论最大页数num=1whilenum<=maxPageandnum<20:#每个商品的评论我最多取20页,每页有20条评论,也就是每个商品最多只取400个评论try:#抓取某个商品的某页评论数据tmalljson=getCommentDetail(itemId,num)rateList=tmalljson['rateDetail']['rateList']commentList=[]n=0while(n<len(rateList)):comment=[]#商品描述colorSize=rateList[n]['auctionSku']m=re.split('[:;]',colorSize)rateContent=rateList[n]['rateContent']dtime=rateList[n]['rateDate']comment.append(m[1])comment.append(m[3])comment.append('天猫')comment.append(rateContent)comment.append(dtime)commentList.append(comment)n+=1print(num)sql="insertintobras(bra_id,bra_color,bra_size,resource,comment,comment_time)value(null,%s,%s,%s,%s,%s)"common.patchInsertData(sql,commentList)#mysql操作的批量插入num+=1exceptExceptionase:num+=1print(e)continueinitial+=1exceptExceptionase:print(e)
所有的代码就这样完成了,我现在把 common.py 的代码,还有 tmallbra.py 的代码都贴出来
#-*-coding:utf-8-*-#Author:zwwimportrequestsimporttimeimportrandomimportsocketimporthttp.clientimportpymysqlimportcsv#封装requestsclassCommon(object):defgetUrlContent(self,url,data=None):header={'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','Accept-Encoding':'gzip,deflate,br','Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8','user-agent':"User-Agent:Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/62.0.3202.94Safari/537.36",'cache-control':'max-age=0'}#request的请求头timeout=random.choice(range(80,180))whileTrue:try:rep=requests.get(url,headers=header,timeout=timeout)#请求url地址,获得返回response信息#rep.encoding='utf-8'breakexceptsocket.timeoutase:#以下都是异常处理print('3:',e)time.sleep(random.choice(range(8,15)))exceptsocket.errorase:print('4:',e)time.sleep(random.choice(range(20,60)))excepthttp.client.BadStatusLinease:print('5:',e)time.sleep(random.choice(range(30,80)))excepthttp.client.IncompleteReadase:print('6:',e)time.sleep(random.choice(range(5,15)))print('requestsuccess')returnrep.text#返回的Html全文defwriteData(self,data,url):withopen(url,'a',errors='ignore',newline='')asf:f_csv=csv.writer(f)f_csv.writerows(data)print('write_csvsuccess')defqueryData(self,sql):db=pymysql.connect("localhost","zww","960128","test")cursor=db.cursor()results=[]try:cursor.execute(sql)#执行查询语句results=cursor.fetchall()exceptExceptionase:print('查询时发生异常'+e)#如果发生错误则回滚db.rollback()#关闭数据库连接db.close()returnresultsprint('insertdatasuccess')definsertData(self,sql):#打开数据库连接db=pymysql.connect("localhost","zww","000000","zwwdb")#使用cursor()方法创建一个游标对象cursorcursor=db.cursor()try:#sql="INSERTINTOWEATHER(w_id,w_date,w_detail,w_temperature)VALUES(null,'%s','%s','%s')"%(data[0],data[1],data[2])cursor.execute(sql)#单条数据写入#提交到数据库执行mit()exceptExceptionase:print('插入时发生异常'+e)#如果发生错误则回滚db.rollback()#关闭数据库连接db.close()print('insertdatasuccess')defpatchInsertData(self,sql,datas):#打开数据库连接db=pymysql.connect("localhost","zww","960128","test")#使用cursor()方法创建一个游标对象cursorcursor=db.cursor()try:#批量插入数据#cursor.executemany('insertintoWEATHER(w_id,w_date,w_detail,w_temperature_low,w_temperature_high)value(null,%s,%s,%s,%s)',datas)cursor.executemany(sql,datas)#提交到数据库执行mit()exceptExceptionase:print('插入时发生异常'+e)#如果发生错误则回滚db.rollback()#关闭数据库连接db.close()print('insertdatasuccess')
上面需要注意,数据库的配置。
#-*-coding:utf-8-*-#Author:monimportCommonfrombs4importBeautifulSoupimportjsonimportreimportpymysqlcommon=Common()#获取商品iddefgetProductIdList():url='/search_product.htm?q=内衣'#q参数是查询的关键字,这要改变一下查询值,就可以抓取任意你想知道的数据html=common.getUrlContent(url)#获取网页信息soup=BeautifulSoup(html,'html.parser')idList=[]#用BeautifulSoup提取商品页面中所有的商品IDproductList=soup.find_all('div',{'class':'product'})forproductinproductList:idList.append(product['data-id'])returnidList#获取商品评论数据defgetCommentDetail(itemId,currentPage):url='/list_detail_rate.htm?itemId='+str(itemId)+'&sellerId=2451699564&order=3¤tPage='+str(currentPage)+'&append=0callback=jsonp336'# itemId 产品id ;sellerId 店铺id 字段必须有值,但随意值就行html=common.getUrlContent(url)#获取网页信息#删掉返回的多余信息html=html.replace('jsonp128(','')#需要确定是不是jsonp128html=html.replace(')','')html=html.replace('false','"false"')html=html.replace('true','"true"')#将string转换为字典对象tmalljson=json.loads(html)returntmalljson#获取商品评论最大页数defgetLastPage(itemId):tmalljson=getCommentDetail(itemId,1)returntmalljson['rateDetail']['paginator']['lastPage']#最大页数if__name__=='__main__':productIdList=getProductIdList()#获取商品idinitial=0whileinitial<len(productIdList)-30:#总共有60个商品,我只取了前30个try:itemId=productIdList[initial]print('----------',itemId,'------------')maxPage=getLastPage(itemId)#获取商品评论最大页数num=1whilenum<=maxPageandnum<20:#每个商品的评论我最多取20页,每页有20条评论,也就是每个商品最多只取400个评论try:#抓取某个商品的某页评论数据tmalljson=getCommentDetail(itemId,num)rateList=tmalljson['rateDetail']['rateList']commentList=[]n=0while(n<len(rateList)):comment=[]#商品描述colorSize=rateList[n]['auctionSku']m=re.split('[:;]',colorSize)rateContent=rateList[n]['rateContent']dtime=rateList[n]['rateDate']comment.append(m[1])comment.append(m[3])comment.append('天猫')comment.append(rateContent)comment.append(dtime)commentList.append(comment)n+=1print(num)sql="insertintobras(bra_id,bra_color,bra_size,resource,comment,comment_time)value(null,%s,%s,%s,%s,%s)"common.patchInsertData(sql,commentList)#mysql操作的批量插入num+=1exceptExceptionase:num+=1print(e)continueinitial+=1exceptExceptionase:print(e)
存储、分析数据
所有的代码都有了,就差数据库的建立了。我这里用的是 MySql 数据库。
CREATETABLE`bra`(`bra_id`int(11)NOTNULLAUTO_INCREMENTCOMMENT'id',`bra_color`varchar(25)NULLCOMMENT'颜色',`bra_size`varchar(25)NULLCOMMENT'罩杯',`resource`varchar(25)NULLCOMMENT'数据来源',`comment`varchar(500)CHARACTERSETutf8mb4DEFAULTNULLCOMMENT'评论',`comment_time`datetimeNULLCOMMENT'评论时间',PRIMARYKEY(`bra_id`))charactersetutf8;
这里有两个地方需要注意, comment 评论字段需要设置编码格式为 utf8mb4 ,因为可能有表情文字。还有表需要设置为 utf8 编码,不然存不了中文。
建好了表,就可以完整执行代码了。(这里的执行可能需要点时间,可以做成多线程的方式)。看一下执行完之后,数据库有没有数据。
数据是有了,但是有些我们多余的文字描述,我们可以稍微整理一下。
updatebrasetbra_color=REPLACE(bra_color,'2B6521-无钢圈4-','');updatebrasetbra_color=REPLACE(bra_color,'-1','');updatebrasetbra_color=REPLACE(bra_color,'5','');updatebrasetbra_size=substr(bra_size,1,3);
这里需要根据自己实际情况来修改。如果数据整理的差不多了,我们可以分析一下数据库的信息。
select'A罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%A'unionallselect'B罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%B'unionallselect'C罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%C'unionallselect'D罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%D'unionallselect'E罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%E'unionallselect'F罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%F'unionallselect'G罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%G'unionallselect'H罩杯'as罩杯,CONCAT(ROUND(COUNT(*)/(selectcount(*)frombra)*100,2),"%")as比例,COUNT(*)as销量frombrawherebra_sizelike'%H'orderby销量desc;
(想知道是哪6位小姐姐买的 G (~ ̄▽ ̄)~ )
数据可视化
数据的展示,我用了是 mycharts 模块,如果不了解的可以去学习一下/#/zh-cn/prepare
这里我就不细说了,直接贴代码看
#encoding:utf-8#monimportCommonif__name__=='__main__':common=Common()results=common.queryData("""selectcount(*)frombrawherebra_sizelike'%A'unionallselectcount(*)frombrawherebra_sizelike'%B'unionallselectcount(*)frombrawherebra_sizelike'%C'unionallselectcount(*)frombrawherebra_sizelike'%D'unionallselectcount(*)frombrawherebra_sizelike'%E'unionallselectcount(*)frombrawherebra_sizelike'%F'unionallselectcount(*)frombrawherebra_sizelike'%G'""")#获取每个罩杯数量attr=["A罩杯",'G罩杯',"B罩杯","C罩杯","D罩杯","E罩杯","F罩杯"]v1=[results[0][0],results[6][0],results[1][0],results[2][0],results[3][0],results[4][0],results[5][0]]pie=Pie("内衣罩杯",width=1300,height=620)pie.add("",attr,v1,is_label_show=True)pie.render('size.html')print('success')results=common.queryData("""selectcount(*)frombrawherebra_colorlike'%肤%'unionallselectcount(*)frombrawherebra_colorlike'%灰%'unionallselectcount(*)frombrawherebra_colorlike'%黑%'unionallselectcount(*)frombrawherebra_colorlike'%蓝%'unionallselectcount(*)frombrawherebra_colorlike'%粉%'unionallselectcount(*)frombrawherebra_colorlike'%红%'unionallselectcount(*)frombrawherebra_colorlike'%紫%'unionallselectcount(*)frombrawherebra_colorlike'%绿%'unionallselectcount(*)frombrawherebra_colorlike'%白%'unionallselectcount(*)frombrawherebra_colorlike'%褐%'unionallselectcount(*)frombrawherebra_colorlike'%黄%'""")#获取每个罩杯数量attr=["肤色",'灰色',"黑色","蓝色","粉色","红色","紫色",'绿色',"白色","褐色","黄色"]v1=[results[0][0],results[1][0],results[2][0],results[3][0],results[4][0],results[5][0],results[6][0],results[7][0],results[8][0],results[9][0],results[10][0]]pieColor=Pie("内衣颜色",width=1300,height=620)pieColor.add("",attr,v1,is_label_show=True)pieColor.render('color.html')print('success')
这一章就到这里了,该知道的你也知道了,不该知道的你也知道了。
哪些人是G杯,哪些妹子是A杯,以后找对象就不用担心这块的了。
代码全部存放在 GitHub上:
/zwwjava/python_capture