python 爬虫抓取某电商页面的商品价格
业务需求最近想通过爬虫抓取某电商商品页的价格。页面如下:实践然后就兴冲冲的写了段代码来爬取网页数据。# 厨房卫浴href = 'http://search.gome.com.cn/search?question=%E5%8E%A8%E6%88%BF%E5%8D%AB%E6%B5%B4'res = requests.get(href)# print(res.text)soup = Beautiful
1. 业务需求
最近想通过爬虫抓取某电商商品页的商品详情,
浏览器页面打开如下:
本来以为是一个很简单的爬虫,却没想到一波三折,并没有那么简单。
2. 付诸实践
接到任务后,就兴冲冲的写了段代码来爬取网页数据。
# 厨房卫浴
href = 'http://search.gome.com.cn/search?question=%E5%8E%A8%E6%88%BF%E5%8D%AB%E6%B5%B4'
res = requests.get(href)
# print(res.text)
soup = BeautifulSoup(res.text, 'html.parser')
# product_list = soup.find('li', class_='product-item hideSmallBox')
# print(product_list)
product_list = soup.find_all('li', attrs={'class': 'product-item hideSmallBox'})
for item in product_list:
if type(item) is bs4.element.Tag:
print(item)
sku_id = item.get('skuid')
"""
<a class="emcodeItem item-link" href="//item.gome.com.cn/9140257462-1130961371.html?search_id=sdw233vyv434" target="_blank"
title="宜来卫浴厨房龙头E-49018镀铬"
track="产品列表图片"><img alt="宜来卫浴厨房龙头E-49018镀铬" gome-src="//gfs17.gomein.net.cn/T1hK_eBj_T1RCvBVdK_210.jpg"
src="//app.gomein.net.cn/images/grey.gif"/></a>
"""
emcode_item = item.find('a', class_='emcodeItem item-link')
item_title = emcode_item.attrs['title']
item_url = emcode_item.attrs['href']
"""
<img alt="宜来卫浴厨房龙头E-49018镀铬" gome-src="//gfs17.gomein.net.cn/T1hK_eBj_T1RCvBVdK_210.jpg"
src="//app.gomein.net.cn/images/grey.gif"/>
"""
item_image_tag = emcode_item.find('img')
item_image_url = item_image_tag.attrs['gome-src']
执行任务后,好像哪里不对,遇到了意想不到的问题。
3. 出现问题
问题来了,查看网页源代码是能看到价格的,如下:
但是我通过BeautifulSoup
解析后却死活找不到价格在那里。如下图,本应出现在price的<span>
标签中的价格呢?
后来检查了一通,才知道该电商页面的价格是通过js加载的,这也是一般电商行业防爬虫的手段之一,也就是说直接获取html的<span>
是不能的。
4. 找出问题
F12 打开这个页面,按住Ctrl + F
调出左侧搜索框,搜索价格:
经过一通排查,终于发现了猫腻,原来价格获取藏在这里,接下来又是一顿操作终于找到藏在深处的获取价格的url,如下图:
把这个链接拿出来:
http://ss.gome.com.cn/search/v1/price/single/1001/G001/9140257462/1130961371/11010000/flag/item/fn1616072940509?callback=fn1616072940509&_=1616072940509
这个就是该电商商品页的价格获取链接,然后再来看看响应吧:
fn1616072940509({
"success": true,
"result": {
"price": "299.0",
"priceType": "RUSHBUYPRICE",
"productId": "9140257462",
"skuId": "1130961371"
}
})
我们解析一下上面的url,9140257462是productId
,1130961371是skuId
,这两个值都可以从HTML中获取到,然后再研究一下这个链接,发现还有三个参数未知:
经过N次的摸索测试,发现这三个参数如果不知道就都写成null
就好了,这应该是该电商的一个漏洞吧,而且按照下面的连接请求,也能获取到价格:
http://ss.gome.com.cn/search/v1/price/single/null/null/9140257462/1130961371/null/flag/item
截止到此,已经探索出来商品的价格获取链接了,然后又赶紧改进代码,增加一个获取价格的方法:
def get_item_price(product_id, skuid):
"""
:param product_id: 商品id
:param skuid: skuId
:return: 返回价格
"""
price_url = 'http://ss.gome.com.cn/search/v1/price/single/null/null/' + product_id + '/' + skuid + '/null/flag/item'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
response = requests.get(price_url, headers=headers, timeout=30)
if response.status_code == 200:
return response.json()['result']['price']
else:
return "价格未知"
5. 最终代码
找到问题所在之后,确定最终代码:
# encoding: utf-8
import bs4
import requests
from bs4 import BeautifulSoup
import csv
import os
import platform
# 全局变量 用来计算一共爬虫爬了多少条数据
counter = 0
file = ''
project = ''
def write_fo_file(file_path, rows_data, *is_touch_file):
"""
:param file_path:
:param rows:
:param is_touch_file:
:return:
"""
if is_touch_file and is_touch_file[0] == 1:
# sys_str = platform.system()
# if sys_str == "Windows":
# if not os.path.exists(file_path):
# os.system(r"type nul > {}".format(file_path))
# else:
# print(file_path, '文件已存在')
# elif sys_str == "Linux":
# if not os.path.exists(file_path):
# os.system(r"touch {}".format(file_path))
# else:
# print(file_path, '文件已存在')
# else:
# print("Other System tasks: %s" % sys_str)
global file
file = file_path
with open(file_path, "a", newline="", encoding='utf-8-sig') as csvfile:
rows = ("商品名称", "商品productID", "商品sku_id", '商品链接', '商品图片', '商品价格')
writer = csv.writer(csvfile)
writer.writerow(rows)
csvfile.flush()
csvfile.close()
else:
with open(file_path, "a", newline="", encoding='utf-8-sig') as csvfile:
writer = csv.writer(csvfile)
# 批量写入文件
for row_data in rows_data:
writer.writerow(row_data)
csvfile.flush()
csvfile.close()
def get_product_lists(product_lists):
item_lists = []
for item in product_lists:
if type(item) is bs4.element.Tag:
# print(item)
sku_id = item.get('skuid')
pid = item.get('pid')
"""
样例数据:
<a class="emcodeItem item-link" href="//item.gome.com.cn/9140257462-1130961371.html?search_id=sdw233vyv434" target="_blank"
title="宜来卫浴厨房龙头E-49018镀铬"
track="产品列表图片"><img alt="宜来卫浴厨房龙头E-49018镀铬" gome-src="//gfs17.gomein.net.cn/T1hK_eBj_T1RCvBVdK_210.jpg"
src="//app.gomein.net.cn/images/grey.gif"/></a>
"""
emcode_item = item.find('a', class_='emcodeItem item-link')
item_title = emcode_item.attrs['title']
item_url = emcode_item.attrs['href']
"""
样例数据:
<img alt="宜来卫浴厨房龙头E-49018镀铬" gome-src="//gfs17.gomein.net.cn/T1hK_eBj_T1RCvBVdK_210.jpg"
src="//app.gomein.net.cn/images/grey.gif"/>
"""
item_image_tag = emcode_item.find('img')
item_image_url = item_image_tag.attrs['gome-src']
item_price = get_item_price(product_id=pid, skuid=sku_id)
info = {'name': item_title, 'pid': pid, 'sku_id': sku_id, "href": item_url,
'image': item_image_url, 'price': item_price}
item_info = (item_title, pid, sku_id, 'http:' + item_url, 'http:' + item_image_url, item_price)
item_lists.append(item_info)
return item_lists
def get_page(url, page_num):
"""
:param url: 要爬取页面的url
:param page_num: 要爬取的页数
:return:
"""
headers = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"Host": "search.gome.com.cn",
"Referer": "http://search.gome.com.cn/search?question=%E5%8E%A8%E6%88%BF%E5%8D%AB%E6%B5%B4",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
'X-Requested-With': 'XMLHttpRequest'
}
"""
Chengyanan 注:
如果为了更好的展示数据效果,可将下列type 改为json即可
我这里是为了更好的使用BeautifulSoup解析,所以用了默认的HTML类型
"""
for i in range(1, page_num + 1):
formdata = {
"facets": '',
"page": i,
# "type": "json",
"aCnt": 0
}
# print([url, formdata])
try:
r = requests.post(url, data=formdata, headers=headers)
soup = BeautifulSoup(r.text, 'html.parser')
product_list = soup.find('ul', class_='product-lists clearfix')
items = get_product_lists(product_list)
global counter
counter += items.__len__()
print(r"当前正在爬取的项目是{},一共需要爬取{}页,正在爬取第{}页,本页爬取数据{}条,累计爬取{}条..."
.format(project, page_num, i, items.__len__(), counter))
write_fo_file(file, items)
except Exception as e:
print(e)
print('链接失败')
def get_item_price(product_id, skuid):
"""
:desc 根据输入的产品id 和 skuid 获取商品的价格
:param product_id: 商品id
:param skuid: skuId
:return: 返回价格
"""
price_url = 'http://ss.gome.com.cn/search/v1/price/single/null/null/' + product_id + '/' + skuid + '/null/flag/item'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
response = requests.get(price_url, headers=headers, timeout=30)
if response.status_code == 200:
return response.json()['result']['price']
else:
return "价格未知"
if __name__ == '__main__':
# 家居家装下分类品牌
dicts = {
"厨房卫浴": 'chu_fang',
"灯饰照明": 'deng_shi',
# "五金电料": 'wujin',
# "装修材料": 'zhuangxiu',
"客厅家具": 'keting',
"卧室家具": 'woshi',
"储物家具": 'chuwu',
"办公家具": 'bangong'
}
for i in dicts.items():
project = i[0]
write_fo_file(r'gm_spider_{}.csv'.format(i[1]), '', 1)
get_page(r'http://search.gome.com.cn/search?question={}'.format(i[0]), 4)
爬取结果
关注一下:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)