记录一次爬取Alibaba商品库存

需求

由于淘宝近期暂停了批量商品库存同步功能,导致一件传淘宝用户查看/同步库存很难受,需要一个一个商品的点开才能看到库存。所以就想有没有什么批量查库存的工具,网上到时有很多,但都是需要收费的,所以就为自己写一个。

需要数据

商品名称、商品规格、商品库存

分析网页

经过分析网页发现所需数据可直接在网页源码中找到,其商品名称在<meta property="og:title" content="xxx"/>里,商品规格与库存都在skuMap里

初期构想

通过上面分析网页发现找到数据不需要异步,直接就出现在网页源码中,所以利用requests库将网页内容扒下来,再通过re的正则筛选需要的信息,因为规格与库存都在skumap里,所以要先扒出skumap的内容再在skumap的内容里查找规格和库存。

初期代码实现

通过requests库扒下网页

1
2
3
html = requests.get(url)
html.encoding = 'gbk'
html = html.text

通过re的正则获取数据

1
2
3
4
title = re.findall(r'<meta property="og:title" content="(.*?)">',html,re.S)  #获取商品名称
skumap = re.findall(r'"skuMap":{(.*?)}},',html,re.S) #匹配skumap的内容
spec = re.findall(r'"(.*?):{"',skumap,re.S) #在skumap中查 获取商品规格
stock = re.findall(r'"canBookCount":([0-9]\d*),',skumap,re.S) #在skumap中查 获取商品库存

初期不足

因为Python的正则匹配默认贪婪开启原则,虽然加了”?”关闭贪婪原则,但商品规格他有数字/字符/中文等混合组合而成,所以即使关闭了贪婪,但还是会多的匹配。

再次分析网页数据

发现数据呈现为如下

1
2
3
4
"粉红&gt;均码":{"specId":"452b5d2989a4a3fa4fc3c1e8a97f47cd","skuStorageInfos":"[]","saleCount":54,"canBookCount":160,"skuId":3827147188400},
"银&gt;均码":{"specId":"eb9b5c651f824a16fe2c02d393b22ab0","skuStorageInfos":"[]","saleCount":84,"canBookCount":125,"skuId":3827147188401},
"黑色&gt;均码":{"specId":"7ccb9e57d1d5f349172254f5c3eb0f3e","skuStorageInfos":"[]","saleCount":88,"canBookCount":138,"skuId":3827147188399},
"蓝&gt;均码":{"specId":"292c801ce4f1b2a78b43aaab27bb21f6","skuStorageInfos":"[]","saleCount":100,"canBookCount":124,"skuId":3827147188402}

初期处理方法

将skumap的数据用.split()将其分割,然后在通过for循环一条一条的匹配

1
skuMap = skuMap.split('},')

规格和库存新的匹配代码

1
2
3
4
5
6
7
spec = []
stock = []
for skum in skuMap:
specs = re.findall(r'"(.*?):{"',skum,re.S) #获取商品规格
stocks = re.findall(r'"canBookCount":([0-9]\d*),',skum,re.S) #获取商品库存
spec.append(specs)
stock.append(stocks)

进过代码修改能够快速完整的爬取想要爬取的网页但还是不能完成需求的爬取多个商品

初步构想

另外创建一个数据文件url.txt用于保存需要扒取的网页地址,蜘蛛运行时先读取此数据文件的url链接,在for循环所有的链接

代码实现

1
2
with open('url.txt', 'r') as f:
data = f.read().splitlines()

再次的代码调整修改,此蜘蛛已经能快速高效的完成做业,但多次爬取后发现需要登录账号

初步构想

在requests.get()时加上cookie加上headers。但经过多次尝试为成功。最终祭出终极大杀器selenium的无头浏览器PhantomJS()来爬取网页,放弃requests

代码实现

1
2
3
driver = webdriver.PhantomJS()
driver.get(url)
html = self.driver.page_source #获取网页源代码

运用大杀器后处理扒去速度慢外就只剩下爬取数据的保存问题

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
with open('stock.txt','a+') as f:
print(title[0])
f.write(title[0])
f.write('\n')
for sp,st,mo in zip(spec,stock,money):
sp_st = str(sp) + str(mo) + str(st)
f.write(sp_st)
f.write('\n')
f.write(url)
f.write('\n')
f.write('++++++++++++++++++++')
f.write('\n')
print("%s Done"%title[0])
return True

关于selenium的get慢的问题,是因为get后要等网页渲染完后才进行下一步,所以爬取笔requests慢很多。

导图

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import re
from selenium import webdriver

class Stock():
def __init__(self):
self.headers = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36')

def get_html(self, url):
self.driver = webdriver.PhantomJS()
self.driver.get(url)
html = self.driver.page_source # 获取整个网页源代码
self.driver.get_screenshot_as_file('verify.png')
return html

# 读取TXT文件内的url
def get_url(self):
with open('url.txt', 'r') as f:
data = f.read().splitlines()
return data

#获取title
def get_title(self, html):
title = re.findall(r'<meta property="og:title" content="(.*?)">',html,re.S)
return title

def get_skumap(self, html):
skumap = re.findall(r'"skuMap":{(.*?)}},',html,re.S)
return skumap

#获取规格
def get_spec(self, skumap):
spec = []
for skum in skumap:
skum = skum.replace('&gt;', '')
specs = re.findall(r'"(.+?):{"',skum,re.S)
spec.append(specs)
return spec

#获取库存
def get_stock(self, skumap):
stock = []
for skum in skumap:
skum = skum.replace('&gt;', '')
stocks = re.findall(r'"canBookCount":([0-9]\d*),',skum,re.S)
stock.append(stocks)
return stock

#获取价格(因关键字不同一,某些商品无法获取到)
def get_money(self, skumap):
money = []
for skum in skumap:
skum = skum.replace('&gt;', '')
moneys = re.findall(r'"discountPrice":"(.*?)","canBookCount"',skum,re.S)
money.append(moneys)
return money

#数据保存为TXT
def save_stock(self,title,spec,stock,money,url):
with open('stock.txt','a+') as f:
print(title[0])
f.write(title[0])
f.write('\n')
for sp,st,mo in zip(spec,stock,money):
sp_st = str(sp) + str(mo) + str(st)
f.write(sp_st)
f.write('\n')
f.write(url)
f.write('\n')
f.write('++++++++++++++++++++')
f.write('\n')
print("%s Done"%title[0])
return True

#开始
def star(self):
urls = self.get_url()
for url in urls:
if url == '':
continue
else:
html = self.get_html(url=url)
title = self.get_title(html)
skumap = self.get_skumap(html=html)
skumap = skumap[0].split('},')
spec = self.get_spec(skumap=skumap)
stock = self.get_stock(skumap=skumap)
money = self.get_money(skumap=skumap)
self.save_stock(title,spec,stock,money,url)
self.driver.quit()

if __name__ == '__main__':
tb = Stock()
tb.star()