中国情人节——七夕节(别称:乞巧节、七巧节、七姐诞、道德腊,英文名:Double Seventh Festival)被赋予了“牛郎织女”的美丽爱情传说,使其成为了象征爱情的节日,从而被认为是中国最具浪漫色彩的传统节日,日期是每年农历七月初七,有拜月祈福、拜织女、吃巧果、乞求姻缘等习俗。
随着七夕的临近,很多小伙伴都开始筹备送女朋友/男朋友的礼物了,礼物作为一种传达情感的媒介,表达了对于女朋友/男朋友的祝福和心意,但同时对于要送什么礼物,对于很多小伙伴来讲倒是选择困难,本文利用 Python 爬取某宝商品页面,为小伙伴们分析销量较高的礼物清单,以供大家参考。
根据不同关键字,爬取“某宝”
获取商品信息(以“七夕礼物”
、“七夕礼物送男友”
、“七夕礼物送女友”
等为例),根据所获取数据分析得到七夕礼物清单,并通过词云
可视化的方式展示不同礼物的频率比重对比。
爬虫少不了网址,因此首先观察网址的构成,在输入关键字“七夕礼物”
进行搜索时,发现网址中 q
的参数值即为所键入的关键字“七夕礼物”
,如下图所示:
q_value = "七夕礼物" url = "https://s.taobao.com/search?q={}imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210802&ie=utf8&bcoffset=5&p4ppushleft=2%2C48&ntoffset=5&s=44".format(q_value)
虽然也可以直接复制网址,但是这种方法的弊端在于,每次想要爬取其他类别的商品时,都需要重新打开网页复制网址;而利用 q_value
变量构造网址,当需要获取其他品类商品时仅需要修改 q_value
变量,例如要爬取关键字“七夕礼物送男友”
,只需要做如下修改:
q_value = "七夕礼物送男友"
使用浏览器“开发者工具”
,观察网页结构,可以看出商品的信息都是在 中,
因此,首先使用 requests 库请求网页内容,需要注意的是,在请求页面信息时,需要构造请求头中的 cookie 和 user-agent 信息,否则并不会得到有效响应,获取 cookie 和 user-agent 信息需要在浏览器“开发者工具”中的网络标签下单击当前请求页面(如果网络标签下没有当前请求的页面,需要刷新后才可以显示),在标头选项卡中找到请求标头的 cookie 和 user-agent 值并复制,按照以下方式构造请求头:
headers = { # 将user-agent值,替换为刚刚复制的user-agent值 "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62", # 将cookie值,替换为刚刚复制的cookie值 "cookie":"...JSESSIONID=4990DB1E8783DF90266F5159209F8E3A" }
下图显示了获取 cookie
值的示例(获取 user-agent
值的方式类似,只需在标头
选项卡中找到
请求标头
的 user-agent
值):
得到响应网页后,需要使用 BeautifulSoup4
与正则表达库 re
解析网页,获取商品信息:
import re import requests from bs4 import BeautifulSoup # 请求网页 response = requests.get(url,headers =headers) response.raise_for_status() response.encoding = 'utf-8' # 解析网页 soup = BeautifulSoup(response.text, 'html.parser') results = soup.find_all('script') information = str(results[7]) # 获取商品标题 raw_title = re.findall(r'"raw_title":"(.*?)"', information) # 获取商品价格 view_price = re.findall(r'"view_price":"(.*?)"', information) # 获取购买人数 view_sales = re.findall(r'"view_sales":"(.*?)"', information) # 获取发货地址 item_loc = re.findall(r'"item_loc":"(.*?)"', information) # 获取店铺名 nick = re.findall(r'"nick":"(.*?)"', information) # 获取详情页地址 detail_url = re.findall(r'"detail_url":"(.*?)"', information) # 获取封面图片地址 pic_url = re.findall(r'"pic_url":"(.*?)"', information)
需要注意的是,由于一开始使用 utf-8
方式解码网页,并不能解码 Unicode
,因此打印详情页地址和封面图片地址会看到解码不正确的情况:
print(detail_url[4]) # //detail.tmall.com/item.htm?id\u003d628777347316\u0026ns\u003d1\u0026abbucket\u003d17
因此需要使用以下方式进行正确解码:
decodeunichars = detail_url[4].encode('utf-8').decode('unicode-escape') print(decodeunichars) # //detail.tmall.com/item.htm?id=628777347316&ns=1&abbucket=17
为了将数据写入 csv 文件中,首先创建 csv 文件并写入标头:
#创建存储csv文件存储数据 file = open('gift.csv', "w", encoding="utf-8-sig",newline='') csv_head = csv.writer(file) #表头 header = ['raw_title','view_price','view_sales','salary','item_loc','nick','detail_url','pic_url'] csv_head.writerow(header) file.close()
然后,由于英文逗号(",
")表示单元格的切换,因此需要将获取的数据经过预处理:
def precess(item): return item.replace(',', ' ')
最后将预处理数据后,将数据写入 csv 文件中:
for i in range(len(raw_title)): with open('gift.csv', 'a+', encoding='utf-8-sig') as f: f.write(precess(raw_title[i]) + ',' + precess(view_price[i]) + ',' + precess(view_sales[i]) + ',' + precess(item_loc[i]) +',' + precess(nick[i]) + ',' + precess(detail_url[i]) + ',' + precess(pic_url[i]) + '\n')
通过查看第2页以及第3页,网址:
https://s.taobao.com/search?q=七夕礼物imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210805&ie=utf8&bcoffset=1&ntoffset=1&p4ppushleft=2%2C48&s=44 https://s.taobao.com/search?q=七夕礼物&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210805&ie=utf8&bcoffset=1&ntoffset=1&p4ppushleft=2%2C48&s=88
可以看到,网址的不同仅在于第二页的 s
的参数值为 44
,而第三页 s
的参数值为 88
,结合可以每页有 44
个商品,因此可以使用以下方式构造爬取20页商品:
url_pattern = "https://s.taobao.com/search?q={}&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210805&ie=utf8&bcoffset=1&ntoffset=1&p4ppushleft=2%2C48&s={}" for i in range(20): url = url_pattern.format(q, i*44)
import re import requests import time from bs4 import BeautifulSoup import csv import os q = "七夕礼物" url_pattern = "https://s.taobao.com/search?q={}&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210805&ie=utf8&bcoffset=2&ntoffset=2&p4ppushleft=2%2C48&s={}" headers = { "user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.62", # cookie值替换为使用上述方式获取的cookie值 "cookie":"..." } def analysis(item,results): pattern = re.compile(item, re.I|re.M) result_list = pattern.findall(results) return result_list def analysis_url(item, results): pattern = re.compile(item, re.I|re.M) result_list = pattern.findall(results) for i in range(len(result_list)): result_list[i] = result_list[i].encode('utf-8').decode('unicode-escape') return result_list def precess(item): return item.replace(',', ' ') # 创建csv文件 if not os.path.exists("gift.csv"): file = open('gift.csv', "w", encoding="utf-8-sig",newline='') csv_head = csv.writer(file) #表头 header = ['raw_title','view_price','view_sales','salary','item_loc','nick','detail_url','pic_url'] csv_head.writerow(header) file.close() for i in range(100): #增加时延防止反爬虫 time.sleep(25) url = url_pattern.format(q, i*44) response = requests.get(url=url, headers=headers) #声明网页编码方式,需要根据具体网页响应情况 response.encoding = 'utf-8' response.raise_for_status() soup = BeautifulSoup(response.text, 'html.parser') results = soup.find_all('script') information = str(results[7]) # 获取所有的商品信息,由于有些购买人数为空白,导致返回空值,因此添加异常处理 all_goods = analysis(r'"raw_title":"(.*?)"shopLink"', information) for good in all_goods: # 获取商品标题 raw_title = analysis(r'(.*?)","pic_url"', good) if not raw_title: raw_title.append('未命名') # 获取商品价格 view_price = analysis(r'"view_price":"(.*?)"', good) if not view_price: view_price.append('0.00') # 获取购买人数,由于有些购买人数为空白,导致返回空值,因此添加异常处理 view_sales = analysis(r'"view_sales":"(.*?)"', good) #print(view_sales) if not view_sales: view_sales.append('0人付款') # 获取发货地址 item_loc = analysis(r'"item_loc":"(.*?)"', good) if not item_loc: item_loc.append('未知地址') # 获取店铺名 nick = analysis(r'"nick":"(.*?)"', good) if not nick: nick.append('未知店铺') # 获取详情页地址 detail_url = analysis_url(r'"detail_url":"(.*?)"', good) if not detail_url: detail_url.append('无详情页') # 获取封面图片地址 pic_url = analysis_url(r'"pic_url":"(.*?)"', good) if not pic_url: pic_url.append('无封面') with open('gift.csv', 'a+', encoding='utf-8-sig') as f: f.write(precess(raw_title[0]) + ',' + precess(view_price[0]) + ',' + precess(view_sales[0]) + ',' + precess(item_loc[0]) +',' + precess(nick[0]) + ',' + precess(detail_url[0]) + ',' + precess(pic_url[0]) + '\n')
可能很多朋友(并非)是为了学习技术才点击进来的,完(shun)全(bian)是求知若渴的想知道要送男朋友/女朋友什么礼物,别着急,大家最关注的部分来了。
接下来使用词云可视化分析,分别考虑包含销量与不包含销量两种情况。
不考虑销量时:
from os import path from PIL import Image import matplotlib.pyplot as plt import jieba from wordcloud import WordCloud, STOPWORDS import pandas as pd import matplotlib.ticker as ticker import numpy as np import math import re df = pd.read_csv('gift.csv', encoding='utf-8-sig',usecols=['raw_title','view_price','view_sales','salary','item_loc','nick','detail_url','pic_url']) raw_title_list = df['raw_title'].values raw_title = ','.join(raw_title_list) with open('text.txt','a+') as f: f.writelines(raw_title) ###当前文件路径 d = path.dirname(__file__) # Read the whole text. file = open(path.join(d, 'text.txt')).read() ##进行分词 #停用词,去除修饰性的词 stopwords = ["七夕","七夕情人节","情人节","男友","女友","男生","女生","女朋友","男朋友","礼物","生日礼物","创意","实用","朋友","男士","老婆","老公","直营","闺蜜","结婚","送给"] text_split = jieba.cut(file) # 未去掉停用词的分词结果 list类型 #去掉停用词的分词结果 list类型 text_split_no = [] for word in text_split: if word not in stopwords: text_split_no.append(word) #print(text_split_no) text =' '.join(text_split_no) #背景图片 picture_mask = np.array(Image.open(path.join(d, "beijing.jpg"))) stopwords = set(STOPWORDS) stopwords.add("said") wc = WordCloud( #设置字体,指定字体路径 font_path=r'C:\Windows\Fonts\simsun.ttc', background_color="white", max_words=4000, mask=picture_mask, stopwords=stopwords) # 生成词云 wc.generate(text) # 存储图片 wc.to_file(path.join(d, "result.jpg"))
接下来考虑销量:
def get_sales(item): tmp = item[:-3] if '万' in tmp: tmp = tmp.replace('万','0000').replace('.','') if '+' in tmp: tmp = tmp.replace('+','') tmp = int(tmp) tmp = tmp / 100.0 if tmp <= 0: tmp = 0 tmp = round(tmp) return tmp raw_title_list = df['raw_title'].values view_sales_list = df['view_sales'].values for i in range(len(raw_title_list)): for j in range(get_sales(view_sales_list[i])): with open('text.txt','a+') as f: f.writelines(raw_title_list[i]+',') # 其余代码不再赘述 """ ... """
可以看到差别还是较为明显的,最后将分词结果,进行排序,手动去除无效词后,归类整理出排名前15的礼物清单:
gift = {} for i in text_split_no: if gift.get(i,0): gift[i] += 1 else: gift[i] = 1 sorted_gift = sorted(gift.items(), key=lambda x:x[1],reverse=True)
❤️礼物清单❤️
玩偶/公仔/毛绒玩具/抱抱熊 糖果 项链 巧克力 相册/纪念册 零食/小吃 手链/手镯/镯子 玫瑰 吊坠 小夜灯 音乐盒/八音盒 戒指/对戒 口红 手绳/红绳 耳钉
以同样的方法,将关键字改为“女友+礼物”
,可以得到❤️送女朋友礼物清单❤️:
小夜灯 相册/纪念册/相框 刻字类礼物 八音盒 项链/手链/手镯/镯子 口红 手表 包 水晶鞋 玩偶
最后,将关键字改为“男友+礼物”
,可以得到❤️送男朋友礼物清单❤️:
可乐刻字 相册/纪念册/相框 花束 手办 摆件 钥匙扣 木刻画 情书 刺绣 篮球
可以看出,送女朋友和男朋友的礼物还是有一些差别的。 当然,和主观感觉上有很些差异的原因可能在于,很多商品的标题太过创意,完全不包含商品。
当然,本文仅做分析之用,结果也仅供参考,如果清单里没有令你心仪的礼物,也可以选择红包或者清空购物车的方式。无论送女朋友/男朋友什么礼物,传达自己的❤️心意❤️最重要了。
:#私信小编06#即可免费领取!
记得点点关注哦!!!爱你别走!!!