Python动手分析天猫内衣售卖数据,得到你想知道的信息
Posted 躲猫猫的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python动手分析天猫内衣售卖数据,得到你想知道的信息相关的知识,希望对你有一定的参考价值。
大家好,希望各位能怀着正直、严谨、专业的心态观看这篇文章。ヾ(๑╹◡╹)ノ"
接下来我们尝试用 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
# 获取商品评论数据 def getCommentDetail(itemId,currentPage): url = \'https://rate.tmall.com/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(\',\'\') #需要确定是不是 jsonp128 html = html.replace(\')\',\'\') html = html.replace(\'false\',\'"false"\') html = html.replace(\'true\',\'"true"\') # 将string 转换为字典对象 tmalljson = json.loads(html) return tmalljson
这里需要注意的是 jsonp128 这个值需要你自己看一下,你那边跟我这个应该是不同的。
还有几十 common 这我自己封装的一个工具类,主要就是上一篇博客里写的一些功能,想 requests 和 pymysql 模块的功能。在文章最后我会贴出来。
在上面的方法里有两个变量,itemId 和 currentPage 这两个值我们动态来控制,所以我们需要获得 一批 商品id号 和 评论的最大页数 用来遍历。
写个获取商品评论最大页数的方法 getLastPage
# 获取商品评论最大页数 def getLastPage(itemId): tmalljson = getCommentDetail(itemId,1) return tmalljson[\'rateDetail\'][\'paginator\'][\'lastPage\'] #最大页数
那现在怎么获取 产品的id 列表呢? 我们可以在天猫中搜索商品关键字 用开发者模式观察
这里观察一下这个页面的元素分布,很容易就发现了 商品的id 信息,当然你可以想办法确认一下。
现在就写个 获取商品id 的方法 getProductIdList
# 获取商品id def getProductIdList(): url = \'https://list.tmall.com/search_product.htm?q=内衣\' # q参数 是查询的关键字 html = common.getUrlContent(url) # 获取网页信息 soup = BeautifulSoup(html,\'html.parser\') idList = [] # 用Beautiful Soup提取商品页面中所有的商品ID productList = soup.find_all(\'div\', {\'class\': \'product\'}) for product in productList: idList.append(product[\'data-id\']) return idList
现在所有的基本要求都有了,是时候把他们组合起来。
在 main 方法中写剩下的组装部分
if __name__ == \'__main__\': productIdList = getProductIdList() #获取商品id initial = 0 while initial < len(productIdList) - 30: # 总共有60个商品,我只取了前30个 try: itemId = productIdList[initial] print(\'----------\', itemId, \'------------\') maxPage = getLastPage(itemId) #获取商品评论最大页数 num = 1 while num <= maxPage and num < 20: #每个商品的评论我最多取20 页,每页有20条评论,也就是每个商品最多只取 400 个评论 try: # 抓取某个商品的某页评论数据 tmalljson = getCommentDetail(itemId, num) rateList = tmalljson[\'rateDetail\'][\'rateList\'] commentList = [] n = 0 while (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 += 1 print(num) sql = "insert into bras(bra_id, bra_color, bra_size, resource, comment, comment_time) value(null, %s, %s, %s, %s, %s)" common.patchInsertData(sql, commentList) # mysql操作的批量插入 num += 1 except Exception as e: num += 1 print(e) continue initial += 1 except Exception as e: print(e)
所有的代码就这样完成了,我现在把 common.py 的代码,还有 tmallbra.py 的代码都贴出来
# -*- coding:utf-8 -*- # Author: zww import requests import time import random import socket import http.client import pymysql import csv # 封装requests class Common(object): def getUrlContent(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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36", \'cache-control\': \'max-age=0\' } # request 的请求头 timeout = random.choice(range(80, 180)) while True: try: rep = requests.get(url, headers=header, timeout=timeout) # 请求url地址,获得返回 response 信息 # rep.encoding = \'utf-8\' break except socket.timeout as e: # 以下都是异常处理 print(\'3:\', e) time.sleep(random.choice(range(8, 15))) except socket.error as e: print(\'4:\', e) time.sleep(random.choice(range(20, 60))) except http.client.BadStatusLine as e: print(\'5:\', e) time.sleep(random.choice(range(30, 80))) except http.client.IncompleteRead as e: print(\'6:\', e) time.sleep(random.choice(range(5, 15))) print(\'request success\') return rep.text # 返回的 Html 全文 def writeData(self, data, url): with open(url, \'a\', errors=\'ignore\', newline=\'\') as f: f_csv = csv.writer(f) f_csv.writerows(data) print(\'write_csv success\') def queryData(self, sql): db = pymysql.connect("localhost", "zww", "960128", "test") cursor = db.cursor() results = [] try: cursor.execute(sql) #执行查询语句 results = cursor.fetchall() except Exception as e: print(\'查询时发生异常\' + e) # 如果发生错误则回滚 db.rollback() # 关闭数据库连接 db.close() return results print(\'insert data success\') def insertData(self, sql): # 打开数据库连接 db = pymysql.connect("localhost", "zww", "000000", "zwwdb") # 使用 cursor() 方法创建一个游标对象 cursor cursor = db.cursor() try: # sql = "INSERT INTO WEATHER(w_id, w_date, w_detail, w_temperature) VALUES (null, \'%s\',\'%s\',\'%s\')" % (data[0], data[1], data[2]) cursor.execute(sql) #单条数据写入 # 提交到数据库执行 db.commit() except Exception as e: print(\'插入时发生异常\' + e) # 如果发生错误则回滚 db.rollback() # 关闭数据库连接 db.close() print(\'insert data success\') def patchInsertData(self, sql, datas): # 打开数据库连接 db = pymysql.connect("localhost", "zww", "960128", "test") # 使用 cursor() 方法创建一个游标对象 cursor cursor = db.cursor() try: # 批量插入数据 # cursor.executemany(\'insert into WEATHER(w_id, w_date, w_detail, w_temperature_low, w_temperature_high) value(null, %s,%s,%s,%s)\',datas) cursor.executemany(sql, datas) # 提交到数据库执行 db.commit() except Exception as e: print(\'插入时发生异常\' + e) # 如果发生错误则回滚 db.rollback() # 关闭数据库连接 db.close() print(\'insert data success\')
上面需要注意,数据库的配置。
# -*- coding:utf-8 -*- # Author: zww from Include.commons.common import Common from bs4 import BeautifulSoup import json import re import pymysql common = Common() # 获取商品id def getProductIdList(): url = \'https://list.tmall.com/search_product.htm?q=内衣\' # q参数 是查询的关键字,这要改变一下查询值,就可以抓取任意你想知道的数据 html = common.getUrlContent(url) # 获取网页信息 soup = BeautifulSoup(html,\'html.parser\') idList = [] # 用Beautiful Soup提取商品页面中所有的商品ID productList = soup.find_all(\'div\', {\'class\': \'product\'}) for product in productList: idList.append(product[\'data-id\']) return idList # 获取商品评论数据 def getCommentDetail(itemId,currentPage): url = \'https://rate.tmall.com/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(\',\'\') #需要确定是不是 jsonp128 html = html.replace(\')\',\'\') html = html.replace(\'false\',\'"false"\') html = html.replace(\'true\',\'"true"\') # 将string 转换为字典对象 tmalljson = json.loads(html) return tmalljson # 获取商品评论最大页数 def getLastPage(itemId): tmalljson = getCommentDetail(itemId,1) return tmalljson[\'rateDetail\'][\'paginator\'][\'lastPage\'] #最大页数 if __name__ == \'__main__\': productIdList = getProductIdList() #获取商品id initial = 0 while initial < len(productIdList) - 30: # 总共有60个商品,我只取了前30个 try: itemId = productIdList[initial] print(\'----------\', itemId, \'------------\') maxPage = getLastPage(itemId) #获取商品评论最大页数 num = 1 while num <= maxPage and num < 20: #每个商品的评论我最多取20 页,每页有20条评论,也就是每个商品最多只取 400 个评论 try: # 抓取某个商品的某页评论数据 tmalljson = getCommentDetail(itemId, num) rateList = tmalljson[\'rateDetail\'][\'rateList\'] commentList = [] n = 0 while (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 += 1 print(num) sql = "insert into bras(bra_id, bra_color, bra_size, resource, comment, comment_time) value(null, %s, %s, %s, %s, %s)" common.patchInsertData(sql, commentList) # mysql操作的批量插入 num += 1 except Exception as e: num += 1 print(e) continue initial += 1 except Exception as e: print(e)
存储、分析数据
所有的代码都有了,就差数据库的建立了。我这里用的是 MySql 数据库。
CREATE TABLE `bra` ( `bra_id` int(11) NOT NULL AUTO_INCREMENT COMMENT \'id\' , `bra_color` varchar(25) NULL COMMENT \'颜色\' , `bra_size` varchar(25) NULL COMMENT \'罩杯\' , `resource` varchar(25) NULL COMMENT \'数据来源\' , `comment` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT \'评论\' , `comment_time` datetime NULL COMMENT \'评论时间\' , PRIMARY KEY (`bra_id`) ) character set utf8 ;
这里有两个地方需要注意, comment 评论字段需要设置编码格式为 utf8mb4 ,因为可能有表情文字。还有表需要设置为 utf8 编码,不然存不了中文。
建好了表,就可以完整执行代码了。(这里的执行可能需要点时间,可以做成多线程的方式)。看一下执行完之后,数据库有没有数据。
数据是有了,但是有些我们多余的文字描述,我们可以稍微整理一下。
update bra set bra_color = REPLACE(bra_color,\'2B6521-无钢圈4-\',\'\'); update bra set bra_color = REPLACE(bra_color,\'-1\',\'\'); update bra set bra_color = REPLACE(bra_color,\'5\',\'\'); update bra set bra_size = substr(bra_size,1,3);
这里需要根据自己实际情况来修改。如果数据整理的差不多了,我们可以分析一下数据库的信息。
select \'A罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like \'%A\' union all select \'B罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like \'%B\' union all select \'C罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like \'%C\' union all select \'D罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like \'%D\' union all select \'E罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like \'%E\' union all select \'F罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like \'%F\' union all select \'G罩杯\' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from以上是关于Python动手分析天猫内衣售卖数据,得到你想知道的信息的主要内容,如果未能解决你的问题,请参考以下文章