用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&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 信息,当然你可以想办法确认一下。

如果需要Python方面的学习资料可以点击这个链接获取入门资料

现在就写个 获取商品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&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,'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 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')

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

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

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,312评论 0 10
  • pyspark.sql模块 模块上下文 Spark SQL和DataFrames的重要类: pyspark.sql...
    mpro阅读 9,448评论 0 13
  • 一天和一位闺蜜聊天,她说“真羡慕,每年都可以去远方旅行。”我笑笑说,亲爱的,你看到的只是事物的表像,内里的...
    喜风SanPedroSula阅读 493评论 0 2
  • 雨 落在田野里 谁在笑呢 雨 落在花...
    慕云风阅读 88评论 0 0