●前言
? ? ? ? 一年一度的端午节又到了,甜咸粽子之争也拉开了帷幕,它价格高昂,它味道鲜美,然而,默默无名的它却备受广大民众喜爱!好家伙,一看就是老qq看点了
,那咱们能做些什么呢,当然是选择盘它啊,今天咱们就看看京东上粽子的行情。
●爬取京东数据
? ? ? ? 发起请求-获取响应内容-解析内容-保存内容,还是熟悉的老四步曲。
1. 发起请求,获取响应内容
? ? ? ? 浏览器打开京东(https://www.jd.com),搜索“粽子”,点击下一页
? ? ? ? 可以看到url的构造是https://search.jd.com/Search?keyword=%E7%B2%BD%E5%AD%90&qrst=1&stock=1&page=3&s=53&click=0,提取主要的参数keyword(搜索词)和page(页码),那么主要的url为:https://search.jd.com/Search?keyword=%E7%B2%BD%E5%AD%90&page=3,keyword是粽子,固定的,变化的参数是page,我们先请求下:
import requests
url?=?'https://search.jd.com/Search?keyword=%E7%B2%BD%E5%AD%90&page=3'
headers?=?{'Host':?'search.jd.com',
???????????'User-Agent':?'Mozilla/5.0?(Windows?NT?6.1;?Win64;?x64;?rv:77.0)?Gecko/20100101?Firefox/77.0',
????????????'Accept-Language':?'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2'}
r?=?requests.request('GET',url=url,headers=headers)
print(r.text)
2.解析数据
? ? ? ? 解析数据当然要用到最强大也是最方便的BeautifulSoup了,先看我们需要采集的数据在哪:
? ? ? ? 咱们来采集主要的四个数据,图片,价格,描述,店铺名称,打开调试器,分析页面构造:
? ? ? ? 我们可以看到商品的数据在一个id为J_goodsList的div里,内层的每一个li标签包含了一个商品的全部信息,然后我们就可以开始解析了:
from bs4 import BeautifulSoup
import requests
url = 'https://search.jd.com/Search?keyword=%E7%B2%BD%E5%AD%90&page=' + str(page)
headers?=?{'Host':?'search.jd.com',
???????????'User-Agent':?'Mozilla/5.0?(Windows?NT?6.1;?Win64;?x64;?rv:77.0)?Gecko/20100101?Firefox/77.0',
???????????'Accept-Language':?'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2'}
r?=?requests.request('GET',url=url,headers=headers)
div = BeautifulSoup(r.text,'lxml').find_all('div',id='J_goodsList')[0] # 拿到主要div
items = BeautifulSoup(str(div),'lxml').find_all('li',class_='gl-item') # 商品列表
print(items)
print(len(items))
? ? ? ? 打印一下,发现只采集到了30个li标签,然而通过网页我们可以发现每页是有60个商品的,也就是说这只拿到了前30个数据,可以猜想京东是通过ajax加载数据的,打开调试器,查看网络,鼠标往下滑:
? ? ? ? 可以看到出现了后边30条数据的请求,分析链接构造,page参数为偶数,之前的30条数据page参数为奇数,也就是第一页的数据就是page为1和2的数据,商品总共是100页,也就是page从1-200,链接搞定,我们继续解析详细数据:
items = BeautifulSoup(str(div),'lxml').find_all('li',class_='gl-item'
result = []
for?item?in?items:
????desc?=?BeautifulSoup(str(item),?'lxml').find('div',?class_='p-name?p-name-type-2').find_all('a')[0].text.replace('\n','') # 去除换行符
????img?=?BeautifulSoup(str(item),?'lxml').find('div',?class_='p-img').find_all('img')[0].get('src')
????shop?=?BeautifulSoup(str(item),?'lxml').find('div',?class_='p-shop').find_all('a')[0].text
????price?=?BeautifulSoup(str(item),'lxml').find_all('div',class_='p-price')[0].text.replace('\n','').split('¥')[1]??#?去除换行符和格式数据
result.append([shop,img,price,desc])
? ? ? ? 完整获取数据:
result?=?[]
for?page?in?range(1,?201):
???? print('正在采集第'?+?str(page)?+?'页')
?????? url?=?'https://search.jd.com/Search?keyword=%E7%B2%BD%E5%AD%90&page='?+?str(page)
headers = {'Host': 'search.jd.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2'}
r = requests.request('GET',url=url,headers=headers)
print(r.text)
try:
div = BeautifulSoup(r.text,'lxml').find_all('div',id='J_goodsList')[0]
items = BeautifulSoup(str(div),'lxml').find_all('li',class_='gl-item')
for item in items:
try:
desc = BeautifulSoup(str(item), 'lxml').find('div', class_='p-name p-name-type-2').find_all('a')[0].text.replace('\n','')
except Exception as e:
desc = '无'
try:
img = BeautifulSoup(str(item), 'lxml').find('div', class_='p-img').find_all('img')[0].get('src')
except Exception as e:
img = '无'
try:
shop = BeautifulSoup(str(item), 'lxml').find('div', class_='p-shop').find_all('a')[0].text
except Exception as e:
shop = '无'
try:
price = BeautifulSoup(str(item),'lxml').find_all('div',class_='p-price')[0].text.replace('\n','').split('¥')[1]
except Exception as e:
price = '无'
result.append([shop,img,price,desc])
except Exception as e:
pass
3.保存数据
import xlwt
workExcel = xlwt.Workbook(encoding='utf-8')
workSheet?=?workExcel.add_sheet('data')
workSheet.write(0,?0,?'店铺')
workSheet.write(0,?1,?'图片')
workSheet.write(0,?2,?'价格')
workSheet.write(0,?3,?'描述')
i?=?0
for?data?in?result:
????i?+=?1
????workSheet.write(i,?0,?data[0])
????workSheet.write(i,?1,?data[1])
????workSheet.write(i,?2,?data[2])
????workSheet.write(i,?3,?data[3])
workExcel.save(r'C:\Users\Administrator\Desktop\data.xls')
●分析数据
? ? ? ? 先看下粽子价格的top5:
import pandas as pd
wb = pd.read_excel('data.xls', sheet_name='data')
wb?=?pd.DataFrame(wb.rename(columns?=?{u"店铺":'shop',u"价格":'price',u"描述":'desc'}))
df?=?wb[['shop',?'desc',?'price']]
df1 = df.sort_values(by="price", axis=0, ascending=False)
print(df1.iloc[:5,?:])
? ? ? ? 好家伙,2265.5元,看来是吃不起了......还是去买小区门口5块钱3个的吧
? ? ? 再来参照淘宝的价格区间给粽子价格划分下:
? ? ? ? ? ? 贫民窟:小于22元的(底层人民专属)
? ? ? ? ? ? 平民区:大于22元小于115元的
????????? ? 小康家庭:大于115元小于633元的
????????? ? 富人区:大于633元的
wb = pd.read_excel('data.xls', sheet_name='data')
wb?=?pd.DataFrame(wb.rename(columns?=?{u"店铺":'shop',u"价格":'price',u"描述":'desc'}))
df?=?wb[['shop',?'desc',?'price']]
#?统计出现次数,方便绘图
primary?=?df.query('price?<=?22').shop.count()
intermediate?=?df.query('price?>?22?and?price?<=?115').shop.count()
senior?=?df.query('price?>?115?and?price?<=?633').shop.count()
rich?=?df.query('price?>?633').shop.count()
print(primary,?intermediate,?senior,?rich)
? ? ? 绘制饼图:
# 显示中文问题
plt.rcParams['font.sans-serif']?=?['SimHei']
label_list?=?['22元以下',?'22-115元',?'115-633元',?'633元以上']
size?=?[primary,?intermediate,?senior,?rich]
#?各部分颜色
color?=?['red',?'green',?'blue',?'yellow']
#?各部分突出值
explode?=?[0,?0,?0,?0.1]
patches,?l_text,?p_text?=?plt.pie(size,?explode=explode,?colors=color,?labels=label_list,?labeldistance=1.1,?autopct="%1.1f%%",?shadow=False,?startangle=90,?pctdistance=0.6)
#?设置横轴和纵轴大小相等,这样饼才是圆的
plt.axis('equal')
plt.legend(loc='upper?left')
plt.show()
? ? ? ? 看来平民还是占多数的嘛!