目录
一、环境
二、简介
三、京东网页分析
1.获取商品信息入口--商品列表链接获取
2.获取商品信息入口--商品详情链接获取
3.商品详情获取
4.商品评论获取
四、代码实现
五、运行结果
六、结语
win10 + python 3.7 + pycharm64 + selenium
抓取京东商品页面信息:商品名称,售价,店铺,分类,订单信息,好评率,评论等----这里以'每周一花'为例
采集思路如下:
此次主要采集以下字段数据:
爬取商品信息首先得有商品信息入口,这里直接从京东的分类中进入商品列表页面,没有对搜索关键词进行处理,仅处理了商品的页码。
翻看网页,发现共有16页,想用点击下一页(click()的方式)实现翻页,获取每一个商品列表链接。
这里有个坑需要注意:class='pn-next disabled',在第16页依旧可以找到,如果以此设置click() 在第16页后依旧会执行,抓取到别的类目的链接。
踩坑后用设置url的方式获取商品列表链接。
翻看多页发现url里的页码关键字:
page:页码(奇数递增)
s:sku数(page*60+1)
构建请求url:
url='https://search.jd.com/Search?keyword=%E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1&page='+str(i*2+1)+'&s='+str(i*60+1)+'&click=0'
网页对搜索关键词进行了加密,将 ‘ 每周一花 ’ 转化为 ' %E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1 '
可以用 request.unquote()对其解密
一共16页,用for循环获取所有商品列表链接:用urltrue_list接收数据
#获得 京东每周一花的16个商品列表链接 这里可以定义函数,传入要抓取的关键字或页数,使其更加灵活
urltrue_list=[]
for i in range(16):
url='https://search.jd.com/Search?keyword=%E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1&page='+str(i*2+1)+'&s='+str(i*60+1)+'&click=0'
urltrue=request.unquote(url)
urltrue_list.append(urltrue)
打开商品列表链接,每一页都有60个商品,如果直接抓取会发现只能抓到30个,原因是:后30条信息是动态加载的
我的解决办法是设置拖拽,使其将页面加载完整
#定义函数缓慢拖拽页面,使其加载完成
def buffer():
for i in range(50):
time.sleep(0.3)
browser.execute_script('window.scrollBy(0,300)', '')
这样就可以获取到一页里的60个商品详情链接了,同样用for循环获取所有的商品详情链接:
detail_urls = []
def get():
for each in urltrue_list:
browser.get(each) # 遍历每个列表链接
time.sleep(5)
global detail_urls
buffer() # 缓冲,使页面加载完整
info = browser.find_elements_by_class_name('gl-i-wrap')# 寻找商品链接的父阶
for line in info:
# 获取商品列表页所有的商品链接
detail_url = line.find_element_by_class_name('p-img').find_element_by_tag_name('a').get_attribute('href')
detail_urls.append(detail_url)
time.sleep(3)
print(len(detail_urls)) # 查看长度确认是否抓取了60个连接
return detail_urls # 返回商品列表页所有的商品链接组成的列表
get()
有了商品详情链接就有了真正的数据采集入口。
要采集下面截图橘黄框中的信息。
for each detail_urls: # 获取商品详情链接
browser.maximize_window()# 最大化窗口
browser.get(each)
time.sleep(3)
buffer()
#获取商品名称
title = browser.find_element_by_class_name('sku-name').text
#获取价格(当前商品价格)
price = browser.find_element_by_class_name('p-price').find_elements_by_xpath('span')[1].text
#获取系列商品总评论条数(有不同规格)
comment_num = browser.find_element_by_id('comment-count').find_element_by_tag_name('a').text
#获取品牌
brand = browser.find_element_by_id('parameter-brand').find_element_by_tag_name('a').text
#获取店名
shop_name = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[3].get_attribute(
'title')
#获取商品重量
weight = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[4].get_attribute('title')
#获取适用人群
user = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[5].get_attribute('title')
#获取分类
classify = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[6].get_attribute('title')
buffer()
#获取系列商品总好评度(有不同规格)
praise_degree=browser.find_element_by_class_name('percent-con').text
#获取系列商品总评价标签(有不同规格)
tag_list = []
tags = browser.find_elements_by_class_name(' tag-1')
for tag in tags:
tag_list.append(tag.text)
browser.find_element_by_id('comm-curr-sku').send_keys(Keys.SPACE) # 只看当前商品评价
# 当前商品评论条数(单个)
single_comment_num = browser.find_element_by_class_name('filter-list').find_elements_by_tag_name('em')[0].text
# 当前商品评价标签
single_tags = browser.find_element_by_class_name('filter-list').find_elements_by_tag_name('li')[1:7]
single_tag_list = []
for each in single_tags:
det = each.find_element_by_tag_name('a').text
single_tag_list.append(det)
这个部分主要用来抓取评论详情,查看了多个商品详情,发现不管评论有多少条,最多只显示50页的数据。
#抓取一页的评论详情
comment_details = []
def com_info():
divs = browser.find_element_by_id('comment-0').find_elements_by_class_name('comment-item')
for each in divs:
if each.find_element_by_class_name('user-level').find_elements_by_tag_name('a'):
member='PLUS会员'
else:
member='普通会员'
#评星
star=each.find_element_by_class_name('comment-column').\
find_element_by_tag_name('div').get_attribute('class')
# 评语
comment=each.find_element_by_class_name('comment-column').find_element_by_tag_name('p').text
# 图片数或视频数
pic_num=len(each.find_element_by_class_name('comment-column').find_elements_by_tag_name('div')[1].\
find_elements_by_tag_name('a'))+len(each.find_element_by_class_name('comment-column').find_elements_by_tag_name('div')[1].\
find_elements_by_tag_name('video'))
#订单详情
spans = each.find_element_by_class_name('order-info').find_elements_by_tag_name('span')
order=[]
for index, everyone in enumerate(spans):
order.append(spans[index].text)
single=[member,star,comment,pic_num,order]
comment_details.append(single)
多页的数据依旧用for循环获取
for i in range(50): #这里的50设置的有问题,有些商品评价没有这么多,需要写if判断来决定循环多少次
com_info()
browser.find_elements_by_class_name('ui-pager-next')[0].send_keys(Keys.ENTER) # 点击下一页
time.sleep(3)
运行时有时候会报错:
Message: stale element reference: element is not attached to the page document
Message: no such element: Unable to locate element: {"method":"id","selector":"comm-curr-sku"}
(也可能是其他method和selector)
原因:都是因为数据没有加载完成导致,可以用增加sleep时间或者切换更流畅的网络解决
from selenium import webdriver
import time
from urllib import request
from selenium.webdriver.common.keys import Keys
#定义函数缓慢拖拽页面,使其加载完成
def buffer():
for i in range(50):
time.sleep(0.3)
browser.execute_script('window.scrollBy(0,300)', '')
#定义函数,抓取商品一页的评论详情
comment_details = []
def com_info():
#获取评论详情的父阶
divs = browser.find_element_by_id('comment-0').find_elements_by_class_name('comment-item')
#获取评论人群的会员分类
for each in divs:
if each.find_element_by_class_name('user-level').find_elements_by_tag_name('a'):
member='PLUS会员'
else:
member='普通会员'
# 评星
star=each.find_element_by_class_name('comment-column').\
find_element_by_tag_name('div').get_attribute('class')
# 评语
comment=each.find_element_by_class_name('comment-column').find_element_by_tag_name('p').text
# 图片数或视频数
pic_num=len(each.find_element_by_class_name('comment-column').find_elements_by_tag_name('div')[1].\
find_elements_by_tag_name('a'))+len(each.find_element_by_class_name('comment-column').find_elements_by_tag_name('div')[1].\
find_elements_by_tag_name('video'))
#订单详情
spans = each.find_element_by_class_name('order-info').find_elements_by_tag_name('span')
order=[]
for index, everyone in enumerate(spans):
order.append(spans[index].text)
single=[member,star,comment,pic_num,order]
comment_details.append(single)
#获得 京东每周一花的16个商品列表链接 这里可以定义函数,传入要抓取的关键字或页数,使其更加灵活
urltrue_list=[]
for i in range(16):
url='https://search.jd.com/Search?keyword=%E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%AF%8F%E5%91%A8%E4%B8%80%E8%8A%B1&page='+str(i*2+1)+'&s='+str(i*60+1)+'&click=0'
urltrue=request.unquote(url)
urltrue_list.append(urltrue)
# 定义函数,获取商品列表页所有的商品详情链接
browser = webdriver.Chrome()
detail_urls = []
def get():
for each in urltrue_list:
browser.get(each) # 遍历每个链接
time.sleep(5)
global detail_urls
buffer() # 缓冲,使页面加载完整
info = browser.find_elements_by_class_name('gl-i-wrap')# 寻找商品链接的父阶
for line in info:
# 获取商品列表页所有的商品链接
detail_url = line.find_element_by_class_name('p-img').find_element_by_tag_name('a').get_attribute('href')
detail_urls.append(detail_url)
time.sleep(3)
print(len(detail_urls)) # 查看长度确认是否抓取了60个连接
return detail_urls # 返回商品列表页所有的商品链接组成的列表
get()
import os
os.mkdir('txt')
os.chdir('txt')
#遍历商品详情页,抓取目标数据, 这里可以对获取的每一个数据分开定义函数
result=[]
for index,each in enumerate(detail_urls): # 获取商品详情链接
#browser.maximize_window()# 最大化窗口
browser.get(each)
time.sleep(3)
file = open(str(index)+'.txt', 'w', encoding='utf-8')
buffer()
#获取商品名称
title = browser.find_element_by_class_name('sku-name').text
#获取价格(当前商品价格)
price = browser.find_element_by_class_name('p-price').find_elements_by_xpath('span')[1].text
#获取系列商品总评论条数(有不同规格)
comment_num = browser.find_element_by_id('comment-count').find_element_by_tag_name('a').text
#获取品牌
brand = browser.find_element_by_id('parameter-brand').find_element_by_tag_name('a').text
#获取店名
shop_name = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[3].get_attribute(
'title')
#获取商品重量
weight = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[4].get_attribute('title')
#获取适用人群
user = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[5].get_attribute('title')
#获取分类
classify = browser.find_element_by_class_name('p-parameter').find_elements_by_tag_name('li')[6].get_attribute('title')
buffer()
#获取系列商品总好评度(有不同规格)
praise_degree=browser.find_element_by_class_name('percent-con').text
#获取系列商品总评价标签(有不同规格)
tag_list = []
tags = browser.find_elements_by_class_name(' tag-1')
for tag in tags:
tag_list.append(tag.text)
browser.find_element_by_id('comm-curr-sku').send_keys(Keys.SPACE) # 只看当前商品评价
# 当前商品评论条数(单个)
single_comment_num = browser.find_element_by_class_name('filter-list').find_elements_by_tag_name('em')[0].text
# 当前商品评价标签
single_tags = browser.find_element_by_class_name('filter-list').find_elements_by_tag_name('li')[1:7]
single_tag_list = []
for each in single_tags:
det = each.find_element_by_tag_name('a').text
single_tag_list.append(det)
detail=[title,price,brand,shop_name,weight,user,classify,comment_num,praise_degree,tag_list,single_comment_num,single_tag_list]
result+=detail # 将以上信息放到result列表中
# 评论详情 包含用户会员等级、评星、评语、图片/视频数及订单详情
for i in range(50): #这里的50设置的有问题,有些商品评价没有这么多,需要写if判断来决定循环多少次
com_info()
browser.find_elements_by_class_name('ui-pager-next')[0].send_keys(Keys.ENTER) #点击评论的下一页
time.sleep(3)
print(comment_details)
con_text=comment_details
# 存储 这里应该将存储单独定义函数
# 将所有的商品信息存入到一个excel中,便于做行业分析
# 将每个产品的评语分别存入txt文件便于做商品分析和用户分析
file.write(str(comment_details)) #将每个商品的评语详情写入单独的txt文件
from openpyxl import Workbook
workbook=Workbook()
sheet=workbook.active
sheet.title='每周一花商品数据'
sheet.append('商品名称,价格,品牌,店名,商品重量,适用人群,商品分类,系列商品总评论数,系列商品总好评度,系列商品总评价标签'
'当前商品评论条数,当前商品评价标签'.split(','))
#花材与产地因为信息不完整,没有抓取 如有分析需求再行补抓
for each in result:
sheet.append(each)
workbook.save('每周一花商品数据.xlsx')
python是万能的,我不是……
这篇博文仅记录自己编程的过程,为这段代码做个总结和review,有很多拙劣的地方,有时间再做优化。