大家好,我是一个严谨的老实人,现在我决定手把手教你怎么用 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 

  1. # 获取商品评论数据
  2. def getCommentDetail(itemId,currentPage):
  3. url = 'https://rate.tmall.com/list_detail_rate.htm?itemId=' + str(
  4. itemId) + '&sellerId=2451699564&order=3&currentPage=' + str(currentPage) + '&append=0callback=jsonp336'
  5. # itemId 产品id ; sellerId 店铺id 字段必须有值,但随意值就行
  6. html = common.getUrlContent(url) # 获取网页信息
  7. # 删掉返回的多余信息
  8. html = html.replace('jsonp128(','') #需要确定是不是 jsonp128
  9. html = html.replace(')','')
  10. html = html.replace('false','"false"')
  11. html = html.replace('true','"true"')
  12. # 将string 转换为字典对象
  13. tmalljson = json.loads(html)
  14. return tmalljson

  这里需要注意的是 jsonp128 这个值需要你自己看一下,你那边跟我这个应该是不同的。

  还有几十 common 这我自己封装的一个工具类,主要就是上一篇博客里写的一些功能,想 requests pymysql 模块的功能。在文章最后我会贴出来。

  在上面的方法里有两个变量,itemId 和 currentPage 这两个值我们动态来控制,所以我们需要获得 一批 商品id号评论的最大页数 用来遍历。

  写个获取商品评论最大页数的方法 getLastPage 

 

  1. # 获取商品评论最大页数
  2. def getLastPage(itemId):
  3. tmalljson = getCommentDetail(itemId,1)
  4. return tmalljson['rateDetail']['paginator']['lastPage'] #最大页数

 

 

 

  那现在怎么获取 产品的id 列表呢? 我们可以在天猫中搜索商品关键字 用开发者模式观察

  

  

  这里观察一下这个页面的元素分布,很容易就发现了 商品的id 信息,当然你可以想办法确认一下。

  

    现在就写个 获取商品id 的方法 getProductIdList

  1. # 获取商品id
  2. def getProductIdList():
  3. url = 'https://list.tmall.com/search_product.htm?q=内衣' # q参数 是查询的关键字
  4. html = common.getUrlContent(url) # 获取网页信息
  5. soup = BeautifulSoup(html,'html.parser')
  6. idList = []
  7. # 用Beautiful Soup提取商品页面中所有的商品ID
  8. productList = soup.find_all('div', {'class': 'product'})
  9. for product in productList:
  10. idList.append(product['data-id'])
  11. return idList

   现在所有的基本要求都有了,是时候把他们组合起来。

   在 main 方法中写剩下的组装部分 

  1. if __name__ == '__main__':
  2. productIdList = getProductIdList() #获取商品id
  3. initial = 0
  4. while initial < len(productIdList) - 30: # 总共有60个商品,我只取了前30个
  5. try:
  6. itemId = productIdList[initial]
  7. print('----------', itemId, '------------')
  8. maxPage = getLastPage(itemId) #获取商品评论最大页数
  9. num = 1
  10. while num <= maxPage and num < 20: #每个商品的评论我最多取20 页,每页有20条评论,也就是每个商品最多只取 400 个评论
  11. try:
  12. # 抓取某个商品的某页评论数据
  13. tmalljson = getCommentDetail(itemId, num)
  14. rateList = tmalljson['rateDetail']['rateList']
  15. commentList = []
  16. n = 0
  17. while (n < len(rateList)):
  18. comment = []
  19. # 商品描述
  20. colorSize = rateList[n]['auctionSku']
  21. m = re.split('[:;]', colorSize)
  22. rateContent = rateList[n]['rateContent']
  23. dtime = rateList[n]['rateDate']
  24. comment.append(m[1])
  25. comment.append(m[3])
  26. comment.append('天猫')
  27. comment.append(rateContent)
  28. comment.append(dtime)
  29. commentList.append(comment)
  30. n += 1
  31. print(num)
  32. sql = "insert into bras(bra_id, bra_color, bra_size, resource, comment, comment_time) value(null, %s, %s, %s, %s, %s)"
  33. common.patchInsertData(sql, commentList) # mysql操作的批量插入
  34. num += 1
  35. except Exception as e:
  36. num += 1
  37. print(e)
  38. continue
  39. initial += 1
  40. except Exception as e:
  41. print(e)

  所有的代码就这样完成了,我现在把 common.py 的代码,还有 tmallbra.py 的代码都贴出来

  1. # -*- coding:utf-8 -*-
  2. # Author: zww
  3. import requests
  4. import time
  5. import random
  6. import socket
  7. import http.client
  8. import pymysql
  9. import csv
  10. # 封装requests
  11. class Common(object):
  12. def getUrlContent(self, url, data=None):
  13. header = {
  14. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
  15. 'Accept-Encoding': 'gzip, deflate, br',
  16. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
  17. '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",
  18. 'cache-control': 'max-age=0'
  19. } # request 的请求头
  20. timeout = random.choice(range(80, 180))
  21. while True:
  22. try:
  23. rep = requests.get(url, headers=header, timeout=timeout) # 请求url地址,获得返回 response 信息
  24. # rep.encoding = 'utf-8'
  25. break
  26. except socket.timeout as e: # 以下都是异常处理
  27. print('3:', e)
  28. time.sleep(random.choice(range(8, 15)))
  29. except socket.error as e:
  30. print('4:', e)
  31. time.sleep(random.choice(range(20, 60)))
  32. except http.client.BadStatusLine as e:
  33. print('5:', e)
  34. time.sleep(random.choice(range(30, 80)))
  35. except http.client.IncompleteRead as e:
  36. print('6:', e)
  37. time.sleep(random.choice(range(5, 15)))
  38. print('request success')
  39. return rep.text # 返回的 Html 全文
  40.  
  41. def writeData(self, data, url):
  42. with open(url, 'a', errors='ignore', newline='') as f:
  43. f_csv = csv.writer(f)
  44. f_csv.writerows(data)
  45. print('write_csv success')
  46. def queryData(self, sql):
  47. db = pymysql.connect("localhost", "zww", "960128", "test")
  48. cursor = db.cursor()
  49. results = []
  50. try:
  51. cursor.execute(sql) #执行查询语句
  52. results = cursor.fetchall()
  53. except Exception as e:
  54. print('查询时发生异常' + e)
  55. # 如果发生错误则回滚
  56. db.rollback()
  57. # 关闭数据库连接
  58. db.close()
  59. return results
  60. print('insert data success')
  61. def insertData(self, sql):
  62. # 打开数据库连接
  63. db = pymysql.connect("localhost", "zww", "000000", "zwwdb")
  64. # 使用 cursor() 方法创建一个游标对象 cursor
  65. cursor = db.cursor()
  66. try:
  67. # sql = "INSERT INTO WEATHER(w_id, w_date, w_detail, w_temperature) VALUES (null, '%s','%s','%s')" % (data[0], data[1], data[2])
  68. cursor.execute(sql) #单条数据写入
  69. # 提交到数据库执行
  70. db.commit()
  71. except Exception as e:
  72. print('插入时发生异常' + e)
  73. # 如果发生错误则回滚
  74. db.rollback()
  75. # 关闭数据库连接
  76. db.close()
  77. print('insert data success')
  78. def patchInsertData(self, sql, datas):
  79. # 打开数据库连接
  80. db = pymysql.connect("localhost", "zww", "960128", "test")
  81. # 使用 cursor() 方法创建一个游标对象 cursor
  82. cursor = db.cursor()
  83. try:
  84. # 批量插入数据
  85. # cursor.executemany('insert into WEATHER(w_id, w_date, w_detail, w_temperature_low, w_temperature_high) value(null, %s,%s,%s,%s)',datas)
  86. cursor.executemany(sql, datas)
  87. # 提交到数据库执行
  88. db.commit()
  89. except Exception as e:
  90. print('插入时发生异常' + e)
  91. # 如果发生错误则回滚
  92. db.rollback()
  93. # 关闭数据库连接
  94. db.close()
  95. print('insert data success')

  上面需要注意,数据库的配置

  1. # -*- coding:utf-8 -*-
  2. # Author: zww
  3.  
  4. from Include.commons.common import Common
  5. from bs4 import BeautifulSoup
  6. import json
  7. import re
  8. import pymysql
  9. common = Common()
  10. # 获取商品id
  11. def getProductIdList():
  12. url = 'https://list.tmall.com/search_product.htm?q=内衣' # q参数 是查询的关键字,这要改变一下查询值,就可以抓取任意你想知道的数据
  13. html = common.getUrlContent(url) # 获取网页信息
  14. soup = BeautifulSoup(html,'html.parser')
  15. idList = []
  16. # 用Beautiful Soup提取商品页面中所有的商品ID
  17. productList = soup.find_all('div', {'class': 'product'})
  18. for product in productList:
  19. idList.append(product['data-id'])
  20. return idList
  21. # 获取商品评论数据
  22. def getCommentDetail(itemId,currentPage):
  23. url = 'https://rate.tmall.com/list_detail_rate.htm?itemId=' + str(
  24. itemId) + '&sellerId=2451699564&order=3&currentPage=' + str(currentPage) + '&append=0callback=jsonp336'
  25. # itemId 产品id ; sellerId 店铺id 字段必须有值,但随意值就行
  26. html = common.getUrlContent(url) # 获取网页信息
  27. # 删掉返回的多余信息
  28. html = html.replace('jsonp128(','') #需要确定是不是 jsonp128
  29. html = html.replace(')','')
  30. html = html.replace('false','"false"')
  31. html = html.replace('true','"true"')
  32. # 将string 转换为字典对象
  33. tmalljson = json.loads(html)
  34. return tmalljson
  35. # 获取商品评论最大页数
  36. def getLastPage(itemId):
  37. tmalljson = getCommentDetail(itemId,1)
  38. return tmalljson['rateDetail']['paginator']['lastPage'] #最大页数
  39.  
  40. if __name__ == '__main__':
  41. productIdList = getProductIdList() #获取商品id
  42. initial = 0
  43. while initial < len(productIdList) - 30: # 总共有60个商品,我只取了前30个
  44. try:
  45. itemId = productIdList[initial]
  46. print('----------', itemId, '------------')
  47. maxPage = getLastPage(itemId) #获取商品评论最大页数
  48. num = 1
  49. while num <= maxPage and num < 20: #每个商品的评论我最多取20 页,每页有20条评论,也就是每个商品最多只取 400 个评论
  50. try:
  51. # 抓取某个商品的某页评论数据
  52. tmalljson = getCommentDetail(itemId, num)
  53. rateList = tmalljson['rateDetail']['rateList']
  54. commentList = []
  55. n = 0
  56. while (n < len(rateList)):
  57. comment = []
  58. # 商品描述
  59. colorSize = rateList[n]['auctionSku']
  60. m = re.split('[:;]', colorSize)
  61. rateContent = rateList[n]['rateContent']
  62. dtime = rateList[n]['rateDate']
  63. comment.append(m[1])
  64. comment.append(m[3])
  65. comment.append('天猫')
  66. comment.append(rateContent)
  67. comment.append(dtime)
  68. commentList.append(comment)
  69. n += 1
  70. print(num)
  71. sql = "insert into bras(bra_id, bra_color, bra_size, resource, comment, comment_time) value(null, %s, %s, %s, %s, %s)"
  72. common.patchInsertData(sql, commentList) # mysql操作的批量插入
  73. num += 1
  74. except Exception as e:
  75. num += 1
  76. print(e)
  77. continue
  78. initial += 1
  79. except Exception as e:
  80. print(e)

  

  

存储、分析数据

  所有的代码都有了,就差数据库的建立了。我这里用的是 MySql 数据库。

  1. CREATE TABLE `bra` (
  2. `bra_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id' ,
  3. `bra_color` varchar(25) NULL COMMENT '颜色' ,
  4. `bra_size` varchar(25) NULL COMMENT '罩杯' ,
  5. `resource` varchar(25) NULL COMMENT '数据来源' ,
  6. `comment` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '评论' ,
  7. `comment_time` datetime NULL COMMENT '评论时间' ,
  8. PRIMARY KEY (`bra_id`)
  9. ) character set utf8
  10. ;

  这里有两个地方需要注意, comment 评论字段需要设置编码格式为 utf8mb4  ,因为可能有表情文字。还有需要设置为 utf8 编码,不然存不了中文。

  建好了表,就可以完整执行代码了。(这里的执行可能需要点时间,可以做成多线程的方式)。看一下执行完之后,数据库有没有数据。

  

  数据是有了,但是有些我们多余的文字描述,我们可以稍微整理一下。

  1. update bra set bra_color = REPLACE(bra_color,'2B6521-无钢圈4-','');
  2. update bra set bra_color = REPLACE(bra_color,'-1','');
  3. update bra set bra_color = REPLACE(bra_color,'5','');
  4. update bra set bra_size = substr(bra_size,1,3);

  这里需要根据自己实际情况来修改。如果数据整理的差不多了,我们可以分析一下数据库的信息。

  1. select 'A罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%A'
  2. union all select 'B罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%B'
  3. union all select 'C罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%C'
  4. union all select 'D罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%D'
  5. union all select 'E罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%E'
  6. union all select 'F罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%F'
  7. union all select 'G罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%G'
  8. union all select 'H罩杯' as 罩杯, CONCAT(ROUND(COUNT(*)/(select count(*) from bra) * 100, 2) , "%") as 比例, COUNT(*) as 销量 from bra where bra_size like '%H'
  9. order by 销量 desc;

  

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

 

数据可视化

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

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

  1. # encoding: utf-8
  2. # author zww
  3.  
  4. from pyecharts import Pie
  5. from Include.commons.common import Common
  6. if __name__ == '__main__':
  7. common = Common()
  8. results = common.queryData("""select count(*) from bra where bra_size like '%A'
  9. union all select count(*) from bra where bra_size like '%B'
  10. union all select count(*) from bra where bra_size like '%C'
  11. union all select count(*) from bra where bra_size like '%D'
  12. union all select count(*) from bra where bra_size like '%E'
  13. union all select count(*) from bra where bra_size like '%F'
  14. union all select count(*) from bra where bra_size like '%G'""") # 获取每个罩杯数量
  15. attr = ["A罩杯", 'G罩杯', "B罩杯", "C罩杯", "D罩杯", "E罩杯", "F罩杯"]
  16. v1 = [results[0][0], results[6][0], results[1][0], results[2][0], results[3][0], results[4][0], results[5][0]]
  17. pie = Pie("内衣罩杯", width=1300, height=620)
  18. pie.add("", attr, v1, is_label_show=True)
  19. pie.render('size.html')
  20. print('success')
  21. results = common.queryData("""select count(*) from bra where bra_color like '%肤%'
  22. union all select count(*) from bra where bra_color like '%灰%'
  23. union all select count(*) from bra where bra_color like '%黑%'
  24. union all select count(*) from bra where bra_color like '%蓝%'
  25. union all select count(*) from bra where bra_color like '%粉%'
  26. union all select count(*) from bra where bra_color like '%红%'
  27. union all select count(*) from bra where bra_color like '%紫%'
  28. union all select count(*) from bra where bra_color like '%绿%'
  29. union all select count(*) from bra where bra_color like '%白%'
  30. union all select count(*) from bra where bra_color like '%褐%'
  31. union all select count(*) from bra where bra_color like '%黄%' """) # 获取每个罩杯数量
  32. attr = ["肤色", '灰色', "黑色", "蓝色", "粉色", "红色", "紫色", '绿色', "白色", "褐色", "黄色"]
  33. 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]]
  34. pieColor = Pie("内衣颜色", width=1300, height=620)
  35. pieColor.add("", attr, v1, is_label_show=True)
  36. pieColor.render('color.html')
  37. print('success')

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

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

 

版权声明:本文为zhaww原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhaww/p/9636383.html