大家好,希望各位能怀着正直、严谨、专业的心态观看这篇文章。ヾ(๑╹◡╹)ノ"

  接下来我们尝试用 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&currentPage=' + 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", "", "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", "", "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&currentPage=' + 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,'','');
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 bra where bra_size like '%G'
union all select 'H罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%H'
order by 销量 desc;

  

  (想知道是哪6位小姐姐买的 G       (~ ̄▽ ̄)~ )

数据可视化

  数据的展示,我用了是 mycharts 模块,如果不了解的可以去 学习一下  http://pyecharts.org/#/zh-cn/prepare

  这里我就不细说了,直接贴代码看

# encoding: utf-8
# author zww from pyecharts import Pie
from Include.commons.common import Common if __name__ == '__main__':
common = Common()
results = common.queryData("""select count(*) from bra where bra_size like '%A'
union all select count(*) from bra where bra_size like '%B'
union all select count(*) from bra where bra_size like '%C'
union all select count(*) from bra where bra_size like '%D'
union all select count(*) from bra where bra_size like '%E'
union all select count(*) from bra where bra_size like '%F'
union all select count(*) from bra where bra_size like '%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("""select count(*) from bra where bra_color like '%肤%'
union all select count(*) from bra where bra_color like '%灰%'
union all select count(*) from bra where bra_color like '%黑%'
union all select count(*) from bra where bra_color like '%蓝%'
union all select count(*) from bra where bra_color like '%粉%'
union all select count(*) from bra where bra_color like '%红%'
union all select count(*) from bra where bra_color like '%紫%'
union all select count(*) from bra where bra_color like '%绿%'
union all select count(*) from bra where bra_color like '%白%'
union all select count(*) from bra where bra_color like '%褐%'
union all select count(*) from bra where bra_color like '%黄%' """) # 获取每个罩杯数量
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')

  这一章就到这里了,该知道的你也知道了,不该知道的你也知道了。

  代码全部存放在 GitHub 上 https://github.com/zwwjava/python_capture

【Python】动手分析天猫内衣售卖数据,得到你想知道的信息的更多相关文章

  1. 为了给女朋友买件心怡内衣,我用Python爬虫了天猫内衣售卖数据

    真爱,请置顶或星标 大家好,希望各位能怀着正直.严谨.专业的心态观看这篇文章.ヾ(๑╹◡╹)ノ" 接下来我们尝试用 Python 抓取天猫内衣销售数据,并分析得到中国女性普遍的罩杯数据.最受 ...

  2. Python股票分析系列——基础股票数据操作(一).p3

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第3部分.在本教程中,我们将使用我们的股票数据进一步分解一些基本的数据操作和可视化.我们将要使用 ...

  3. python爬虫——分析天猫iphonX的销售数据

    01.引言 这篇文章是我最近刚做的一个项目,会带领大家使用多种技术实现一个非常有趣的项目,该项目是关于苹果机(iphoneX)的销售数据分析,是网络爬虫和数据分析的综合应用项目.本项目会分别从天猫和京 ...

  4. Python股票分析系列——基础股票数据操作(二).p4

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第4部分.在本教程中,我们将基于Adj Close列创建烛台/ OHLC图,这将允许我介绍重新采 ...

  5. 飘逸的python - 用urlparse从url中抽离出想要的信息

    最近有个需求,要检测配置中的那些url的域名是否都正常,即是否都能ping通. 不过配置中url格式是这样的 http://www.something.com:1234/ . 要ping的是www.s ...

  6. Python股票分析系列——数据整理和绘制.p2

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第2部分. 在本教程中,我们将利用我们的股票数据进一步分解一些基本的数据操作和可视化. 我们将要 ...

  7. python 爬取天猫美的评论数据

    笔者最近迷上了数据挖掘和机器学习,要做数据分析首先得有数据才行.对于我等平民来说,最廉价的获取数据的方法,应该是用爬虫在网络上爬取数据了.本文记录一下笔者爬取天猫某商品的全过程,淘宝上面的店铺也是类似 ...

  8. python量化分析系列之---5行代码实现1秒内获取一次所有股票的实时分笔数据

    python量化分析系列之---5行代码实现1秒内获取一次所有股票的实时分笔数据 最近工作太忙了,有一个星期没有更新文章了,本来这一期打算分享一些对龙虎榜数据的分析结果的,现在还没有把数据内的价值很好 ...

  9. Python股票分析系列——系列介绍和获取股票数据.p1

    本系列转载自youtuber sentdex博主的教程视频内容 https://www.youtube.com/watch?v=19yyasfGLhk&index=4&list=PLQ ...

随机推荐

  1. HDU 1722 Cake (数论 gcd)(Java版)

    Big Number 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1722 ——每天在线,欢迎留言谈论. 题目大意: 给你两个数 n1,n2 . 然后 ...

  2. ASP.NET Core 应用发布与部署指南

    一.前言 本篇主要包含哪些内容? 将项目发布到本地目录 将项目传输到服务器并配置启动&开机自动启动 将Nginx作为访问入口,配置反向代理 本篇环境信息 开发环境: 用途 工具&版本 ...

  3. ERP项目应该由谁来主导?

    前段时间在朋友圈看到了别人分享的公众号,主要是谈ERP项目应该由谁来主导的问题.文章的观点认为应该由哪个部门主导ERP的判断标准如下: 1.应该由一个期望上进的部门主导ERP项目: 2.应该由一个有话 ...

  4. ERP口碑后付关于如何设置后厨小票打印时间的问题解决方法

    1. 2.

  5. Spark性能优化【OOM】

    一.异常情况 Spark on yarn模式下,当yarn为client的模式时没有OOM而cluster模式下出现OOM 二.异常分析 由于client模型没有出现OOM而cluster模式出现OO ...

  6. Oracle笔记----oracle数字类型number自增

    创建序列 create sequence seq_student start increment maxvalue nominvalue nocycle nocache; 创建触发器 create o ...

  7. Django之--模板加载图片

    在使用Django加载图片时遇到了一些问题,在模板html文件中无论使用绝对路径还是当前相对路径都无法找到图片,一直报403和404的错误,后来结合官网和网上的其他资料总算是成功了,这里记下来. 参考 ...

  8. SQL Server 日常维护经典应用

    SQL Server日常维护常用的一些脚本整理. 1.sql server开启clr权限: GO RECONFIGURE GO ALTER DATABASE HWMESTC SET TRUSTWORT ...

  9. emacs 利用 auto-complete 自动补齐

    emacs 利用 auto-complete 自动补齐 1,首先导入melpa,在文件~/.emacs中添加下面代码 (require 'package) (package-initialize) ( ...

  10. 如何设置可以避免php代码中的中文在浏览器中成为乱码?

    其实很简单,只需要在代码开始的前面加上一条这样的语句就行: //这里面我的浏览器中的字符编码格式为utf-8,所以这里我设置为utf-8,如果你的浏览器中的默认编码不是这个,请选择浏览器默认的编码格式 ...