此专栏用来记录爬各类网站商品信息的Python代码,以便回忆~(谁让博主记性不好咧~)
此次爬虫是关于998KA的商品信息,如果有需要998KA.CN.csv文件的童鞋可以去我的博客上下载,可以看看998KA网站内商品信息的具体内容,方便深入理解代码~
# !/usr/bin/env python
import re
import requests
from lxml import etree
import time
Max = 500 # 每获取到500个商品的信息, 保存一次, 调试时可以改为10, 方便看到结果
# xpath获取数据的规则
get_cateid_rule = '//select[@name="cateid"]/option/attribute::value' # 从网页源码中获取分类的id的xpath规则
get_catename_rule = '//select[@name="cateid"]/option/text()' # 从网页源码中获取分类的名字的xpath规则
gonggao_rule = r'//div[contains(@class, "boxcon")]/span/text()' # 从网页源码中获取商户公告的标识的xpath规则
get_good_list_payload = "cateid=%s" # 发送post请求时的data部分,这里使用了格式化的表示方法, 使用%s占位符(类似c中的%s)
get_good_info_payload = "goodid=%s"
# 获取信息的url
pingtai = "http://wwww.998ka.cn/"
cate_ajax = "http://wwww.998ka.cn/ajax/getgoodlist" # 获取goods_list的url地址
good_ajax = "http://wwww.998ka.cn/ajax/getgoodinfo" # 获取具体商品信息的url地址
# 请求头字典
headers = {
'Cookie': "_qddaz=QD.i3wgpi.5be72m.jxyrtpbm; pgv_pvi=8245519360; pgv_si=s9958262784; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1563082827; __jsluid_h=6130e5c13d16e6f3382281a282d4a535; Hm_lvt_d7682ab43891c68a00de46e9ce5b76aa=1563082904; Hm_lpvt_d7682ab43891c68a00de46e9ce5b76aa=1563082904; Hm_lpvt_d7682ab43891c68a00de46e9ce5b76aa=1563082914; PHPSESSID=ggtkdgh2m11345i97lok1ce203; IESESSION=alive; tencentSig=5170108416; _qddab=3-ksdh2e.jy2kwcul",
'Origin': "http://wwww.998ka.cn",
'Accept-Encoding': "gzip, deflate",
'Accept-Language': "zh-CN,zh;q=0.9",
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",
'Accept': "*/*",
'Referer': "http://wwww.998ka.cn/links/E585221043918FB0",
'X-Requested-With': "XMLHttpRequest",
'Connection': "keep-alive",
'Cache-Control': "no-cache",
'Postman-Token': "24eb553c-79d0-4bc9-90cb-9daa063950b7,b20edaf9-53f0-4e44-96cf-d6a9a4cb6c8b",
'Host': "wwww.998ka.cn",
'content-length': "10",
'cache-control': "no-cache"
}
def remove(data):
"""
去除获取的文本中的转义字符,方便保存
"""
return str(data).replace('\n', '').replace(' ', '') \
.replace('\t', '').replace('\r', '')
def get_all_url():
"""
将csv文件中的所有url进行分类,找出其中的店铺url
"""
shop_url = []
with open('998KA.CN.csv', 'r', encoding='utf-8') as f:
all_lines = f.readlines() # 从文件中一行一行读取数据,并返回一个列表
# print(all_lines)
for line in all_lines:
tmp = line.split(',') # 用逗号分隔每一行的数据,并存在数组里
url = tmp[0]
if 'cate' in url: # 含有'cate'、'links'、'product'这些关键字的就是店铺url
shop_url.append(url)
if 'links' in url:
shop_url.append(url) # 在列表的末尾添加这些url
if 'product' in url:
shop_url.append(url)
with open('998ka.txt', 'w', encoding='utf-8') as f1: # 将url存进文件中
for i in shop_url:
f1.write(i + '\n')
return shop_url
def get_goodids(session, url, cateid):
"""
根据商品列表的id,获得下面所有商品的id和name
:param session:
:param url:
:param cateid:
:return: ids, names
"""
payload = get_good_list_payload % (cateid) # 格式化构造字符串, get_good_list_payload 这个变量中有两个%s占位符,
# print(payload) #在后面加上 % (cateid)后, 会自动将后面的两个变量填充到对应位置
try:
res = session.post(cate_ajax, data=payload, headers=headers)
except Exception as e:
print(url, e)
return None
# 返回商品的id和name信息
else:
if 'option' not in res.text:
print("此分类下没有商品", cateid)
return None, None
else:
goodlist = res.text.split('</option>')
gdid, gdname = [], []
for i in goodlist:
id = re.findall(r'\d+', i) # 将商品编号提取出来
gdid.extend(id)
name = re.sub('<.*>', '', i) # 将商品名称提取出来
gdname.append(name)
print(gdid, gdname)
return gdid, gdname
def get_goodinfo(session, url, goodid):
"""
根据商品id,获取商品所有信息
"""
try:
payload = get_good_info_payload % (goodid) # 构造请求数据
# print(payload)
res = session.post(good_ajax, data=payload, headers=headers)
goodinfo = res.text.encode('utf-8').decode('unicode_escape')
# print(goodinfo)
# goodinfo = res.text
# print(goodinfo)
except Exception as e:
print(res.status_code, res.text)
print(url, e)
return goodinfo
def data_together(url, goodname, sellerinfo, price, stock):
""""
这里是将信息整合的部分, 一个商铺页面有很多信息, 尽量获取他们, 获取不到的信息, 就用一个''占位即可
"""
try:
datalist = [
'', # id, 空着
today, # 今天的日期, 格式如20190711,可以使用time库获取
'', # 插入数据库的时间, 空着即可,
goodname, # 商品标题/商品名
'', # 商品详细信息
'', # 厂家,
'', # 有效期,
sellerinfo, # 商铺信息, 比如联系方式,如果搞不到空着即可
price, # 价格
'', # 价格增长量
'', # 批发价
'', # 商品分类, 空着
'', # 空着
'998ka.cn', # 网站域名
stock, # 库存量, 没有可以空着
'', # 空着即可
url, # 商品所属页面的url,
url, # 商品详情页面的url, 没有的话设置为所属页面的url
]
except Exception as e:
print(url, e)
else:
datalist = list(map(remove, datalist)) # 通过内置函数map() , 将dataList这个列表中的每一个字符串进行去空格,去换行符等处理
data = '\t'.join(datalist) # 用join函数, 以'\t'为分隔符链接dataList这个列表中的每一项,返回一个字符串给data
return data
def get_all_data(url, try_cnt=2):
"""
获取某个商铺的所有商品的信息,重复次数:2
"""
all_data = []
session = requests.session()
try:
r = session.get(url=url, headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", })
except Exception as e:
print(url, e, "连接主页面失败")
else:
if r.status_code != 200:
if r.status_code == 503:
if try_cnt:
time.sleep(5)
return get_all_data(url, try_cnt-1)
else:
print(url, "data为0")
return all_data
else:
print(url, "data为0")
return all_data
res_xpath = etree.HTML(r.text) # etree.HTML解析html内容
# 联系方式
qq = ''
if re.findall('uin=[0-9]+', r.text): # 找到所有匹配的字符串,并返回
qq = re.findall('uin=[0-9]+', r.text)[0].replace('uin=', '')
# 商家信息
gonggao = res_xpath.xpath(gonggao_rule)
# url一共分三种情况
if 'product' in url: # 没有分类也没有名称的情况
seller = res_xpath.xpath('//div[@class="boxcon"]//text()') # 商家信息
sellerinfo = {'商家信息': seller, '商家公告': gonggao, 'qq': qq}
goodname = res_xpath.xpath('//span[@style="font-size:1.2em"]/text()') # 商品名称
if len(goodname) == 0:
goodname = res_xpath.xpath('//span[@class="t1"]/text()')
goodid = res_xpath.xpath('//script/input[@name="goodid"]/attribute::value') # 商品ID
price = res_xpath.xpath('//label/span[@class="price"]/text()')
print(sellerinfo, goodname, goodid, price)
all_data.append(data_together(url, goodname, sellerinfo, price, ''))
else:
cateidlist = res_xpath.xpath(get_cateid_rule) # 第一个不可用,商品列表的id
catenames = res_xpath.xpath(get_catename_rule)
seller = res_xpath.xpath('//div[@class="boxcon"]/text()')
if qq != '':
if len(cateidlist) == 0: # 没有分类有商品的情况
name_rule = '//span[@class="t1"]/text()'
id_rule = '//div/input[@name="cateid"]/attribute::value'
cateidlist.extend('0')
catenames.extend('嗯')
cateid = res_xpath.xpath(id_rule) # 获取分类id
catename = res_xpath.xpath(name_rule) # 获取分类名称
if len(catename) == 0:
catename = res_xpath.xpath('//span[@style="font-size:1.2em"]/text()')
cateid = res_xpath.xpath('//script/input[@name="cateid"]/attribute::value')
cateidlist.extend(cateid)
catenames.extend(catename)
seller = res_xpath.xpath('//h1/p/text()')
# print(cateidlist, catenames, seller)
sellerinfo = {'商家信息': seller, '商家公告': gonggao, 'qq': qq}
# print(sellerinfo)
print(url, cateidlist, catenames, 'qq:', qq) # 这是分类那一栏的,以及每一类的id和名称
for cateid, catename in zip(cateidlist[1:], catenames[1:]):
goodids, goodnames = get_goodids(session, url, cateid)
if goodids:
for goodid, goodname in zip(goodids, goodnames):
goodinfo = get_goodinfo(session, url, goodid)
# 从获取的信息中提取单价
price = re.findall('\d+.?\d*', goodinfo)[0]
# 从获取的信息中提取库存
stock = re.sub('.*value="', '', goodinfo)
stock = re.sub('">","is_discount":.*', '', stock)
stock = re.findall('\d+', stock)[0]
print(price, stock)
all_data.append(data_together(url, goodname, sellerinfo, price, stock))
return all_data
def write_data(total_data):
"""
功能: 将商品信息追加到以当天时间命名的文件上
"""
with open('./data/' + today + '.txt', 'a', encoding='utf-8') as f:
for i in total_data:
f.write(i + '\n')
if __name__ == '__main__':
today = time.strftime('%Y%m%d') # 获取当前时间
total_data = []
print(len(get_all_url()))
num = 0
# 将每个店铺的信息存在data文件中
for url in get_all_url():
time.sleep(3) # 暂停3秒后执行程序
try:
all_data = get_all_data(url) # 获取店铺里的所有信息
total_data.extend(all_data) # 将信息追加进总的信息中
if len(total_data) > Max: # 当已经保存的数据条数大于设置设数量时, 追加到文件之后
write_data(total_data)
num += 1
total_data = []
except Exception as e: # 捕获其他所有的异常信息
print(url, e)
# 将total_data里剩下的数据也追加到文件中
num += len(total_data)
print(num)
write_data(total_data)