def deal_with_data(self, *params): ''' 处理并存储相关秒杀商品数据 :param params: 相关参数 :return: ''' item_list = params[0] chuchujie = ChuChuJie_9_9_Parse() my_pipeline = SqlServerMyPageInfoSaveItemPipeline() if my_pipeline.is_connect_success: db_goods_id_list = [ item[0] for item in list( my_pipeline.select_chuchujie_xianshimiaosha_all_goods_id()) ] # print(db_goods_id_list) # my_phantomjs = MyPhantomjs() # my_phantomjs.init_phantomjs() # index = 1 for item in item_list: if item.get('goods_id', '') in db_goods_id_list: print('该goods_id已经存在于数据库中, 此处跳过') pass else: goods_id = item.get('goods_id', '') tmp_url = 'https://m.chuchujie.com/details/detail.html?id=' + str( goods_id) chuchujie.get_goods_data(goods_id=goods_id) goods_data = chuchujie.deal_with_data() if goods_data == {}: # 返回的data为空则跳过 pass elif goods_data.get('is_delete', 0) == 1: # is_delete=1(即库存为0)则跳过 print('------>>>| 该商品库存为0,已被抢光!') pass else: # 否则就解析并且插入 my_phantomjs = MyPhantomjs() my_phantomjs.init_phantomjs() # 获取剩余时间 tmp_body = my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url, css_selector='p#activityTime span') # print(tmp_body) try: del my_phantomjs except: pass gc.collect() if tmp_body == '': # 获取手机版的页面完整html失败 sleep(.4) pass else: # p#activityTime span _t = Selector(text=tmp_body).css( 'p#activityTime span::text').extract_first() _t = re.compile(r'剩余').sub('', _t) # print(_t) if _t == '' or _t is None: print('获取到的_t为空值, 严重错误! 请检查!') miaosha_end_time = self.get_miaosha_end_time(_t) goods_data['goods_url'] = tmp_url goods_data['goods_id'] = str(goods_id) goods_data['sub_title'] = item.get('sub_title', '') goods_data['miaosha_time'] = { 'miaosha_begin_time': self.timestamp_to_regulartime(int( time.time())), 'miaosha_end_time': self.timestamp_to_regulartime( int(miaosha_end_time)), } goods_data['miaosha_begin_time'], goods_data[ 'miaosha_end_time'] = self.get_miaosha_begin_time_and_miaosha_end_time( miaosha_time=goods_data['miaosha_time']) goods_data['gender'] = str(item.get('gender', '0')) goods_data['page'] = item.get('page') # pprint(goods_data) # print(goods_data) chuchujie.insert_into_chuchujie_xianshimiaosha_table( data=goods_data, pipeline=my_pipeline) # sleep(CHUCHUJIE_SLEEP_TIME) # 放慢速度 由于初始化用了phantomjs时间久,于是就不睡眠 # index += 1 else: print('数据库连接失败,此处跳过!') pass try: del chuchujie except: pass gc.collect()
def get_goods_data(self, goods_id): ''' 模拟构造得到data的url :param goods_id: :return: data 类型dict ''' if goods_id == '': self.result_data = {} return {} print('------>>>| 对应的手机端地址为: ', 'https://m.chuchujie.com/details/detail.html?id=' + goods_id) ''' 1.原先直接去手机端页面api post请求数据但是死活就返回请求参数错误,反复研究无果, 就改为解析pc端的 ''' # tmp_url = 'https://api-product.chuchujie.com/api.php?method=product_detail' # self.headers['Referer'] = 'https://m.chuchujie.com/details/detail.html?id=' + str(goods_id) # # # 设置代理ip # ip_object = MyIpPools() # self.proxies = ip_object.get_proxy_ip_from_ip_pool() # {'http': ['xx', 'yy', ...]} # self.proxy = self.proxies['http'][randint(0, len(self.proxies) - 1)] # # tmp_proxies = { # 'http': self.proxy, # } # # print('------>>>| 正在使用代理ip: {} 进行爬取... |<<<------'.format(self.proxy)) # # params_2 = { # "channel": "QD_appstore", # "package_name": "com.culiukeji.huanletao", # "client_version": "3.9.101", # "ageGroup": "AG_0to24", # "client_type": "h5", # "api_version": "v5", # "imei": "", # "method": "product_detail", # "gender": "1", # 性别 0-女、1-男 # "token": "", # "userId": "", # "product_id": int(goods_id), # } # # params = { # 'data': json.dumps(params_2), # } # # try: # # response = requests.post( # # url=tmp_url, # # headers=self.headers, # # data=json.dumps(params), # # proxies=tmp_proxies, # # timeout=13 # # ) # response = requests.get( # url=tmp_url, # headers=self.headers, # params=params, # proxies=tmp_proxies, # timeout=13, # ) # last_url = re.compile(r'\+').sub('', response.url) # 转换后得到正确的url请求地址 # print(last_url) # print(tmp_url + '&data=%7B%22channel%22%3A%22QD_appstore%22%2C%22package_name%22%3A%22com.culiukeji.huanletao%22%2C%22client_version%22%3A%223.9.101%22%2C%22ageGroup%22%3A%22AG_0to24%22%2C%22client_type%22%3A%22h5%22%2C%22api_version%22%3A%22v5%22%2C%22imei%22%3A%22%22%2C%22method%22%3A%22product_detail%22%2C%22gender%22%3A%221%22%2C%22token%22%3A%22%22%2C%22userId%22%3A%22%22%2C%22product_id%22%3A10016793335%7D') # response = requests.get(last_url, headers=self.headers, proxies=tmp_proxies, timeout=13) # 在requests里面传数据,在构造头时,注意在url外头的&xxx=也得先构造 # # data = response.content.decode('utf-8') # print(data) # # except Exception: # print('requests.post()请求超时....') # print('data为空!') # self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 # return {} ''' 2. 改为解析pc端的商品页面数据 ''' tmp_url = 'http://wx.chuchujie.com/index.php?s=/WebProduct/product_detail/product_id/' + str( goods_id) # 开始常规requests有数据, 后面无数据, 改用phantomjs # body = MyRequests.get_url_body(url=tmp_url, headers=self.headers, had_referer=True) my_phantomjs = MyPhantomjs() body = my_phantomjs.use_phantomjs_to_get_url_body(url=tmp_url) try: del my_phantomjs except: pass # print(body) if body == '': print('获取到的body为空str!') self.result_data = {} return {} data = {} try: data['title'] = Selector( text=body).css('div.zy_info_rt h3::text').extract_first() if data['title'] == '': print('title为空!') raise Exception data['sub_title'] = '' data['shop_name'] = Selector(text=body).css( 'div.other.ft14.clearfix label b::text').extract_first() # print(data['shop_name']) # 获取所有示例图片 all_img_url = [{ 'img_url': item } for item in list( Selector( text=body).css('p.s_img label img::attr("src")').extract()) ] # pprint(all_img_url) data['all_img_url'] = all_img_url ''' 获取p_info ''' # 由于获取的是pc端的对应没有p_info data['p_info'] = [] ''' 获取商品的div_desc ''' div_desc = Selector(text=body).css('div.s_two').extract_first() # print(div_desc) if div_desc == '': print('div_desc为空!请检查!') raise Exception data['div_desc'] = div_desc ''' 获取detail_name_list ''' detail_name_list = Selector(text=body).css( 'div.info-wd.bd-red dl.detail dt::text').extract() if len(detail_name_list) <= 1: detail_name_list = [] else: detail_name_list = [{ 'spec_name': item } for item in detail_name_list[:-1]] # print(detail_name_list) data['detail_name_list'] = detail_name_list # 商品价格(原价)跟淘宝价格 taobao_price = Selector( text=body).css('dl.detail p.price b::text').extract_first() price = Selector(text=body).css( 'dl.detail dd em.yjprice::text').extract_first() # print(taobao_price) # print(price) try: # 后面有'*' 是为了避免有价格为整数不是浮点类型的 taobao_price = re.compile(r'(\d+\.{0,1}\d*)').findall( taobao_price)[0] price = re.compile(r'(\d+\.{0,1}\d*)').findall(price)[0] except IndexError: print('获取price失败,请检查!') raise IndexError if taobao_price == '' or price == '': print('获取到的taobao_price或者price为空值出错, 请检查!') raise Exception taobao_price = Decimal(taobao_price).__round__(2) price = Decimal(price).__round__(2) # print('商品促销价为: ', taobao_price, ' 商品原价为: ', price) data['price'] = price data['taobao_price'] = taobao_price ''' 获取每个规格对应价格跟规格以及其库存 ''' price_info_list = self.get_price_info_list(detail_name_list, body, price, taobao_price) # pprint(price_info_list) if price_info_list == '': raise Exception else: data['price_info_list'] = price_info_list ''' 是否卖光 ''' all_stock = int( Selector(text=body).css( 'dl.detail dd label em::text').extract_first()) if all_stock == 0: is_delete = 1 else: is_delete = 0 data['is_delete'] = is_delete except Exception as e: print('遇到错误: ', e) self.result_data = {} return {} if data != {}: # pprint(data) self.result_data = data return data else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {}
class PinduoduoParse(object): def __init__(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'mobile.yangkeduo.com', 'User-Agent': HEADERS[randint(0, 34)], # 随机一个请求头 # 'Cookie': 'api_uid=rBQh+FoXerAjQWaAEOcpAg==;', # 分析发现需要这个cookie值 } self.result_data = {} # self.set_cookies_key_api_uid() # 设置cookie中的api_uid的值 self.my_phantomjs = MyPhantomjs() def get_goods_data(self, goods_id): ''' 模拟构造得到data的url :param goods_id: :return: data 类型dict ''' if goods_id == '': self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: tmp_url = 'http://mobile.yangkeduo.com/goods.html?goods_id=' + str(goods_id) print('------>>>| 得到的商品手机版地址为: ', tmp_url) ''' 1.采用requests,由于经常返回错误的body(即requests.get返回的为空的html), So pass ''' # body = MyRequests.get_url_body(url=tmp_url, headers=self.headers, had_referer=True) ''' 2.采用phantomjs来获取 ''' body = self.my_phantomjs.use_phantomjs_to_get_url_body(url=tmp_url) if body == '': print('body中re匹配到的data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} data = re.compile(r'window.rawData= (.*?);</script>').findall(body) # 贪婪匹配匹配所有 if data != []: data = data[0] try: data = json.loads(data) except Exception: self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} # pprint(data) try: data['goods'].pop('localGroups') data['goods'].pop('mallService') data.pop('reviews') # 评价信息跟相关统计 except: pass # pprint(data) ''' 处理detailGallery转换成能被html显示页面信息 ''' detail_data = data.get('goods', {}).get('detailGallery', []) tmp_div_desc = '' if detail_data != []: for index in range(0, len(detail_data)): if index == 0: # 跳过拼多多的提示 pass else: tmp = '' tmp_img_url = detail_data[index].get('url') tmp = r'<img src="{}" style="height:auto;width:100%;"/>'.format(tmp_img_url) tmp_div_desc += tmp detail_data = '<div>' + tmp_div_desc + '</div>' else: detail_data = '' # print(detail_data) try: data['goods'].pop('detailGallery') # 删除图文介绍的无第二次用途的信息 except: pass data['div_desc'] = detail_data # pprint(data) self.result_data = data return data else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} def deal_with_data(self): ''' 处理result_data, 返回需要的信息 :return: 字典类型 ''' data = self.result_data if data != {}: # 店铺名称 if data.get('mall') is not None: shop_name = data.get('mall', {}).get('mallName', '') else: shop_name = '' # 掌柜 account = '' # 商品名称 title = data.get('goods', {}).get('goodsName', '') # 子标题 sub_title = '' # 商品库存 # 商品标签属性对应的值 # 商品标签属性名称 if data.get('goods', {}).get('skus', []) == []: detail_name_list = [] else: if data.get('goods', {}).get('skus', [])[0].get('specs') == []: detail_name_list = [] else: detail_name_list = [{'spec_name': item.get('spec_key')} for item in data.get('goods', {}).get('skus', [])[0].get('specs')] # print(detail_name_list) # 要存储的每个标签对应规格的价格及其库存 skus = data.get('goods', {}).get('skus', []) # pprint(skus) price_info_list = [] if skus != []: # ** 注意: 拼多多商品只有一个规格时skus也不会为空的 ** for index in range(0, len(skus)): tmp = {} price = skus[index].get('groupPrice', '') # 拼团价 normal_price = skus[index].get('normalPrice', '') # 单独购买价格 spec_value = [item.get('spec_value') for item in data.get('goods', {}).get('skus', [])[index].get('specs')] spec_value = '|'.join(spec_value) img_url = skus[index].get('thumbUrl', '') rest_number = skus[index].get('quantity', 0) # 剩余库存 is_on_sale = skus[index].get('isOnSale', 0) # 用于判断是否在特价销售,1:特价 0:原价(normal_price) tmp['spec_value'] = spec_value tmp['detail_price'] = price tmp['normal_price'] = normal_price tmp['img_url'] = img_url if rest_number <= 0: tmp['rest_number'] = 0 else: tmp['rest_number'] = rest_number tmp['is_on_sale'] = is_on_sale price_info_list.append(tmp) if price_info_list == []: print('price_info_list为空值') return {} # 商品价格和淘宝价 tmp_price_list = sorted([round(float(item.get('detail_price', '')), 2) for item in price_info_list]) price = tmp_price_list[-1] # 商品价格 taobao_price = tmp_price_list[0] # 淘宝价 if detail_name_list == []: print('## detail_name_list为空值 ##') price_info_list = [] # print('最高价为: ', price) # print('最低价为: ', taobao_price) # print(len(price_info_list)) # pprint(price_info_list) # 所有示例图片地址 all_img_url = [{'img_url': item} for item in data.get('goods', {}).get('topGallery', [])] # print(all_img_url) # 详细信息标签名对应属性 tmp_p_value = re.compile(r'\n').sub('', data.get('goods', {}).get('goodsDesc', '')) tmp_p_value = re.compile(r'\t').sub('', tmp_p_value) tmp_p_value = re.compile(r' ').sub('', tmp_p_value) p_info = [{'p_name': '商品描述', 'p_value': tmp_p_value}] # print(p_info) # 总销量 all_sell_count = data.get('goods', {}).get('sales', 0) # div_desc div_desc = data.get('div_desc', '') # 商品销售时间区间 schedule = [{ 'begin_time': self.timestamp_to_regulartime(data.get('goods', {}).get('groupTypes', [])[0].get('startTime')), 'end_time': self.timestamp_to_regulartime(data.get('goods', {}).get('groupTypes', [])[0].get('endTime')), }] # pprint(schedule) # 用于判断商品是否已经下架 is_delete = 0 result = { 'shop_name': shop_name, # 店铺名称 'account': account, # 掌柜 'title': title, # 商品名称 'sub_title': sub_title, # 子标题 # 'shop_name_url': shop_name_url, # 店铺主页地址 'price': price, # 商品价格 'taobao_price': taobao_price, # 淘宝价 # 'goods_stock': goods_stock, # 商品库存 'detail_name_list': detail_name_list, # 商品标签属性名称 # 'detail_value_list': detail_value_list,# 商品标签属性对应的值 'price_info_list': price_info_list, # 要存储的每个标签对应规格的价格及其库存 'all_img_url': all_img_url, # 所有示例图片地址 'p_info': p_info, # 详细信息标签名对应属性 'div_desc': div_desc, # div_desc 'schedule': schedule, # 商品开卖时间和结束开卖时间 'all_sell_count': all_sell_count, # 商品总销售量 'is_delete': is_delete # 用于判断商品是否已经下架 } # pprint(result) # print(result) # wait_to_send_data = { # 'reason': 'success', # 'data': result, # 'code': 1 # } # json_data = json.dumps(wait_to_send_data, ensure_ascii=False) # print(json_data) return result else: print('待处理的data为空的dict, 该商品可能已经转移或者下架') return {} def to_right_and_update_data(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id ''' 时区处理,时间处理到上海时间 ''' tz = pytz.timezone('Asia/Shanghai') # 创建时区对象 now_time = datetime.datetime.now(tz) # 处理为精确到秒位,删除时区信息 now_time = re.compile(r'\..*').sub('', str(now_time)) # 将字符串类型转换为datetime类型 now_time = datetime.datetime.strptime(now_time, '%Y-%m-%d %H:%M:%S') tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 商品子标题 tmp['link_name'] = '' # 卖家姓名 tmp['account'] = data_list['account'] # 掌柜名称 # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['price_info'] = [] # 价格信息 tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get('price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') # 采集的来源地 # tmp['site_id'] = 13 # 采集来源地(拼多多常规商品) tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 tmp['my_shelf_and_down_time'] = data_list.get('my_shelf_and_down_time') tmp['delete_time'] = data_list.get('delete_time') tmp['all_sell_count'] = str(data_list.get('all_sell_count')) pipeline.update_pinduoduo_table(item=tmp) def insert_into_pinduoduo_xianshimiaosha_table(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id tmp['spider_url'] = data_list['spider_url'] # 商品地址 tmp['username'] = data_list['username'] # 操作人员username ''' 时区处理,时间处理到上海时间 ''' tz = pytz.timezone('Asia/Shanghai') # 创建时区对象 now_time = datetime.datetime.now(tz) # 处理为精确到秒位,删除时区信息 now_time = re.compile(r'\..*').sub('', str(now_time)) # 将字符串类型转换为datetime类型 now_time = datetime.datetime.strptime(now_time, '%Y-%m-%d %H:%M:%S') tmp['deal_with_time'] = now_time # 操作时间 tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 商品子标题 # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get('price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') tmp['stock_info'] = data_list.get('stock_info') tmp['miaosha_time'] = data_list.get('miaosha_time') tmp['miaosha_begin_time'] = data_list.get('miaosha_begin_time') tmp['miaosha_end_time'] = data_list.get('miaosha_end_time') # 采集的来源地 tmp['site_id'] = 16 # 采集来源地(卷皮秒杀商品) tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 # print('is_delete=', tmp['is_delete']) # print('------>>>| 待存储的数据信息为: |', tmp) print('------>>>| 待存储的数据信息为: ', tmp.get('goods_id')) pipeline.insert_into_pinduoduo_xianshimiaosha_table(item=tmp) def to_update_pinduoduo_xianshimiaosha_table(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id ''' 时区处理,时间处理到上海时间 ''' tz = pytz.timezone('Asia/Shanghai') # 创建时区对象 now_time = datetime.datetime.now(tz) # 处理为精确到秒位,删除时区信息 now_time = re.compile(r'\..*').sub('', str(now_time)) # 将字符串类型转换为datetime类型 now_time = datetime.datetime.strptime(now_time, '%Y-%m-%d %H:%M:%S') tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get('price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') tmp['stock_info'] = data_list.get('stock_info') tmp['miaosha_time'] = data_list.get('miaosha_time') tmp['miaosha_begin_time'] = data_list.get('miaosha_begin_time') tmp['miaosha_end_time'] = data_list.get('miaosha_end_time') tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 # print('is_delete=', tmp['is_delete']) # print('------>>> | 待存储的数据信息为: |', tmp) print('------>>>| 待存储的数据信息为: |', tmp.get('goods_id')) pipeline.update_pinduoduo_xianshimiaosha_table(tmp) def set_cookies_key_api_uid(self): ''' 给headers增加一个cookie, 里面有个key名字为api_uid :return: ''' # 设置代理ip ip_object = MyIpPools() self.proxies = ip_object.get_proxy_ip_from_ip_pool() # {'http': ['xx', 'yy', ...]} self.proxy = self.proxies['http'][randint(0, len(self.proxies) - 1)] tmp_proxies = { 'http': self.proxy, } # 得到cookie中的key名为api_uid的值 host_url = 'http://mobile.yangkeduo.com' try: response = requests.get(host_url, headers=self.headers, proxies=tmp_proxies, timeout=10) # 在requests里面传数据,在构造头时,注意在url外头的&xxx=也得先构造 api_uid = response.cookies.get('api_uid') # print(response.cookies.items()) # if api_uid is None: # api_uid = 'rBQh+FoXerAjQWaAEOcpAg==' self.headers['Cookie'] = 'api_uid=' + str(api_uid) + ';' # print(api_uid) except Exception: print('requests.get()请求超时....') pass def timestamp_to_regulartime(self, timestamp): ''' 将时间戳转换成时间 ''' # 利用localtime()函数将时间戳转化成localtime的格式 # 利用strftime()函数重新格式化时间 # 转换成localtime time_local = time.localtime(timestamp) # 转换成新的时间格式(2016-05-05 20:28:54) dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) return dt def get_goods_id_from_url(self, pinduoduo_url): ''' 得到goods_id :param pinduoduo_url: :return: goods_id (类型str) ''' is_pinduoduo_url = re.compile(r'http://mobile.yangkeduo.com/goods.html.*?').findall(pinduoduo_url) if is_pinduoduo_url != []: if re.compile(r'http://mobile.yangkeduo.com/goods.html\?.*?goods_id=(\d+).*?').findall(pinduoduo_url) != []: tmp_pinduoduo_url = re.compile(r'http://mobile.yangkeduo.com/goods.html\?.*?goods_id=(\d+).*?').findall(pinduoduo_url)[0] if tmp_pinduoduo_url != '': goods_id = tmp_pinduoduo_url else: # 只是为了在pycharm里面测试,可以不加 pinduoduo_url = re.compile(r';').sub('', pinduoduo_url) goods_id = re.compile(r'http://mobile.yangkeduo.com/goods.html\?.*?goods_id=(\d+).*?').findall(pinduoduo_url)[0] print('------>>>| 得到的拼多多商品id为:', goods_id) return goods_id else: pass else: print('拼多多商品url错误, 非正规的url, 请参照格式(http://mobile.yangkeduo.com/goods.html)开头的...') return '' def __del__(self): try: del self.my_phantomjs except: pass gc.collect()
class MoGuJiePinTuanRealTimesUpdate(object): def __init__(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', # 'Accept-Encoding:': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'list.mogujie.com', # 'Referer': 'https://pintuan.mogujie.com/ptpt/app/pd?acm=3.mce.1_10_1fvsk.51827.0.mUTadqIzS9Pbg.m_370494-pos_2-mf_4537_796033&ptp=m1._mf1_1239_4537._keyword_51827.0.xLt0G92', 'User-Agent': HEADERS[randint(0, 34)], # 随机一个请求头 } def run_forever(self): ''' 实时更新数据 :return: ''' tmp_sql_server = SqlServerMyPageInfoSaveItemPipeline() try: result = list(tmp_sql_server.select_mogujie_pintuan_all_goods_id()) except TypeError: print('TypeError错误, 原因数据库连接失败...(可能维护中)') result = None if result is None: pass else: print('------>>> 下面是数据库返回的所有符合条件的goods_id <<<------') print(result) print('--------------------------------------------------------') print('即将开始实时更新数据, 请耐心等待...'.center(100, '#')) index = 1 self.my_phantomjs = MyPhantomjs() for item in result: # 实时更新数据 pintuan_end_time = json.loads(item[1]).get('end_time') pintuan_end_time = int( str( time.mktime( time.strptime(pintuan_end_time, '%Y-%m-%d %H:%M:%S')))[0:10]) # print(miaosha_end_time) data = {} mogujie_pintuan = MoGuJieParse() if index % 8 == 0: try: del self.my_phantomjs except: pass gc.collect() self.my_phantomjs = MyPhantomjs() if index % 50 == 0: # 每50次重连一次,避免单次长连无响应报错 print('正在重置,并与数据库建立新连接中...') tmp_sql_server = SqlServerMyPageInfoSaveItemPipeline() print('与数据库的新连接成功建立...') if tmp_sql_server.is_connect_success: if self.is_recent_time(pintuan_end_time) == 0: tmp_sql_server.delete_mogujie_pintuan_expired_goods_id( goods_id=item[0]) print( '过期的goods_id为(%s)' % item[0], ', 拼团开始时间为(%s), 删除成功!' % json.loads(item[1]).get('begin_time')) elif self.is_recent_time(pintuan_end_time) == 2: # break # 跳出循环 pass # 此处应该是pass,而不是break,因为数据库传回的goods_id不都是按照顺序的 else: # 返回1,表示在待更新区间内 print( '------>>>| 正在更新的goods_id为(%s) | --------->>>@ 索引值为(%d)' % (item[0], index)) data['goods_id'] = item[0] tmp_url = 'http://list.mogujie.com/search?page={0}&fcid={1}&algoKey=pc_tuan_book_pop&cKey=pc-tuan'.format( item[3], item[2]) # print(tmp_url) # requests请求不到数据,涉及证书认证,直接用phantomjs # body = MyRequests.get_url_body(url=tmp_url, headers=self.headers, had_referer=True) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url) # print(body) if body == '': print('获取到的body为空值! 此处跳过') else: try: body = re.compile( r'<pre.*?>(.*?)</pre>').findall(body)[0] tmp_data = json.loads(body) # pprint(tmp_data) except: print('json.loads转换body时出错, 请检查') tmp_data = {} if tmp_data.get('result', {}).get('wall', {}).get('docs', []) == []: print('得到的docs为[]!') print('该商品已被下架限时秒杀活动,此处将其删除') tmp_sql_server.delete_mogujie_pintuan_expired_goods_id( goods_id=item[0]) print('下架的goods_id为(%s)' % item[0], ', 删除成功!') pass else: tmp_item_list = tmp_data.get('result', {}).get( 'wall', {}).get('docs', []) # print(tmp_item_list) # pprint(tmp_item_list) begin_time_timestamp = int( time.time()) # 开始拼团的时间戳 item_list = [{ 'goods_id': item.get('tradeItemId', ''), 'pintuan_time': { 'begin_time': self.timestamp_to_regulartime( timestamp=begin_time_timestamp), 'end_time': self.timestamp_to_regulartime( self.get_pintuan_end_time( begin_time_timestamp, item.get('leftTimeOrg', ''))), }, 'all_sell_count': str(item.get('salesVolume', 0)), } for item in tmp_item_list] # print(item_list) pintuan_goods_all_goods_id = [ item_1.get('goods_id', '') for item_1 in item_list ] # print(pintuan_goods_all_goods_id) ''' 内部已经下架的(内部下架的其实并未真实下架,还在卖的,所以我就更新其商品信息数据,不更新上下架时间) ''' if item[0] not in pintuan_goods_all_goods_id: # print('该商品已被下架限时秒杀活动,此处将其删除') # tmp_sql_server.delete_mogujie_pintuan_expired_goods_id(goods_id=item[0]) # print('下架的goods_id为(%s)' % item[0], ', 删除成功!') # pass mogujie_pintuan.get_goods_data( goods_id=item[0]) goods_data = mogujie_pintuan.deal_with_data( ) if goods_data == {}: pass else: # 规范化 print('+++ 内部下架,其实还在售卖的商品更新') tmp_price_info_list = goods_data[ 'price_info_list'] price_info_list = [{ 'spec_value': item_4.get('spec_value'), 'pintuan_price': item_4.get('detail_price'), 'normal_price': item_4.get('normal_price'), 'img_url': item_4.get('img_url'), 'rest_number': item_4.get('rest_number'), } for item_4 in tmp_price_info_list] goods_data['goods_id'] = item[0] goods_data[ 'price_info_list'] = price_info_list # pprint(goods_data) # print(goods_data) mogujie_pintuan.update_mogujie_pintuan_table_2( data=goods_data, pipeline=tmp_sql_server) sleep(MOGUJIE_SLEEP_TIME) # 放慢速度 else: # 未下架的 for item_2 in item_list: if item_2.get('goods_id', '') == item[0]: mogujie_pintuan.get_goods_data( goods_id=item[0]) goods_data = mogujie_pintuan.deal_with_data( ) if goods_data == {}: pass else: # 规范化 tmp_price_info_list = goods_data[ 'price_info_list'] price_info_list = [ { 'spec_value': item_4.get( 'spec_value'), 'pintuan_price': item_4.get( 'detail_price'), 'normal_price': item_4.get( 'normal_price'), 'img_url': item_4.get('img_url'), 'rest_number': item_4.get( 'rest_number'), } for item_4 in tmp_price_info_list ] goods_data['goods_id'] = item[ 0] goods_data[ 'price_info_list'] = price_info_list goods_data[ 'pintuan_time'] = item_2.get( 'pintuan_time', {}) goods_data['pintuan_begin_time'], goods_data[ 'pintuan_end_time'] = self.get_pintuan_begin_time_and_pintuan_end_time( pintuan_time=goods_data[ 'pintuan_time']) goods_data[ 'all_sell_count'] = item_2.get( 'all_sell_count', '') # pprint(goods_data) # print(goods_data) mogujie_pintuan.update_mogujie_pintuan_table( data=goods_data, pipeline=tmp_sql_server) sleep( MOGUJIE_SLEEP_TIME) # 放慢速度 else: pass else: print('数据库连接失败,此处跳过!') pass index += 1 gc.collect() print('全部数据更新完毕'.center(100, '#')) # sleep(60*60) if get_shanghai_time().hour == 0: # 0点以后不更新 sleep(60 * 60 * 5.5) else: sleep(5) gc.collect() def get_pintuan_begin_time_and_pintuan_end_time(self, pintuan_time): ''' 返回拼团开始和结束时间 :param pintuan_time: :return: tuple pintuan_begin_time, pintuan_end_time ''' pintuan_begin_time = pintuan_time.get('begin_time', '') pintuan_end_time = pintuan_time.get('end_time', '') # 将字符串转换为datetime类型 pintuan_begin_time = datetime.datetime.strptime( pintuan_begin_time, '%Y-%m-%d %H:%M:%S') pintuan_end_time = datetime.datetime.strptime(pintuan_end_time, '%Y-%m-%d %H:%M:%S') return pintuan_begin_time, pintuan_end_time def timestamp_to_regulartime(self, timestamp): ''' 将时间戳转换成时间 ''' # 利用localtime()函数将时间戳转化成localtime的格式 # 利用strftime()函数重新格式化时间 # 转换成localtime time_local = time.localtime(int(timestamp)) # print(time_local) # 转换成新的时间格式(2016-05-05 20:28:54) dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) return dt def get_pintuan_end_time(self, begin_time, left_time): ''' 处理并得到拼团结束时间 :param begin_time: 秒杀开始时间戳 :param left_time: 剩余时间字符串 :return: end_time 时间戳(int) ''' # 'leftTimeOrg': '6天13小时' # 'leftTimeOrg': '13小时57分' had_day = re.compile(r'天').findall(left_time) had_hour = re.compile(r'小时').findall(left_time) had_min = re.compile(r'分').findall(left_time) tmp = re.compile(r'\d+').findall(left_time) if had_day != [] and had_hour != []: # left_time 格式为 '6天13小时' day, hour, min = int(tmp[0]), int(tmp[1]), 0 elif had_day == [] and had_hour != []: # left_time 格式为 '13小时57分' day, hour, min = 0, int(tmp[0]), int(tmp[1]) elif had_day == [] and had_hour == []: # left_time 格式为 '36分' print('left_time = ', left_time) day, hour, min = 0, 0, int(tmp[0]) else: # 无天, 小时, 分 print('day, hour, min = 0, 0, 0', 'left_time = ', left_time) day, hour, min = 0, 0, 0 left_end_time_timestamp = \ day * 24 * 60 * 60 + \ hour * 60 * 60 + \ min * 60 return begin_time + left_end_time_timestamp def is_recent_time(self, timestamp): ''' 判断是否在指定的日期差内 :param timestamp: 时间戳 :return: 0: 已过期恢复原价的 1: 待更新区间内的 2: 未来时间的 ''' time_1 = int(timestamp) time_2 = int(time.time()) # 当前的时间戳 diff_time = time_1 - time_2 if diff_time < -86400: # (为了后台能同步下架)所以设置为 24个小时 # if diff_time < 0: # (原先的时间)结束时间 与当前时间差 <= 0 return 0 # 已过期恢复原价的 elif diff_time > 0: return 1 # 表示是昨天跟今天的也就是待更新的 else: # 表示过期但是处于等待的数据不进行相关先删除操作(等<=24小时时再2删除) return 2 def __del__(self): try: del self.my_phantomjs except: pass gc.collect()
class ALi1688CommentParse(object): ''' 阿里1688评论抓取解析类 ''' def __init__(self, logger=None): super().__init__() self.result_data = {} self.msg = '' self._set_headers() self._set_logger(logger) self.my_phantomjs = MyPhantomjs() # 可动态执行的代码 self._exec_code = ''' self.driver.find_element_by_css_selector('div.tab-item.filter:nth-child(2)').click() _text = str(self.driver.find_element_by_css_selector('div.tab-item.filter:nth-child(2)').text) print(_text) # if _text == '四五星(0)': assert _text != '四五星(0)', 'my assert error!' # 通过断言来跳过执行下面的代码 sleep(2.5) # 向下滚动10000像素 js = 'document.body.scrollTop=10000' self.driver.execute_script(js) sleep(4) ''' self._page_sleep_time = 1.2 def _get_comment_data(self, goods_id): if goods_id == '': self.result_data = {} return {} self.my_lg.info('------>>>| 待处理的goods_id为: %s' % str(goods_id)) # 原先采用phantomjs, 改用pc端抓包到的接口(speed slow, give up) tmp_url = 'https://m.1688.com/page/offerRemark.htm?offerId=' + str( goods_id) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url, exec_code=self._exec_code) # self.my_lg.info(str(body)) if body == '': self.result_data = {} self.my_lg.error('该地址的body为空值, 出错地址: ' + tmp_url) return {} _html_comment_list = list( Selector(text=body).css('div.remark-item').extract()) if _html_comment_list != []: _comment_list = [] for index, item in enumerate(_html_comment_list): if index > 25: # 就取前25条评论信息 break buyer_name = str( Selector( text=item).css('span.member::text').extract_first()) quantify = str( Selector( text=item).css('span.amount::text').extract_first()) try: quantify = int(re.compile(r'\d+').findall(quantify)[0]) except IndexError: self.my_lg.error('获取quantify时索引异常! 出错地址: ' + tmp_url) self.result_data = {} return {} comment_date = str( Selector( text=item).css('div.date span::text').extract_first()) comment_date = self._get_comment_date( comment_date) # str '2017-01-25 17:06:00' tmp_sku_info = str( Selector(text=item).css('div.date::text').extract_first()) comment = [{ 'comment': self._wash_comment( str( Selector(text=item).css( 'div.bd::text').extract_first())), 'comment_date': comment_date, # 评论创建日期 'sku_info': re.compile(r'<span.*?</span>').sub( '', tmp_sku_info), # 购买的商品规格 'img_url_list': [], 'star_level': randint(3, 5), # 几星好评 'video': '', }] _ = { 'buyer_name': buyer_name, # 买家昵称 'comment': comment, # 评论内容 'quantify': quantify, # 购买数量 'head_img': '', # 用户头像 'append_comment': {}, # 追评 } _comment_list.append(_) _t = datetime.datetime.now() _r = CommentItem() _r['goods_id'] = str(goods_id) _r['create_time'] = _t _r['modify_time'] = _t _r['_comment_list'] = _comment_list self.result_data = _r # pprint(self.result_data) return self.result_data else: self.my_lg.error('该商品的comment为空list! 出错地址: ' + tmp_url) self.result_data = {} return {} # 下面是模拟手机端好评接口 # _comment_list = [] # for page_num in range(1, 4): # tmp_url = 'https://m.1688.com/page/offerRemark.htm' # _params = self._set_params(goods_id=goods_id, page_num=page_num) # # body = MyRequests.get_url_body(url=tmp_url, headers=self.headers, params=_params) # # _url = self._set_url(url=tmp_url, params=_params) # # print(_url) # # self.my_lg.info(str(body)) # if body == '': # self.result_data = {} # self.my_lg.error('该地址的body为空值, 出错goods_id: ' + goods_id) # return {} # # data = self.json_str_2_dict(json_str=body).get('data', {}).get('model', []) # pprint(data) # try: # for item in data: # buyer_name = item.get('member', '') # comment = [{ # 'comment': i.get('remarkContent', ''), # 'comment_date': string_to_datetime(i.get('remarkTime', '')), # 评论日期 # 'star_level': i.get('starLevel', 5), # 'sku_info': '', # 购买的商品规格(pc端1688商品没有规格) # 'img_url_list': [], # } for i in item.get('rateItem', [])] # quantify = item.get('quantity', 1) # 购买数量 # # _ = { # 'buyer_name': buyer_name, # 买家昵称 # 'comment': comment, # 评论内容 # 'quantify': quantify # 购买数量 # } # _comment_list.append(_) # # except Exception as e: # self.result_data = {} # self.my_lg.error('出错商品goods_id: ' + goods_id) # self.my_lg.exception(e) # return {} # sleep(self._page_sleep_time) # # pprint(_comment_list) # self.result_data = { # 'goods_id': str(goods_id), # 'modify_time': datetime.datetime.now(), # '_comment_list': _comment_list, # } # pprint(self.result_data) # return self.result_data def _wash_comment(self, comment: str): ''' 清洗comment :param comment: :return: ''' comment = re.compile('阿里巴巴').sub('', comment) comment = re.compile('1688|合作|阿里').sub('', comment) return comment def _set_headers(self): self.headers = { # 下面的ali-ss为必要字段 'cookie': 'ali-ss=eyJ1c2VySWQiOm51bGwsImxvZ2luSWQiOm51bGwsInNpZCI6bnVsbCwiZWNvZGUiOm51bGwsIm1lbWJlcklkIjpudWxsLCJzZWNyZXQiOiI5WmZucV96VDl6NDhTOTg4WkNsaFpxSEwiLCJfZXhwaXJlIjoxNTI0MTE5MzI3NDQ5LCJfbWF4QWdlIjo4NjQwMDAwMH0=; ', 'accept-language': 'zh-CN,zh;q=0.9', 'user-agent': HEADERS[randint(0, len(HEADERS) - 1)], 'accept': 'application/json, text/javascript, */*; q=0.01', 'referer': 'https://m.1688.com/page/offerRemark.htm?offerId=42735065607', 'x-requested-with': 'XMLHttpRequest', } def _set_logger(self, logger): if logger is None: self.my_lg = set_logger(log_file_name=MY_SPIDER_LOGS_PATH + '/阿里1688/comment/' + str(get_shanghai_time())[0:10] + '.txt', console_log_level=INFO, file_log_level=ERROR) else: self.my_lg = logger def _set_url(self, url, params): ''' 得到待抓取的api接口地址 :param url: :param params: :return: str ''' _ = [item[0] + '=' + str(item[1]) for item in params] return url + '?' + '&'.join(_) def _set_params(self, goods_id, page_num: int): ''' 设置params :param goods_id: :param page_num: :return: ''' data = json.dumps({ 'data': { 'offerId': goods_id, # 'receiveUserId': 2318703732, 'starLevel': 7, 'itemId': int(goods_id), 'bizType': 'trade', 'page': page_num, 'pageSize': 5, } }) params = ( ('_csrf', 'xMrEnTz7-VByOlidz0AzkXFg_ifMZBv6bCA0'), ('__wing_navigate_type', 'view'), ('__wing_navigate_url', 'detail:modules/offerRemarkList/view'), # ('__wing_navigate_options', '{"data":{"offerId":"42735065607","receiveUserId":2318703732,"starLevel":7,"itemId":42735065607,"bizType":"trade","page":1,"pageSize":5}}'), ('__wing_navigate_options', data), ('_', str(time.time().__round__()) + str(randint(100, 999))), ) return params def json_str_2_dict(self, json_str): ''' json字符串转dict :param json_str: :return: ''' try: data = json.loads(json_str) except: self.my_lg.error('json.loads转换json_str时出错!请检查!') data = {} return data def _get_comment_date(self, comment_date): ''' 得到datetime类型的时间 :param comment_date: eg: 2017-12-04 :return: datetime ''' _ = str(randint(0, 23)) if len(_) == 1: _hour = '0' + _ else: _hour = _ _ = str(randint(0, 59)) if len(_) == 1: _min = '0' + _ else: _min = _ _ = str(randint(0, 59)) if len(_) == 1: _s = '0' + _ else: _s = _ comment_date = comment_date + ' ' + _hour + ':' + _min + ':' + _s return comment_date def __del__(self): try: del self.my_phantomjs del self.my_lg del self.msg except: pass gc.collect()
class ALi1688CommentParse(object): def __init__(self, logger=None): super().__init__() self.result_data = {} self.msg = '' if logger is None: self.my_lg = set_logger(log_file_name=MY_SPIDER_LOGS_PATH + '/阿里1688/comment/' + str(get_shanghai_time())[0:10] + '.txt', console_log_level=INFO, file_log_level=ERROR) else: self.my_lg = logger self.my_phantomjs = MyPhantomjs() # 可动态执行的代码 self._exec_code = ''' self.driver.find_element_by_css_selector('div.tab-item.filter:nth-child(2)').click() sleep(1.5) # 向下滚动10000像素 js = 'document.body.scrollTop=10000' self.driver.execute_script(js) sleep(3) ''' self.headers = { 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'user-agent': HEADERS[randint(0, len(HEADERS) - 1)], 'accept': 'image/webp,image/apng,image/*,*/*;q=0.8', 'referer': 'https://detail.1688.com/offer/45579899125.html', } self.page_size = '30' def _get_comment_data(self, goods_id): if goods_id == '': self.result_data = {} return {} ''' 原先采用phantomjs, 改用pc端抓包到的接口 ''' tmp_url = 'https://m.1688.com/page/offerRemark.htm?offerId=' + str( goods_id) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url, exec_code=self._exec_code) # self.my_lg.info(str(body)) if body == '': self.result_data = {} self.my_lg.error('该地址的body为空值, 出错地址: ' + tmp_url) return {} _html_comment_list = list( Selector(text=body).css('div.remark-item').extract()) if _html_comment_list != []: _comment_list = [] for index, item in enumerate(_html_comment_list): if index > 25: # 就取前25条评论信息 break buyer_name = str( Selector( text=item).css('span.member::text').extract_first()) quantify = str( Selector( text=item).css('span.amount::text').extract_first()) try: quantify = int(re.compile(r'\d+').findall(quantify)[0]) except IndexError: self.my_lg.error('获取quantify时索引异常! 出错地址: ' + tmp_url) self.result_data = {} return {} comment_date = str( Selector( text=item).css('div.date span::text').extract_first()) comment_date = self._get_comment_date( comment_date) # str '2017-01-25 17:06:00' tmp_sku_info = str( Selector(text=item).css('div.date::text').extract_first()) comment = [{ 'comment': str( Selector( text=item).css('div.bd::text').extract_first()), 'comment_date': comment_date, # 评论创建日期 'sku_info': re.compile(r'<span.*?</span>').sub( '', tmp_sku_info), # 购买的商品规格 'img_url_list': [], 'star_level': randint(3, 5), # 几星好评 'video': '', }] _ = { 'buyer_name': buyer_name, # 买家昵称 'comment': comment, # 评论内容 'quantify': quantify, # 购买数量 'head_img': '', # 用户头像 } _comment_list.append(_) self.result_data = { 'goods_id': str(goods_id), 'modify_time': datetime.datetime.now(), '_comment_list': _comment_list, } pprint(self.result_data) return self.result_data else: self.my_lg.error('该商品的comment为空list! 出错地址: ' + tmp_url) self.result_data = {} return {} # 下面是模拟pc端接口的 # tmp_url = 'https://rate.1688.com/remark/offerDetail/rates.json' # _params = self._set_params(goods_id=goods_id) # # # 常规requests获取不到数据改用phantomjs # # body = MyRequests.get_url_body(url=tmp_url, headers=self.headers, params=_params) # # _url = self._set_url(url=tmp_url, params=_params) # print(_url) # body = self.my_phantomjs.use_phantomjs_to_get_url_body(url=_url) # self.my_lg.info(str(body)) # if body == '': # self.result_data = {} # self.my_lg.error('该地址的body为空值, 出错goods_id: ' + goods_id) # return {} # try: # body = re.compile('<pre.*?>(.*)</pre>').findall(body)[0] # except IndexError: # self.result_data = {} # self.my_lg.error('re筛选body为空[], 出错goods_id: ' + goods_id) # return {} # # data = self.json_str_2_dict(json_str=body).get('data', {}).get('rates', []) # # pprint(data) # _comment_list = [] # try: # for item in data: # buyer_name = item.get('member', '') # comment = [{ # 'comment': i.get('remarkContent', ''), # 'comment_date': string_to_datetime(i.get('remarkTime', '')), # 评论日期 # 'star_level': i.get('starLevel', 5), # 'sku_info': '', # 购买的商品规格(pc端1688商品没有规格) # 'img_url_list': [], # } for i in item.get('rateItem', [])] # quantify = item.get('quantity', 1) # 购买数量 # # _ = { # 'buyer_name': buyer_name, # 买家昵称 # 'comment': comment, # 评论内容 # 'quantify': quantify # 购买数量 # } # _comment_list.append(_) # # except Exception as e: # self.result_data = {} # self.my_lg.error('出错商品goods_id: ' + goods_id) # self.my_lg.exception(e) # return {} # # self.result_data = { # 'goods_id': str(goods_id), # 'modify_time': datetime.datetime.now(), # '_comment_list': _comment_list, # } # pprint(self.result_data) # return self.result_data def _set_url(self, url, params): ''' 得到待抓取的api接口地址 :param url: :param params: :return: str ''' _ = [item[0] + '=' + str(item[1]) for item in params] return url + '?' + '&'.join(_) def _set_params(self, goods_id): ''' 设置params :param goods_id: :return: ''' params = ( ('_input_charset', 'GBK'), ('offerId', goods_id), ('page', '1'), ('pageSize', self.page_size), # 一个页面返回的comment数量 ('starLevel', '7'), ('orderBy', 'date'), # ('semanticId', ''), ('showStat', '0'), ('content', '1'), # ('t', '1523264528741'), ('memberId', 'zhangchenghao2009'), # ('callback', 'jQuery1720041881430222992844_1523264353082'), ) return params def json_str_2_dict(self, json_str): ''' json字符串转dict :param json_str: :return: ''' try: data = json.loads(json_str) except: self.my_lg.error('json.loads转换json_str时出错!请检查!') data = {} return data def _get_comment_date(self, comment_date): ''' 得到datetime类型的时间 :param comment_date: eg: 2017-12-04 :return: datetime ''' _ = str(randint(0, 23)) if len(_) == 1: _hour = '0' + _ else: _hour = _ _ = str(randint(0, 59)) if len(_) == 1: _min = '0' + _ else: _min = _ _ = str(randint(0, 59)) if len(_) == 1: _s = '0' + _ else: _s = _ comment_date = comment_date + ' ' + _hour + ':' + _min + ':' + _s return comment_date def __del__(self): try: del self.my_phantomjs del self.my_lg del self.msg except: pass gc.collect()
class JuanPiParse(object): def __init__(self): super(JuanPiParse, self).__init__() self._set_headers() self.result_data = {} self.my_phantomjs = MyPhantomjs() def _set_headers(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'web.juanpi.com', 'User-Agent': HEADERS[randint(0, 34)], # 随机一个请求头 } def get_goods_data(self, goods_id): ''' 模拟构造得到data的url :param goods_id: :return: data 类型dict ''' if goods_id == '': self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: tmp_url = 'https://web.juanpi.com/pintuan/shop/' + str(goods_id) print('------>>>| 得到的商品手机版的地址为: ', tmp_url) ''' 1.原先使用requests来模拟(起初安全的运行了一个月),但是后来发现光requests会not Found,记住使用前别翻墙 ''' # try: # response = requests.get(tmp_url, headers=self.headers, proxies=tmp_proxies, timeout=12) # 在requests里面传数据,在构造头时,注意在url外头的&xxx=也得先构造 # main_body = response.content.decode('utf-8') # # print(main_body) # # main_body = re.compile(r'\n').sub('', main_body) # main_body = re.compile(r'\t').sub('', main_body) # main_body = re.compile(r' ').sub('', main_body) # print(main_body) # data = re.compile(r'__PRELOADED_STATE__=(.*),window\.__SERVER_TIME__=').findall(main_body) # 贪婪匹配匹配所有 # print(data) # except Exception: # print('requests.get()请求超时....') # print('data为空!') # self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 # return {} ''' 2.采用phantomjs来处理,记住使用前别翻墙 ''' body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url, css_selector='div.sc-kgoBCf.bTQvTk') # 该css为手机端标题块 if body == '': print('获取到的body为空str!请检查!') self.result_data = {} return {} data = re.compile( r'__PRELOADED_STATE__ = (.*);</script> <style ').findall( body) # 贪婪匹配匹配所有 # 得到skudata # 卷皮原先的skudata请求地址1(官方放弃) # skudata_url = 'https://webservice.juanpi.com/api/getOtherInfo?goods_id=' + str(goods_id) # 现在卷皮skudata请求地址2 skudata_url = 'https://webservice.juanpi.com/api/getMemberAboutInfo?goods_id=' + str( goods_id) self.skudata_headers = self.headers self.skudata_headers.update({'Host': 'webservice.juanpi.com'}) skudata_body = MyRequests.get_url_body( url=skudata_url, headers=self.skudata_headers) if skudata_body == '': print('获取到的skudata_body为空str!请检查!') self.result_data = {} return {} skudata = re.compile(r'(.*)').findall(skudata_body) # 贪婪匹配匹配所有 if skudata != []: skudata = skudata[0] try: skudata = json.loads(skudata) except: self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} skudata = skudata.get('skudata', {}) # pprint(skudata) try: if skudata.get('info') is not None: pass # 说明得到正确的skudata else: # 否则跳出 print('skudata中info的key为None, 返回空dict') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} except AttributeError as e: print('遇到错误如下(先跳过!): ', e) self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: print('skudata为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} if data != []: main_data = data[0] # print(main_data) try: main_data = json.loads(main_data) # pprint(main_data) except: self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} if main_data.get('detail') is not None: main_data = self._wash_main_data( main_data.get('detail', {})) main_data['skudata'] = skudata # pprint(main_data) # print(main_data) main_data['goods_id'] = goods_id self.result_data = main_data return main_data else: print('data中detail的key为None, 返回空dict') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} def deal_with_data(self): ''' 解析data数据,得到需要的东西 :return: dict ''' data = self.result_data if data != {}: # 店铺名称 shop_name = self._get_shop_name(data=data) # 掌柜 account = '' # 商品名称 title = data.get('baseInfo', {}).get('title', '') # 子标题 sub_title = '' # 商品库存 # 商品标签属性名称 detail_name_list = self._get_detail_name_list(data=data) if isinstance(detail_name_list, str): # 单独处理下架的情况 if detail_name_list == 'is_delete=1': print('该商品已下架...') sql_str = 'update dbo.GoodsInfoAutoGet set IsDelete=1 where GoodsID=%s' params = (self.result_data.get('goods_id', ''), ) _ = SqlServerMyPageInfoSaveItemPipeline() result = _._update_table(sql_str=sql_str, params=params) if result: print('### 该商品已经is_delete=1 ###') else: print('is_delete=1标记失败!') if detail_name_list == {}: self.result_data = {} return {} # print(detail_name_list) # 商品标签属性对应的值(pass不采集) # 要存储的每个标签对应的规格的价格及其库存 price_info_list, price, taobao_price = self._get_price_info_list_and_price_and_taobao_price( data=data) # print('最高价为: ', price) # print('最低价为: ', taobao_price) # pprint(price_info_list) # 所有示例图片的地址 # pprint(data.get('goodImages')) all_img_url = [{ 'img_url': item } for item in data.get('goodImages')] # print(all_img_url) # 详细信息标签名对应的属性 p_info = self._get_p_info(data=data) # pprint(p_info) # div_desc div_desc = self._get_div_desc(data=data) # print(div_desc) # 商品销售时间段 schedule = self._get_goods_schedule(data=data) # pprint(schedule) is_delete = self._get_is_delete(data=data, schedule=schedule) if price == 0 or taobao_price == 0: # 没有获取到价格说明商品已经下架了 is_delete = 1 # print('is_delete = ', is_delete) result = { 'shop_name': shop_name, # 店铺名称 'account': account, # 掌柜 'title': title, # 商品名称 'sub_title': sub_title, # 子标题 'price': price, # 商品价格 'taobao_price': taobao_price, # 淘宝价 # 'goods_stock': goods_stock, # 商品库存 'detail_name_list': detail_name_list, # 商品标签属性名称 # 'detail_value_list': detail_value_list, # 商品标签属性对应的值 'price_info_list': price_info_list, # 要存储的每个标签对应规格的价格及其库存 'all_img_url': all_img_url, # 所有示例图片地址 'p_info': p_info, # 详细信息标签名对应属性 'div_desc': div_desc, # div_desc 'is_delete': is_delete, # 是否下架判断 'schedule': schedule, # 商品销售时间段 } # pprint(result) # wait_to_send_data = { # 'reason': 'success', # 'data': result, # 'code': 1 # } # json_data = json.dumps(wait_to_send_data, ensure_ascii=False) # print(json_data) gc.collect() return result else: print('待处理的data为空的dict') return {} def to_right_and_update_data(self, data, pipeline): data_list = data tmp = GoodsItem() tmp['goods_id'] = data_list['goods_id'] # 官方商品id now_time = get_shanghai_time() tmp['modify_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 商品子标题 tmp['link_name'] = '' # 卖家姓名 tmp['account'] = data_list['account'] # 掌柜名称 # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['price_info'] = [] # 价格信息 tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get( 'price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 tmp['my_shelf_and_down_time'] = data_list.get('my_shelf_and_down_time') tmp['delete_time'] = data_list.get('delete_time') tmp['is_price_change'] = data_list.get('_is_price_change') tmp['price_change_info'] = data_list.get('_price_change_info') params = self._get_db_update_params(item=tmp) # 改价格的sql语句 # sql_str = r'update dbo.GoodsInfoAutoGet set ModfiyTime = %s, ShopName=%s, Account=%s, GoodsName=%s, SubTitle=%s, LinkName=%s, Price=%s, TaoBaoPrice=%s, PriceInfo=%s, SKUName=%s, SKUInfo=%s, ImageUrl=%s, PropertyInfo=%s, DetailInfo=%s, MyShelfAndDownTime=%s, delete_time=%s, IsDelete=%s, Schedule=%s, IsPriceChange=%s, PriceChangeInfo=%s where GoodsID = %s' # 不改价格的sql语句 sql_str = r'update dbo.GoodsInfoAutoGet set ModfiyTime = %s, ShopName=%s, Account=%s, GoodsName=%s, SubTitle=%s, LinkName=%s, PriceInfo=%s, SKUName=%s, SKUInfo=%s, ImageUrl=%s, PropertyInfo=%s, DetailInfo=%s, MyShelfAndDownTime=%s, delete_time=%s, IsDelete=%s, Schedule=%s, IsPriceChange=%s, PriceChangeInfo=%s where GoodsID = %s' pipeline._update_table(sql_str=sql_str, params=params) def insert_into_juanpi_xianshimiaosha_table(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id tmp['spider_url'] = data_list['spider_url'] # 商品地址 tmp['username'] = data_list['username'] # 操作人员username now_time = get_shanghai_time() tmp['deal_with_time'] = now_time # 操作时间 tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 商品子标题 # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get( 'price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') tmp['stock_info'] = data_list.get('stock_info') tmp['miaosha_time'] = data_list.get('miaosha_time') tmp['miaosha_begin_time'] = data_list.get('miaosha_begin_time') tmp['miaosha_end_time'] = data_list.get('miaosha_end_time') tmp['tab_id'] = data_list.get('tab_id') tmp['page'] = data_list.get('page') # 采集的来源地 tmp['site_id'] = 15 # 采集来源地(卷皮秒杀商品) tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 # print('is_delete=', tmp['is_delete']) # print('------>>> | 待存储的数据信息为: |', tmp) print('------>>> | 待存储的数据信息为: |', tmp.get('goods_id')) params = self._get_db_insert_miaosha_params(item=tmp) sql_str = r'insert into dbo.juanpi_xianshimiaosha(goods_id, goods_url, username, create_time, modfiy_time, shop_name, goods_name, sub_title, price, taobao_price, sku_name, sku_info, all_image_url, property_info, detail_info, schedule, stock_info, miaosha_time, miaosha_begin_time, miaosha_end_time, tab_id, page, site_id, is_delete) values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' pipeline._insert_into_table(sql_str=sql_str, params=params) def to_update_juanpi_xianshimiaosha_table(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id now_time = get_shanghai_time() tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get( 'price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') tmp['stock_info'] = data_list.get('stock_info') tmp['miaosha_time'] = data_list.get('miaosha_time') tmp['miaosha_begin_time'] = data_list.get('miaosha_begin_time') tmp['miaosha_end_time'] = data_list.get('miaosha_end_time') tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 # print('is_delete=', tmp['is_delete']) # print('------>>> | 待存储的数据信息为: |', tmp) print('------>>>| 待存储的数据信息为: |', tmp.get('goods_id')) params = self._get_db_update_miaosha_params(item=tmp) sql_str = r'update dbo.juanpi_xianshimiaosha set modfiy_time = %s, shop_name=%s, goods_name=%s, sub_title=%s, price=%s, taobao_price=%s, sku_name=%s, sku_info=%s, all_image_url=%s, property_info=%s, detail_info=%s, is_delete=%s, schedule=%s, stock_info=%s, miaosha_time=%s, miaosha_begin_time=%s, miaosha_end_time=%s where goods_id = %s' pipeline._update_table(sql_str=sql_str, params=params) def insert_into_juuanpi_pintuan_table(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id tmp['spider_url'] = data_list['spider_url'] # 商品地址 tmp['username'] = data_list['username'] # 操作人员username now_time = get_shanghai_time() tmp['deal_with_time'] = now_time # 操作时间 tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 设置最高价price, 最低价taobao_price try: tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal( data_list['taobao_price']).__round__(2) except: # 此处抓到的可能是卷皮拼团券所以跳过 print('此处抓到的可能是卷皮拼团券所以跳过') return None tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get( 'price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['all_sell_count'] = data_list.get('all_sell_count') # 总销量 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') tmp['pintuan_begin_time'] = data_list.get('pintuan_begin_time') tmp['pintuan_end_time'] = data_list.get('pintuan_end_time') tmp['page'] = data_list.get('page') # 采集的来源地 tmp['site_id'] = 18 # 采集来源地(卷皮拼团商品) tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 # print('is_delete=', tmp['is_delete']) # print('------>>> | 待存储的数据信息为: |', tmp) print('------>>> | 待存储的数据信息为: |', tmp.get('goods_id')) params = self._get_db_insert_pintuan_params(item=tmp) sql_str = r'insert into dbo.juanpi_pintuan(goods_id, goods_url, username, create_time, modfiy_time, shop_name, goods_name, sub_title, price, taobao_price, sku_name, sku_info, all_image_url, all_sell_count, property_info, detail_info, schedule, miaosha_begin_time, miaosha_end_time, page, site_id, is_delete) values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' _r = pipeline._insert_into_table(sql_str=sql_str, params=params) return _r def to_right_and_update_pintuan_data(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id now_time = get_shanghai_time() tmp['modfiy_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['shop_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['sub_title'] = data_list['sub_title'] # 设置最高价price, 最低价taobao_price try: tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal( data_list['taobao_price']).__round__(2) except: # 此处抓到的可能是卷皮拼团券所以跳过 print('此处抓到的可能是卷皮拼团券所以跳过') return None tmp['detail_name_list'] = data_list['detail_name_list'] # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get( 'price_info_list') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 # tmp['all_sell_count'] = data_list.get('all_sell_count') # 总销量 tmp['p_info'] = data_list.get('p_info') # 详细信息 tmp['div_desc'] = data_list.get('div_desc') # 下方div tmp['schedule'] = data_list.get('schedule') # 采集的来源地 # tmp['site_id'] = 18 # 采集来源地(卷皮拼团商品) tmp['is_delete'] = data_list.get('is_delete') # 逻辑删除, 未删除为0, 删除为1 # print('is_delete=', tmp['is_delete']) # print('------>>>| 待存储的数据信息为: |', tmp) print('------>>>| 待存储的数据信息为: |', tmp.get('goods_id')) params = self._get_db_update_pintuan_params(item=tmp) sql_str = r'update dbo.juanpi_pintuan set modfiy_time=%s, shop_name=%s, goods_name=%s, sub_title=%s, price=%s, taobao_price=%s, sku_name=%s, sku_Info=%s, all_image_url=%s, property_info=%s, detail_info=%s, schedule=%s, is_delete=%s where goods_id = %s' pipeline._update_table(sql_str=sql_str, params=params) def _get_shop_name(self, data): ''' 获取shop_name :param data: :return: ''' if data.get('brand_info') is not None: shop_name = data.get('brand_info', {}).get('title', '') else: shop_name = data.get('schedule_info', {}).get('brand_title', '') return shop_name def _get_detail_name_list(self, data): ''' 获取detail_name_list :param data: :return: {} 表示出错 | [] 非空正常 ''' sku = data.get('skudata', {}).get('sku', []) # pprint(sku) detail_name_list = [] if sku != []: try: if sku[0].get('av_fvalue', '') == '': fav_name = '' pass else: tmp = {} fav_name = data.get('skudata', {}).get('info', {}).get('fav_name', '') tmp['spec_name'] = fav_name detail_name_list.append(tmp) except IndexError: print('IndexError错误,此处跳过!') # print(sku) if isinstance(sku, str): # 单独处理下架的 if sku == '': return 'is_delete=1' return {} if sku[0].get('av_zvalue', '') == '': zav_name = '' else: tmp = {} zav_name = data.get('skudata', {}).get('info', {}).get('zav_name', '') tmp['spec_name'] = zav_name detail_name_list.append(tmp) return detail_name_list def _get_price_info_list_and_price_and_taobao_price(self, data): ''' 获取price_info_list, price, taobao_price :param data: :return: a tuple ''' sku = data.get('skudata', {}).get('sku', []) # 分析得到sku肯定不为[] # pprint(sku) price_info_list = [] if len(sku) == 1 and sku[0].get( 'av_fvalue', '') == '' and sku[0].get('av_zvalue') == '': # 没有规格的默认只有一个{} # price最高价, taobao_price最低价 price = round(float(sku[0].get('cprice')), 2) taobao_price = price else: # 有规格的 # 通过'stock'='1'来判断是否有库存, ='0'表示无库存 # '由于卷皮不给返回库存值, 所以 'stock_tips'='库存紧张', 我就设置剩余库存为10, 如果'stock_tips'='', 就默认设置库存量为50 # print('777') for item in sku: tmp = {} tmp_1 = [] if item.get('av_fvalue', '') == '': pass else: tmp_1.append(item.get('av_fvalue')) if item.get('av_zvalue', '') == '': pass else: tmp_1.append(item.get('av_zvalue')) tmp_1 = '|'.join(tmp_1) if item.get('av_origin_zpic', '') != '': tmp['img_url'] = item.get('av_origin_zpic', '') else: tmp['img_url'] = '' if item.get('cprice', '') != '': tmp['pintuan_price'] = item.get('cprice') tmp['detail_price'] = item.get('sprice', '') tmp['normal_price'] = item.get('price') else: tmp['pintuan_price'] = item.get('price') if item.get('sprice', '') != '': tmp['detail_price'] = item.get('sprice', '') else: tmp['detail_price'] = item.get('price') tmp['normal_price'] = item.get('price') if item.get('stock') == '0': # 跳过 rest_number = '0' else: # 即'stock'='1' rest_number = '50' if item.get('stock_tips', '') != '' and item.get( 'stock_tips', '') == '库存紧张': # 库存紧张的时候设置下 rest_number = '10' tmp['spec_value'] = tmp_1 tmp['rest_number'] = rest_number price_info_list.append(tmp) # 得到有规格时的最高价和最低价 tmp_price_list = sorted([ round(float(item.get('pintuan_price', '')), 2) for item in price_info_list ]) # print(tmp_price_list) if tmp_price_list == []: price = 0 taobao_price = 0 else: price = tmp_price_list[-1] # 商品价格 taobao_price = tmp_price_list[0] # 淘宝价 return price_info_list, price, taobao_price def _get_p_info(self, data): ''' 获取p_info :param data: :return: ''' p_info = [] attr = data.get('goodsDetail', {}).get('attr', []) # print(attr) if attr != []: # item是str时跳过 p_info = [{ 'p_name': item.get('st_key'), 'p_value': item.get('st_value') } for item in attr if isinstance(item, dict)] for item in p_info: if item.get('p_name') == '运费': # 过滤掉颜色的html代码 item['p_value'] = '全国包邮(偏远地区除外)' # 过滤清洗 tmp_p_value = item.get('p_value', '') tmp_p_value = re.compile(r'\xa0').sub(' ', tmp_p_value) # 替换为一个空格 item['p_value'] = tmp_p_value return p_info def _get_div_desc(self, data): ''' 获取div_desc :param data: :return: ''' div_images_list = data.get('goodsDetail', {}).get('images', []) tmp_div_desc = '' for item in div_images_list: tmp = r'<img src="{}" style="height:auto;width:100%;"/>'.format( item) tmp_div_desc += tmp return '<div>' + tmp_div_desc + '</div>' def _get_goods_schedule(self, data): ''' 获取商品销售时间段 :param data: :return: ''' # print(data.get('skudata', {}).get('info', {})) # print(data.get('skudata', {})) begin_time = data.get('skudata', {}).get('info', {}).get( 'start_time') # 取这个时间段才是正确的销售时间, 之前baseInfo是虚假的 end_time = data.get('skudata', {}).get('info', {}).get('end_time') if begin_time is None or end_time is None: schedule = [] else: schedule = [{ 'begin_time': timestamp_to_regulartime(begin_time), 'end_time': timestamp_to_regulartime(end_time), }] return schedule def _get_is_delete(self, data, schedule): ''' 得到商品的上下架状态 :param data: :param schedule: :return: ''' end_time = data.get('skudata', {}).get('info', {}).get('end_time') is_delete = 0 # 是否下架判断 # 结束时间戳小于当前时间戳则表示已经删除无法购买, 另外每个规格卖光也不显示is_delete=1(在上面已经判断, 这个就跟销售时间段没关系了) if schedule != []: if data.get('baseInfo', {}).get('end_time') is not None: ''' 先判断如果baseInfo中的end_time=='0'表示已经下架 ''' # base_info_end_time = data.get('baseInfo', {}).get('end_time') # self.my_lg.info(base_info_end_time) # if base_info_end_time == '0': # is_delete = 1 pass if float(end_time) < time.time(): ''' 再判断日期过期的 ''' is_delete = 1 ''' 卷皮-新增下架判断: time: 2018-5-12 ''' if data.get('skudata', {}).get('info', {}).get('gstatus', '1') == '2': # 'gstatus'在售状态为'1' is_delete = 1 return is_delete def _wash_main_data(self, main_data): ''' 清洗main_data :param main_data: :return: ''' # 处理commitments try: main_data['commitments'] = '' main_data.get('discount', {})['coupon'] = '' main_data.get('discount', {})['coupon_index'] = '' main_data.get('discount', {})['vip_info'] = '' main_data['topbanner'] = '' except: pass try: main_data.get('brand_info')['sub_goods'] = '' except: pass return main_data def _get_db_update_params(self, item): ''' 得到待更新的db数据 :param item: :return: ''' params = ( item['modify_time'], item['shop_name'], item['account'], item['title'], item['sub_title'], item['link_name'], # item['price'], # item['taobao_price'], dumps(item['price_info'], ensure_ascii=False), dumps(item['detail_name_list'], ensure_ascii=False), dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), dumps(item['p_info'], ensure_ascii=False), item['div_desc'], dumps(item['my_shelf_and_down_time'], ensure_ascii=False), item['delete_time'], item['is_delete'], dumps(item['schedule'], ensure_ascii=False), item['is_price_change'], dumps(item['price_change_info'], ensure_ascii=False), item['goods_id'], ) return params def _get_db_insert_miaosha_params(self, item): params = ( item['goods_id'], item['spider_url'], item['username'], item['deal_with_time'], item['modfiy_time'], item['shop_name'], item['title'], item['sub_title'], item['price'], item['taobao_price'], dumps(item['detail_name_list'], ensure_ascii=False ), # 把list转换为json才能正常插入数据(并设置ensure_ascii=False) dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), dumps(item['p_info'], ensure_ascii=False), # 存入到PropertyInfo item['div_desc'], # 存入到DetailInfo dumps(item['schedule'], ensure_ascii=False), dumps(item['stock_info'], ensure_ascii=False), dumps(item['miaosha_time'], ensure_ascii=False), item['miaosha_begin_time'], item['miaosha_end_time'], item['tab_id'], item['page'], item['site_id'], item['is_delete'], ) return params def _get_db_update_miaosha_params(self, item): params = ( item['modfiy_time'], item['shop_name'], item['title'], item['sub_title'], item['price'], item['taobao_price'], dumps(item['detail_name_list'], ensure_ascii=False), dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), dumps(item['p_info'], ensure_ascii=False), item['div_desc'], item['is_delete'], dumps(item['schedule'], ensure_ascii=False), dumps(item['stock_info'], ensure_ascii=False), dumps(item['miaosha_time'], ensure_ascii=False), item['miaosha_begin_time'], item['miaosha_end_time'], item['goods_id'], ) return params def _get_db_insert_pintuan_params(self, item): params = ( item['goods_id'], item['spider_url'], item['username'], item['deal_with_time'], item['modfiy_time'], item['shop_name'], item['title'], item['sub_title'], item['price'], item['taobao_price'], dumps(item['detail_name_list'], ensure_ascii=False ), # 把list转换为json才能正常插入数据(并设置ensure_ascii=False) dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), item['all_sell_count'], dumps(item['p_info'], ensure_ascii=False), # 存入到PropertyInfo item['div_desc'], # 存入到DetailInfo dumps(item['schedule'], ensure_ascii=False), item['pintuan_begin_time'], item['pintuan_end_time'], item['page'], item['site_id'], item['is_delete'], ) return params def _get_db_update_pintuan_params(self, item): params = ( item['modfiy_time'], item['shop_name'], item['title'], item['sub_title'], item['price'], item['taobao_price'], dumps(item['detail_name_list'], ensure_ascii=False ), # 把list转换为json才能正常插入数据(并设置ensure_ascii=False) dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), # item['all_sell_count'], dumps(item['p_info'], ensure_ascii=False), # 存入到PropertyInfo item['div_desc'], # 存入到DetailInfo dumps(item['schedule'], ensure_ascii=False), item['is_delete'], item['goods_id']) return params def get_goods_id_from_url(self, juanpi_url): ''' 得到goods_id :param juanpi_url: :return: goods_id (类型str) ''' is_juanpi_url = re.compile(r'http://shop.juanpi.com/deal/.*?').findall( juanpi_url) if is_juanpi_url != []: if re.compile(r'http://shop.juanpi.com/deal/(\d+).*?').findall( juanpi_url) != []: tmp_juanpi_url = re.compile( r'http://shop.juanpi.com/deal/(\d+).*?').findall( juanpi_url)[0] if tmp_juanpi_url != '': goods_id = tmp_juanpi_url else: # 只是为了在pycharm运行时不跳到chrome,其实else完全可以不要的 juanpi_url = re.compile(r';').sub('', juanpi_url) goods_id = re.compile( r'http://shop.juanpi.com/deal/(\d+).*?').findall( juanpi_url)[0] print('------>>>| 得到的卷皮商品的地址为:', goods_id) return goods_id else: print( '卷皮商品url错误, 非正规的url, 请参照格式(http://shop.juanpi.com/deal/)开头的...' ) return '' def __del__(self): try: del self.my_phantomjs del self.result_data except: pass gc.collect()
class MoGuJiePinTuan(object): def __init__(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'api.mogujie.com', 'Referer': 'https://pintuan.mogujie.com/ptpt/app/pd?acm=3.mce.1_10_1fvsk.51827.0.mUTadqIzS9Pbg.m_370494-pos_2-mf_4537_796033&ptp=m1._mf1_1239_4537._keyword_51827.0.xLt0G92', 'User-Agent': HEADERS[randint(0, 34)], # 随机一个请求头 } self.fcid_dict = { '女装': 10053171, # '精选': 10053172, '男友': 10053173, '内衣': 10053174, '女鞋': 10053175, '包包': 10053176, '美妆': 10053177, '生活': 10053178, '配饰': 10053179, '母婴': 10053180, '食品': 10053181, } def get_pintuan_goods_info(self): ''' 模拟构造得到data的url,得到近期所有的限时拼团商品信息 :return: None ''' goods_list = [] ''' 方法一: 蘑菇街手机版拼团商品列表获取签名无法破解,所以不用手机端的方法来获取数据 ''' # mw_appkey = '100028' # mw_t = str(time.time().__round__()) + str(randint(100, 999)) # time.time().__round__() 表示保留到个位 # mw_uuid = '956bf265-90a4-45b0-bfa8-31040782f99e' # mw_ttid = 'NMMain%40mgj_h5_1.0' # # _ = str(time.time().__round__()) + str(randint(100, 999)) # time.time().__round__() 表示保留到个位 # # data = { # "pid": "93745", # "platform": "m", # "cKey": "mwp_mait", # "fcid": "", # } # # params = { # 'data': data # } # # # https://api.mogujie.com/h5/mwp.darwin.get/3/?mw-appkey=100028&mw-t=1517647409632&mw-uuid=956bf265-90a4-45b0-bfa8-31040782f99e&mw-ttid=NMMain%40mgj_h5_1.0&mw-sign=abde92f778e47bce98a3ed25fd71eb1a&data=%7B%22pid%22%3A%2293745%22%2C%22platform%22%3A%22m%22%2C%22cKey%22%3A%22mwp_mait%22%2C%22fcid%22%3A%22%22%7D&callback=mwpCb1&_=1517647409648 # # https://api.mogujie.com/h5/mwp.darwin.get/3/?mw-appkey=100028&mw-t=1517647893930&mw-uuid=956bf265-90a4-45b0-bfa8-31040782f99e&mw-ttid=NMMain%40mgj_h5_1.0&callback=mwpCb1&_=1517647893748&data=pid&data=platform&data=cKey&data=fcid # # tmp_url = 'https://api.mogujie.com/h5/mwp.darwin.get/3/?mw-appkey={0}&mw-t={1}&mw-uuid={2}&mw-ttid={3}&callback=mwpCb1&_={4}'.format( # mw_appkey, mw_t, mw_uuid, mw_ttid, _ # ) # # # 设置代理ip # ip_object = MyIpPools() # self.proxies = ip_object.get_proxy_ip_from_ip_pool() # {'http': ['xx', 'yy', ...]} # self.proxy = self.proxies['http'][randint(0, len(self.proxies) - 1)] # # tmp_proxies = { # 'http': self.proxy, # } # # try: # response = requests.post(tmp_url, headers=self.headers, data=data, proxies=tmp_proxies, timeout=13) # 在requests里面传数据,在构造头时,注意在url外头的&xxx=也得先构造 # body = response.content.decode('utf-8') # print(body) # except Exception: # print('requests.get()请求超时....') # print('data为空!') # self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 # return {} ''' 方法二: 通过pc端来获取拼团商品列表 ''' self.my_phantomjs = MyPhantomjs() for key in self.fcid_dict: print('正在抓取的分类为: ', key) for index in range(1, 100): if index % 5 == 0: try: del self.my_phantomjs except: pass gc.collect() self.my_phantomjs = MyPhantomjs() fcid = self.fcid_dict[key] tmp_url = 'http://list.mogujie.com/search?page={0}&fcid={1}&algoKey=pc_tuan_book_pop&cKey=pc-tuan'.format( str(index), fcid) # requests请求数据被过滤(起初能用),改用phantomjs # body = MyRequests.get_url_body(url=tmp_url, headers=self.headers, had_referer=True) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url) # print(body) try: body = re.compile(r'<pre.*?>(.*?)</pre>').findall(body)[0] tmp_data = json.loads(body) except: print('json.loads转换body时出错, 请检查') continue if tmp_data.get('result', {}).get('wall', {}).get('docs', []) == []: # 表示拼团数据为空则跳出循环 break # pprint(tmp_data) # print(tmp_data) tmp_item_list = tmp_data.get('result', {}).get('wall', {}).get('docs', []) # print(tmp_item_list) # pprint(tmp_item_list) begin_time_timestamp = int(time.time()) # 开始拼团的时间戳 item_list = [{ 'goods_id': item.get('tradeItemId', ''), 'pintuan_time': { 'begin_time': self.timestamp_to_regulartime( timestamp=begin_time_timestamp), 'end_time': self.timestamp_to_regulartime( self.get_pintuan_end_time( begin_time_timestamp, item.get('leftTimeOrg', ''))), }, 'all_sell_count': str(item.get('salesVolume', 0)), 'fcid': fcid, 'page': index, 'sort': key, } for item in tmp_item_list] print(item_list) for item_1 in item_list: goods_list.append(item_1) sleep(MOGUJIE_SLEEP_TIME) # 处理goods_list数据 print(goods_list) self.deal_with_data(goods_list) sleep(5) def deal_with_data(self, *params): ''' 处理并存储相关拼团商品的数据 :param params: 待传参数 :return: ''' goods_list = params[0] mogujie = MoGuJieParse() my_pipeline = SqlServerMyPageInfoSaveItemPipeline() if my_pipeline.is_connect_success: db_goods_id_list = [ item[0] for item in list( my_pipeline.select_mogujie_pintuan_all_goods_id()) ] print(db_goods_id_list) for item in goods_list: if item.get('goods_id', '') in db_goods_id_list: print('该goods_id已经存在于数据库中, 此处跳过') pass else: goods_id = str(item.get('goods_id', '')) tmp_url = 'https://shop.mogujie.com/detail/' + str( goods_id) mogujie.get_goods_data(goods_id=str(goods_id)) goods_data = mogujie.deal_with_data() if goods_data == {}: # 返回的data为空则跳过 pass else: # 否则就解析并且插入 # 规范化 tmp_price_info_list = goods_data['price_info_list'] price_info_list = [{ 'spec_value': item_4.get('spec_value'), 'pintuan_price': item_4.get('detail_price'), 'normal_price': item_4.get('normal_price'), 'img_url': item_4.get('img_url'), 'rest_number': item_4.get('rest_number'), } for item_4 in tmp_price_info_list] goods_data['price_info_list'] = price_info_list goods_data['goods_url'] = tmp_url goods_data['goods_id'] = str(goods_id) goods_data['pintuan_time'] = item.get( 'pintuan_time', {}) goods_data['pintuan_begin_time'], goods_data[ 'pintuan_end_time'] = self.get_pintuan_begin_time_and_pintuan_end_time( pintuan_time=item.get('pintuan_time', {})) goods_data['all_sell_count'] = item.get( 'all_sell_count', '') goods_data['fcid'] = str(item.get('fcid')) goods_data['page'] = str(item.get('page')) goods_data['sort'] = str(item.get('sort', '')) # pprint(goods_data) # print(goods_data) mogujie.insert_into_mogujie_pintuan_table( data=goods_data, pipeline=my_pipeline) sleep(MOGUJIE_SLEEP_TIME) # 放慢速度 else: print('数据库连接失败,此处跳过!') pass try: del mogujie except: pass gc.collect() def get_pintuan_end_time(self, begin_time, left_time): ''' 处理并得到拼团结束时间 :param begin_time: 秒杀开始时间戳 :param left_time: 剩余时间字符串 :return: end_time 时间戳(int) ''' # 'leftTimeOrg': '6天13小时' # 'leftTimeOrg': '13小时57分' had_day = re.compile(r'天').findall(left_time) had_hour = re.compile(r'小时').findall(left_time) had_min = re.compile(r'分').findall(left_time) tmp = re.compile(r'\d+').findall(left_time) if had_day != [] and had_hour != []: # left_time 格式为 '6天13小时' day, hour, min = int(tmp[0]), int(tmp[1]), 0 elif had_day == [] and had_hour != []: # left_time 格式为 '13小时57分' day, hour, min = 0, int(tmp[0]), int(tmp[1]) elif had_day == [] and had_hour == []: # left_time 格式为 '36分' print('left_time = ', left_time) day, hour, min = 0, 0, int(tmp[0]) else: # 无天, 小时, 分 print('day, hour, min = 0, 0, 0', 'left_time = ', left_time) day, hour, min = 0, 0, 0 left_end_time_timestamp = \ day * 24 * 60 * 60 + \ hour * 60 * 60 + \ min * 60 return begin_time + left_end_time_timestamp def timestamp_to_regulartime(self, timestamp): ''' 将时间戳转换成时间 ''' # 利用localtime()函数将时间戳转化成localtime的格式 # 利用strftime()函数重新格式化时间 # 转换成localtime time_local = time.localtime(int(timestamp)) # print(time_local) # 转换成新的时间格式(2016-05-05 20:28:54) dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) return dt def get_pintuan_begin_time_and_pintuan_end_time(self, pintuan_time): ''' 返回拼团开始和结束时间 :param pintuan_time: :return: tuple pintuan_begin_time, pintuan_end_time ''' pintuan_begin_time = pintuan_time.get('begin_time') pintuan_end_time = pintuan_time.get('end_time') # 将字符串转换为datetime类型 pintuan_begin_time = datetime.datetime.strptime( pintuan_begin_time, '%Y-%m-%d %H:%M:%S') pintuan_end_time = datetime.datetime.strptime(pintuan_end_time, '%Y-%m-%d %H:%M:%S') return pintuan_begin_time, pintuan_end_time def __del__(self): try: del self.my_phantomjs except: pass gc.collect()
class TmallCommentParse(object): def __init__(self, logger=None): self.result_data = {} self.msg = '' self._set_logger(logger) self._set_headers() self.page_size = '10' self.comment_page_switch_sleep_time = 1.5 # 评论下一页sleep time self.my_phantomjs = MyPhantomjs() self.g_data = {} # 临时数据 self.random_sku_info_list = [] # 临时数据(存该商品所有的规格) def _get_comment_data(self, type: int, goods_id): if goods_id == '' or type == '': self.result_data = {} return {} self.my_lg.info('------>>>| 待处理的goods_id为: %s' % str(goods_id)) '''先获取到sellerId''' try: seller_id = self._get_seller_id(type=type, goods_id=goods_id) except AssertionError or IndexError as e: self.my_lg.error('出错goods_id: %s' % goods_id) self.my_lg.error(e.args[0]) self.result_data = {} self.random_sku_info_list = [] return {} """再获取price_info_list""" try: self.random_sku_info_list = self._get_random_sku_info_list() # self.my_lg.info(self.random_sku_info_list) except Exception as e: self.my_lg.error('出错goods_id: %s' % str(goods_id)) self.my_lg.exception(e) self.result_data = {} self.random_sku_info_list = [] return {} _tmp_comment_list = [] for current_page in range(1, 4): self.my_lg.info('------>>>| 正在抓取第 {0} 页的评论...'.format( str(current_page))) _url = 'https://rate.tmall.com/list_detail_rate.htm' params = self._set_params(goods_id=goods_id, seller_id=seller_id, current_page=current_page) self.headers.update({ 'referer': 'https://detail.m.tmall.com/item.htm?id=' + goods_id }) # 原先用代理请求不到数据的原因是没有cookies # body = MyRequests.get_url_body(url=_url, headers=self.headers, params=params, encoding='gbk') # 所以直接用phantomjs来获取相关api数据 _url = _get_url_contain_params(url=_url, params=params) # 根据params组合得到url # self.my_lg.info(_url) body = self.my_phantomjs.use_phantomjs_to_get_url_body(url=_url) # self.my_lg.info(str(body)) if body == '': self.my_lg.error( '获取到的body为空str! 出错type:{0}, goods_id:{1}'.format( str(type), goods_id)) self.result_data = {} return {} try: _ = re.compile('\((.*)\)').findall(body)[0] except IndexError: _ = {} self.my_lg.error('索引异常! 出错type:{0}, goods_id:{1}'.format( str(type), goods_id)) try: data = json.loads(_).get('rateDetail', {}).get('rateList', []) # pprint(data) except: data = [] self.my_lg.error( 'json.loads转换_出错! 出错type:{0}, goods_id:{1}'.format( str(type), goods_id)) _tmp_comment_list += data sleep(self.comment_page_switch_sleep_time) try: _comment_list = self._get_comment_list( _tmp_comment_list=_tmp_comment_list) except Exception as e: self.my_lg.error('出错type:{0}, goods_id:{1}'.format( str(type), goods_id)) self.my_lg.exception(e) self.result_data = {} return {} _t = datetime.datetime.now() _r = CommentItem() _r['goods_id'] = str(goods_id) _r['create_time'] = _t _r['modify_time'] = _t _r['_comment_list'] = _comment_list self.result_data = _r # pprint(self.result_data) return self.result_data def _get_comment_list(self, _tmp_comment_list): ''' 转换成需求的结果集 :param _tmp_comment_list: :return: ''' _comment_list = [] for item in _tmp_comment_list: _comment_date = item.get('rateDate', '') assert _comment_date != '', '得到的_comment_date为空str!请检查!' # 天猫接口拿到的sku_info默认为空 # sku_info = '' # 从所有规格里面随机一个 if self.random_sku_info_list == []: self.random_sku_info_list = [''] sku_info = str(choice(self.random_sku_info_list)) _comment_content = item.get('rateContent', '') assert _comment_content != '', '得到的评论内容为空str!请检查!' _comment_content = self._wash_comment(comment=_comment_content) buyer_name = item.get('displayUserNick', '') assert buyer_name != '', '得到的用户昵称为空值!请检查!' # 天猫设置默认 购买量为1 quantify = 1 # 天猫没有head_img回传,就设置一个默认地址 head_img = '' # 第一次评论图片 _comment_img_list = item.get( 'pics', []) if item.get('pics', '') != '' else [] if _comment_img_list != []: _comment_img_list = [{ 'img_url': 'https:' + img } for img in _comment_img_list] '''追评''' _tmp_append_comment = item.get( 'appendComment', {}) if item.get('appendComment', '') != '' else {} # 追评的图片 _append_comment_img_list = _tmp_append_comment.get( 'pics', []) if _tmp_append_comment.get('pics', '') != '' else [] if _append_comment_img_list != []: _append_comment_img_list = [{ 'img_url': 'https:' + img } for img in _append_comment_img_list] if _tmp_append_comment != {}: append_comment = { 'comment_date': _tmp_append_comment.get('commentTime', ''), 'comment': self._wash_comment(_tmp_append_comment.get('content', '')), 'img_url_list': _append_comment_img_list, } else: append_comment = {} comment = [{ 'comment': _comment_content, 'comment_date': _comment_date, 'sku_info': sku_info, 'img_url_list': _comment_img_list, 'star_level': randint(4, 5), 'video': '', }] _ = { 'buyer_name': buyer_name, # 买家昵称 'comment': comment, # 评论内容 'quantify': quantify, # 评论数量 'head_img': head_img, # 头像 'append_comment': append_comment, # 追评 } _comment_list.append(_) return _comment_list def _get_seller_id(self, type, goods_id): ''' 得到seller_id :param type: :param goods_id: :return: ''' _ = TmallParse(logger=self.my_lg) _g = [type, goods_id] self.g_data = _.get_goods_data(goods_id=_g) seller_id = str(self.g_data.get('seller', {}).get('userId', 0)) # self.my_lg.info('获取到的seller_id: ' + seller_id) try: del _ except: pass assert seller_id != 0, '获取到的seller_id为0!' return seller_id def _get_random_sku_info_list(self): ''' 得到所有的sku_info_list信息,用于随机一个属性 :return: ''' assert self.g_data != {}, 'g_data为空dict' _t = TaoBaoLoginAndParse(logger=self.my_lg) # 得到每个标签对应值的价格及其库存 price_info_list = _t._get_price_info_list( data=self.g_data, detail_value_list=_t._get_detail_name_and_value_list( data=self.g_data)[1]) try: del _t except: pass return list(set([_i.get('spec_value', '') for _i in price_info_list])) def _set_logger(self, logger): if logger is None: self.my_lg = set_logger(log_file_name=MY_SPIDER_LOGS_PATH + '/天猫/comment/' + str(get_shanghai_time())[0:10] + '.txt', console_log_level=INFO, file_log_level=ERROR) else: self.my_lg = logger def _set_headers(self): self.headers = { 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9', 'user-agent': HEADERS[randint(0, len(HEADERS) - 1)], 'accept': '*/*', 'referer': 'https://detail.m.tmall.com/item.htm?id=524718632348', } def _wash_comment(self, comment): ''' 清洗评论 :param comment: :return: ''' comment = re.compile('天猫超市|天猫国际|天猫全球购|天猫大药房|某淘|某宝').sub('', comment) comment = comment.replace('天猫', '').replace('淘宝', '') comment = re.compile('tmall|Tmall|TMALL|TAOBAO|taobao').sub( '', comment) return comment def _set_params(self, **kwargs): ''' 设置params :param kwargs: :return: ''' goods_id = kwargs.get('goods_id') seller_id = kwargs.get('seller_id') current_page = kwargs.get('current_page') callback = '_DLP_2519_der_3_currentPage_{0}_pageSize_{1}_'.format( str(current_page), self.page_size) _params = ( ('itemId', goods_id), ('sellerId', seller_id), ('order', '3'), ('currentPage', str(current_page)), ('pageSize', self.page_size), ('callback', callback), ) return _params def __del__(self): try: del self.my_lg del self.my_phantomjs del self.g_data except: pass gc.collect()
class Zhe800Spike(object): def __init__(self): self._set_headers() self.my_phantomjs = MyPhantomjs() def _set_headers(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'zhe800.com', 'User-Agent': HEADERS[randint(0, 34)] # 随机一个请求头 } def get_spike_hour_goods_info(self): ''' 模拟构造得到data的url,得到近期所有的限时秒杀商品信息 :return: ''' base_session_id = BASE_SESSION_ID while base_session_id < MAX_SESSION_ID: print('待抓取的session_id为: ', base_session_id) data = self._get_one_session_id_data( base_session_id=base_session_id) sleep(.2) if data.get('data', {}).get('blocks', []) == []: # session_id不存在 pass else: # 否则session_id存在 try: _ = str( data.get('data', {}).get('blocks', [])[0].get('deal', {}).get('begin_time', ''))[:10] if _ != '': pass elif data.get('data', {}).get('blocks', [])[0].get( 'showcase', {}) != {}: # 未来时间 print('*** 未来时间 ***') # pprint(data.get('data', {})) _ = str( data.get('data', {}).get('blocks', [])[1].get( 'deal', {}).get('begin_time', ''))[:10] else: raise Exception begin_times_timestamp = int( _) # 将如 "2017-09-28 10:00:00"的时间字符串转化为时间戳,然后再将时间戳取整 except Exception as e: print('遇到严重错误: ', e) continue print('秒杀时间为: ', timestamp_to_regulartime(begin_times_timestamp)) if self.is_recent_time( timestamp=begin_times_timestamp): # 说明秒杀日期合法 try: data = [ item_s.get('deal', {}) for item_s in data.get( 'data', {}).get('blocks', []) ] except Exception as e: print('遇到严重错误: ', e) continue # pprint(data) if data != []: # 否则说明里面有数据 miaosha_goods_list = self.get_miaoshao_goods_info_list( data=data) # pprint(miaosha_goods_list) zhe_800 = Zhe800Parse() my_pipeline = SqlServerMyPageInfoSaveItemPipeline() if my_pipeline.is_connect_success: sql_str = r'select goods_id, miaosha_time, session_id from dbo.zhe_800_xianshimiaosha where site_id=14' db_goods_id_list = [ item[0] for item in list( my_pipeline._select_table(sql_str=sql_str)) ] for item in miaosha_goods_list: if item.get('zid', '') in db_goods_id_list: print('该goods_id已经存在于数据库中, 此处跳过') pass else: tmp_url = 'https://shop.zhe800.com/products/' + str( item.get('zid', '')) goods_id = zhe_800.get_goods_id_from_url( tmp_url) zhe_800.get_goods_data(goods_id=goods_id) goods_data = zhe_800.deal_with_data() if goods_data == {}: # 返回的data为空则跳过 pass else: # 否则就解析并且插入 goods_data['stock_info'] = item.get( 'stock_info') goods_data['goods_id'] = str( item.get('zid')) goods_data['spider_url'] = tmp_url goods_data['username'] = '******' goods_data['price'] = item.get('price') goods_data['taobao_price'] = item.get( 'taobao_price') goods_data['sub_title'] = item.get( 'sub_title') # goods_data['is_baoyou'] = item.get('is_baoyou') goods_data['miaosha_time'] = item.get( 'miaosha_time') goods_data[ 'miaosha_begin_time'], goods_data[ 'miaosha_end_time'] = get_miaosha_begin_time_and_miaosha_end_time( miaosha_time=item.get( 'miaosha_time')) goods_data['session_id'] = str( base_session_id) # print(goods_data['miaosha_time']) # print(goods_data) zhe_800.insert_into_zhe_800_xianshimiaosha_table( data=goods_data, pipeline=my_pipeline) sleep(ZHE_800_SPIKE_SLEEP_TIME) # 放慢速度 # sleep(2) else: pass try: del zhe_800 except: pass gc.collect() else: # 说明这个sessionid没有数据 print('该sessionid没有相关key为jsons的数据') # return {} pass else: pass base_session_id += 2 def _get_one_session_id_data(self, base_session_id): ''' 得到一个session_id的data :param base_session_id: :return: ''' _data = [] for _page in range(1, 20): '''per_page为20固定,其他不返回数据''' tmp_url = 'https://zapi.zhe800.com/zhe800_n_api/xsq/m/session_deals?session_id={0}&page={1}&per_page=20'.format( str(base_session_id), _page) body = self.my_phantomjs.use_phantomjs_to_get_url_body(url=tmp_url) # print(body) body_1 = re.compile(r'<pre.*?>(.*)</pre>').findall(body) if body_1 != []: data = body_1[0] data = json.loads(data) # pprint(data) # print(type(data.get('data', {}).get('has_next'))) if data.get('msg', '') == '无效场次': print('该session_id不存在,此处跳过') break if not data.get('data', {}).get('has_next', True): print('该session_id没有下页了!!') break else: print('正在抓取该session_id的第 {0} 页...'.format(_page)) for _i in data.get('data', {}).get('blocks', []): _data.append(_i) sleep(.2) return { 'data': { 'blocks': _data, } } def get_miaoshao_goods_info_list(self, data): ''' 得到秒杀商品有用信息 :param data: 待解析的data :return: 有用信息list ''' miaosha_goods_list = [] for item in data: # pprint(item) tmp = {} # 秒杀开始时间和结束时间 tmp['miaosha_time'] = { 'miaosha_begin_time': timestamp_to_regulartime(int( str(item.get('begin_time'))[0:10])), 'miaosha_end_time': timestamp_to_regulartime(int(str(item.get('end_time'))[0:10])), } # 折800商品地址 tmp['zid'] = item.get('zid') # 是否包邮 # tmp['is_baoyou'] = item.get('is_baoyou', 0) # 限时秒杀的库存信息 tmp['stock_info'] = { 'activity_stock': item.get('activity_stock', 0), # activity_stock为限时抢的剩余数量 'stock': item.get('stock', 0), # stock为限时秒杀的总库存 } # 原始价格 tmp['price'] = float(item.get('list_price')) # 秒杀的价格, float类型 tmp['taobao_price'] = float(item.get('price')) # 子标题 tmp['sub_title'] = item.get('description', '') miaosha_goods_list.append(tmp) # pprint(miaosha_goods_list) return miaosha_goods_list def is_recent_time(self, timestamp): ''' 判断是否在指定的日期差内 :param timestamp: 时间戳 :return: True or False ''' time_1 = int(timestamp) time_2 = time.time() # 当前的时间戳 time_1 = time.localtime(time_1) time_2 = time.localtime(time_2) if time_1.tm_year > time_2.tm_year: print('** 该年份为未来时间年份 **') if time_1.tm_hour >= SPIDER_START_HOUR and time_1.tm_hour <= SPIDER_END_HOUR: # 规定到SPIDER_START_HOUR点到SPIDER_END_HOUR点的商品信息 print('合法时间') # diff_days = abs(time_1.tm_mday - time_2.tm_mday) return True else: print('该小时在{0}点到{1}点以外,此处不处理跳过'.format(SPIDER_START_HOUR, SPIDER_END_HOUR)) return False if time_1.tm_year == time_2.tm_year: if time_1.tm_mon > time_2.tm_mon: # 先处理得到的time_1的月份大于当前月份的信息(即未来月份的) print('** 该月份为未来时间月份 **') if time_1.tm_hour >= SPIDER_START_HOUR and time_1.tm_hour <= SPIDER_END_HOUR: # 规定到SPIDER_START_HOUR点到SPIDER_END_HOUR点的商品信息 print('合法时间') # diff_days = abs(time_1.tm_mday - time_2.tm_mday) return True else: print('该小时在{0}点到{1}点以外,此处不处理跳过'.format( SPIDER_START_HOUR, SPIDER_END_HOUR)) return False if time_1.tm_mon >= time_2.tm_mon: # 如果目标时间的月份时间 >= 当前月份(月份合法, 表示是当前月份或者是今年其他月份) if time_1.tm_mday >= time_2.tm_mday - 2: # 这样能抓到今天的前两天的信息 if time_1.tm_hour >= SPIDER_START_HOUR and time_1.tm_hour <= SPIDER_END_HOUR: # 规定到SPIDER_START_HOUR点到SPIDER_END_HOUR点的商品信息 print('合法时间') # diff_days = abs(time_1.tm_mday - time_2.tm_mday) return True else: print('该小时在{0}点到{1}点以外,此处不处理跳过'.format( SPIDER_START_HOUR, SPIDER_END_HOUR)) return False else: print('该日时间已过期, 此处跳过') return False else: # 月份过期 print('该月份时间已过期,此处跳过') return False else: print('非本年度的限时秒杀时间,此处跳过') return False def __del__(self): try: del self.my_phantomjs except: pass gc.collect()
class Zhe800Spike(object): def __init__(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'zhe800.com', 'User-Agent': HEADERS[randint(0, 34)] # 随机一个请求头 } self.my_phantomjs = MyPhantomjs() def get_spike_hour_goods_info(self): ''' 模拟构造得到data的url,得到近期所有的限时秒杀商品信息 :return: ''' base_session_id = BASE_SESSION_ID while base_session_id < MAX_SESSION_ID: print('待抓取的session_id为: ', base_session_id) tmp_url = 'https://zapi.zhe800.com/zhe800_n_api/xsq/m/session_deals?session_id={0}&page=1&per_page=1000'.format( str(base_session_id) ) body = self.my_phantomjs.use_phantomjs_to_get_url_body(url=tmp_url) # print(body) body_1 = re.compile(r'<pre.*?>(.*)</pre>').findall(body) if body_1 != []: data = body_1[0] data = json.loads(data) # pprint(data) if data.get('data', {}).get('blocks', []) == []: # session_id不存在 print('该session_id不存在,此处跳过') pass else: # 否则session_id存在 # begin_times_timestamp = int(time.mktime(time.strptime(begin_times, '%Y-%m-%d %H:%M:%S'))) # 将如 "2017-09-28 10:00:00"的时间字符串转化为时间戳,然后再将时间戳取整 try: begin_times_timestamp = int(str(data.get('data', {}).get('blocks', [])[0].get('deal', {}).get('begin_time', ''))[:10]) except Exception as e: print('遇到严重错误: ', e) continue print('秒杀时间为: ', self.timestamp_to_regulartime(begin_times_timestamp)) if self.is_recent_time(timestamp=begin_times_timestamp): # 说明秒杀日期合法 try: data = [item_s.get('deal', {}) for item_s in data.get('data', {}).get('blocks', [])] except Exception as e: print('遇到严重错误: ', e) continue # pprint(data) if data != []: # 否则说明里面有数据 miaosha_goods_list = self.get_miaoshao_goods_info_list(data=data) # pprint(miaosha_goods_list) zhe_800 = Zhe800Parse() my_pipeline = SqlServerMyPageInfoSaveItemPipeline() if my_pipeline.is_connect_success: db_goods_id_list = [item[0] for item in list(my_pipeline.select_zhe_800_xianshimiaosha_all_goods_id())] for item in miaosha_goods_list: if item.get('zid', '') in db_goods_id_list: print('该goods_id已经存在于数据库中, 此处跳过') pass else: tmp_url = 'https://shop.zhe800.com/products/' + str(item.get('zid', '')) goods_id = zhe_800.get_goods_id_from_url(tmp_url) zhe_800.get_goods_data(goods_id=goods_id) goods_data = zhe_800.deal_with_data() if goods_data == {}: # 返回的data为空则跳过 pass else: # 否则就解析并且插入 goods_data['stock_info'] = item.get('stock_info') goods_data['goods_id'] = str(item.get('zid')) goods_data['spider_url'] = tmp_url goods_data['username'] = '******' goods_data['price'] = item.get('price') goods_data['taobao_price'] = item.get('taobao_price') goods_data['sub_title'] = item.get('sub_title') # goods_data['is_baoyou'] = item.get('is_baoyou') goods_data['miaosha_time'] = item.get('miaosha_time') goods_data['miaosha_begin_time'], goods_data['miaosha_end_time'] = self.get_miaosha_begin_time_and_miaosha_end_time(miaosha_time=item.get('miaosha_time')) goods_data['session_id'] = str(base_session_id) # print(goods_data) zhe_800.insert_into_zhe_800_xianshimiaosha_table(data=goods_data, pipeline=my_pipeline) sleep(ZHE_800_SPIKE_SLEEP_TIME) # 放慢速度 # sleep(2) else: pass try: del zhe_800 except: pass gc.collect() else: # 说明这个sessionid没有数据 print('该sessionid没有相关key为jsons的数据') # return {} pass else: pass else: print('获取到的data为空!') # return {} pass base_session_id += 2 def timestamp_to_regulartime(self, timestamp): ''' 将时间戳转换成时间 ''' # 利用localtime()函数将时间戳转化成localtime的格式 # 利用strftime()函数重新格式化时间 # 转换成localtime time_local = time.localtime(int(timestamp)) # print(time_local) # 转换成新的时间格式(2016-05-05 20:28:54) dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) return dt def get_miaosha_begin_time_and_miaosha_end_time(self, miaosha_time): ''' 返回秒杀开始和结束时间 :param miaosha_time: :return: tuple miaosha_begin_time, miaosha_end_time ''' miaosha_begin_time = miaosha_time.get('miaosha_begin_time') miaosha_end_time = miaosha_time.get('miaosha_end_time') # 将字符串转换为datetime类型 miaosha_begin_time = datetime.datetime.strptime(miaosha_begin_time, '%Y-%m-%d %H:%M:%S') miaosha_end_time = datetime.datetime.strptime(miaosha_end_time, '%Y-%m-%d %H:%M:%S') return miaosha_begin_time, miaosha_end_time def get_miaoshao_goods_info_list(self, data): ''' 得到秒杀商品有用信息 :param data: 待解析的data :return: 有用信息list ''' miaosha_goods_list = [] for item in data: # pprint(item) tmp = {} # 秒杀开始时间和结束时间 tmp['miaosha_time'] = { 'miaosha_begin_time': self.timestamp_to_regulartime(int(str(item.get('begin_time'))[0:10])), 'miaosha_end_time': self.timestamp_to_regulartime(int(str(item.get('end_time'))[0:10])), } # 折800商品地址 tmp['zid'] = item.get('zid') # 是否包邮 # tmp['is_baoyou'] = item.get('is_baoyou', 0) # 限时秒杀的库存信息 tmp['stock_info'] = { 'activity_stock': item.get('activity_stock', 0), # activity_stock为限时抢的剩余数量 'stock': item.get('stock', 0), # stock为限时秒杀的总库存 } # 原始价格 tmp['price'] = float(item.get('list_price')) # 秒杀的价格, float类型 tmp['taobao_price'] = float(item.get('price')) # 子标题 tmp['sub_title'] = item.get('description', '') miaosha_goods_list.append(tmp) # pprint(miaosha_goods_list) return miaosha_goods_list def is_recent_time(self, timestamp): ''' 判断是否在指定的日期差内 :param timestamp: 时间戳 :return: True or False ''' time_1 = int(timestamp) time_2 = time.time() # 当前的时间戳 time_1 = time.localtime(time_1) time_2 = time.localtime(time_2) if time_1.tm_year > time_2.tm_year: print('** 该年份为未来时间年份 **') if time_1.tm_hour >= SPIDER_START_HOUR and time_1.tm_hour <= SPIDER_END_HOUR: # 规定到SPIDER_START_HOUR点到SPIDER_END_HOUR点的商品信息 print('合法时间') # diff_days = abs(time_1.tm_mday - time_2.tm_mday) return True else: print('该小时在{0}点到{1}点以外,此处不处理跳过'.format(SPIDER_START_HOUR, SPIDER_END_HOUR)) return False if time_1.tm_year == time_2.tm_year: if time_1.tm_mon > time_2.tm_mon: # 先处理得到的time_1的月份大于当前月份的信息(即未来月份的) print('** 该月份为未来时间月份 **') if time_1.tm_hour >= SPIDER_START_HOUR and time_1.tm_hour <= SPIDER_END_HOUR: # 规定到SPIDER_START_HOUR点到SPIDER_END_HOUR点的商品信息 print('合法时间') # diff_days = abs(time_1.tm_mday - time_2.tm_mday) return True else: print('该小时在{0}点到{1}点以外,此处不处理跳过'.format(SPIDER_START_HOUR, SPIDER_END_HOUR)) return False if time_1.tm_mon >= time_2.tm_mon: # 如果目标时间的月份时间 >= 当前月份(月份合法, 表示是当前月份或者是今年其他月份) if time_1.tm_mday >= time_2.tm_mday-2: # 这样能抓到今天的前两天的信息 if time_1.tm_hour >= SPIDER_START_HOUR and time_1.tm_hour <= SPIDER_END_HOUR: # 规定到SPIDER_START_HOUR点到SPIDER_END_HOUR点的商品信息 print('合法时间') # diff_days = abs(time_1.tm_mday - time_2.tm_mday) return True else: print('该小时在{0}点到{1}点以外,此处不处理跳过'.format(SPIDER_START_HOUR, SPIDER_END_HOUR)) return False else: print('该日时间已过期, 此处跳过') return False else: # 月份过期 print('该月份时间已过期,此处跳过') return False else: print('非本年度的限时秒杀时间,此处跳过') return False def __del__(self): try: del self.my_phantomjs except: pass gc.collect()
class Zhe_800_Miaosha_Real_Time_Update(object): def __init__(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': 'zhe800.com', 'User-Agent': HEADERS[randint(0, 34)] # 随机一个请求头 } self.my_phantomjs = MyPhantomjs() def run_forever(self): ''' 这个实时更新的想法是只更新当天前天未来两小时的上架商品的信息,再未来信息价格(全为原价)暂不更新 :return: ''' #### 实时更新数据 tmp_sql_server = SqlServerMyPageInfoSaveItemPipeline() try: result = list( tmp_sql_server.select_zhe_800_xianshimiaosha_all_goods_id()) except TypeError as e: print('TypeError错误, 原因数据库连接失败...(可能维护中)') result = None if result is None: pass else: print('------>>> 下面是数据库返回的所有符合条件的goods_id <<<------') print(result) print('--------------------------------------------------------') print('即将开始实时更新数据, 请耐心等待...'.center(100, '#')) index = 1 for item in result: # 实时更新数据 miaosha_begin_time = json.loads( item[1]).get('miaosha_begin_time') miaosha_begin_time = int( str( time.mktime( time.strptime(miaosha_begin_time, '%Y-%m-%d %H:%M:%S')))[0:10]) # print(miaosha_begin_time) data = {} # 释放内存,在外面声明就会占用很大的,所以此处优化内存的方法是声明后再删除释放 zhe_800_miaosha = Zhe800Parse() if index % 50 == 0: # 每50次重连一次,避免单次长连无响应报错 print('正在重置,并与数据库建立新连接中...') tmp_sql_server = SqlServerMyPageInfoSaveItemPipeline() print('与数据库的新连接成功建立...') if tmp_sql_server.is_connect_success: if self.is_recent_time(miaosha_begin_time) == 0: tmp_sql_server.delete_zhe_800_expired_goods_id( goods_id=item[0]) print( '过期的goods_id为(%s)' % item[0], ', 限时秒杀开始时间为(%s), 删除成功!' % json.loads(item[1]).get('miaosha_begin_time')) elif self.is_recent_time(miaosha_begin_time) == 2: # break # 跳出循环 pass # 此处应该是pass,而不是break,因为数据库传回的goods_id不都是按照顺序的 else: # 返回1,表示在待更新区间内 print( '------>>>| 正在更新的goods_id为(%s) | --------->>>@ 索引值为(%d)' % (item[0], index)) data['goods_id'] = item[0] # print('------>>>| 爬取到的数据为: ', data) tmp_url = 'https://zapi.zhe800.com/zhe800_n_api/xsq/m/session_deals?session_id={0}&page=1&per_page=1000'.format( str(item[2])) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=tmp_url) body_1 = re.compile(r'<pre.*?>(.*)</pre>').findall( body) if body_1 != []: tmp_data = body_1[0] tmp_data = json.loads(tmp_data) # pprint(tmp_data) if tmp_data.get('data', {}).get('blocks', []) == []: # session_id不存在 print('该session_id不存在,此处跳过') pass else: tmp_data = [ item_s.get('deal', {}) for item_s in tmp_data.get('data', {}).get( 'blocks', []) ] if tmp_data != []: # 否则说明里面有数据 miaosha_goods_list = self.get_miaoshao_goods_info_list( data=tmp_data) # pprint(miaosha_goods_list) # 该session_id中现有的所有zid的list miaosha_goods_all_goods_id = [ i.get('zid') for i in miaosha_goods_list ] if item[0] not in miaosha_goods_all_goods_id: # 内部已经下架的 print('该商品已被下架限时秒杀活动,此处将其删除') tmp_sql_server.delete_zhe_800_expired_goods_id( goods_id=item[0]) print('下架的goods_id为(%s)' % item[0], ', 删除成功!') pass else: # 未下架的 for item_1 in miaosha_goods_list: if item_1.get('zid', '') == item[0]: zhe_800_miaosha.get_goods_data( goods_id=item[0]) goods_data = zhe_800_miaosha.deal_with_data( ) if goods_data == {}: # 返回的data为空则跳过 pass else: # 否则就解析并且插入 goods_data[ 'stock_info'] = item_1.get( 'stock_info') goods_data[ 'goods_id'] = str( item_1.get('zid')) # goods_data['username'] = '******' if item_1.get( 'stock_info' ).get('activity_stock' ) > 0: goods_data[ 'price'] = item_1.get( 'price') goods_data[ 'taobao_price'] = item_1.get( 'taobao_price') else: pass goods_data[ 'sub_title'] = item_1.get( 'sub_title') goods_data[ 'miaosha_time'] = item_1.get( 'miaosha_time') goods_data[ 'miaosha_begin_time'], goods_data[ 'miaosha_end_time'] = self.get_miaosha_begin_time_and_miaosha_end_time( miaosha_time= item_1.get( 'miaosha_time' )) # print(goods_data['stock_info']) # print(goods_data['miaosha_time']) zhe_800_miaosha.to_update_zhe_800_xianshimiaosha_table( data=goods_data, pipeline=tmp_sql_server ) else: pass else: # 说明这个sessionid没有数据, 就删除对应这个sessionid的限时秒杀商品 print('该sessionid没有相关key为jsons的数据') # return {} tmp_sql_server.delete_zhe_800_expired_goods_id( goods_id=item[0]) print( '过期的goods_id为(%s)' % item[0], ', 限时秒杀开始时间为(%s), 删除成功!' % json.loads( item[1]).get('miaosha_begin_time')) pass else: print('获取到的data为空!') # return {} pass else: # 表示返回的data值为空值 print('数据库连接失败,数据库可能关闭或者维护中') pass index += 1 # try: # del tmall # except: # pass # sleep(.8) gc.collect() print('全部数据更新完毕'.center(100, '#')) # sleep(60*60) if get_shanghai_time().hour == 0: # 0点以后不更新 sleep(60 * 60 * 5.5) else: sleep(5) # del ali_1688 gc.collect() def get_miaosha_begin_time_and_miaosha_end_time(self, miaosha_time): ''' 返回秒杀开始和结束时间 :param miaosha_time: :return: tuple miaosha_begin_time, miaosha_end_time ''' miaosha_begin_time = miaosha_time.get('miaosha_begin_time') miaosha_end_time = miaosha_time.get('miaosha_end_time') # 将字符串转换为datetime类型 miaosha_begin_time = datetime.datetime.strptime( miaosha_begin_time, '%Y-%m-%d %H:%M:%S') miaosha_end_time = datetime.datetime.strptime(miaosha_end_time, '%Y-%m-%d %H:%M:%S') return miaosha_begin_time, miaosha_end_time def is_recent_time(self, timestamp): ''' 判断是否在指定的日期差内 :param timestamp: 时间戳 :return: 0: 已过期恢复原价的 1: 待更新区间内的 2: 未来时间的 ''' time_1 = int(timestamp) time_2 = int(time.time()) # 当前的时间戳 diff_time = time_1 - time_2 if diff_time < -259200: # (为了后台能同步下架)所以设置为 72个小时, 只需要更新过去48小时和对与当前时间的未来2小时的商品信息 # if diff_time < -172800: # (原先的时间)48个小时, 只需要跟新过去48小时和对与当前时间的未来2小时的商品信息 return 0 # 已过期恢复原价的 elif diff_time > -172800 and diff_time < 7200: return 1 # 表示是昨天跟今天的也就是待更新的 else: return 2 # 未来时间的暂时不用更新 def get_miaoshao_goods_info_list(self, data): ''' 得到秒杀商品有用信息 :param data: 待解析的data :return: 有用信息list ''' miaosha_goods_list = [] for item in data: # pprint(item) tmp = {} # 秒杀开始时间和结束时间 tmp['miaosha_time'] = { 'miaosha_begin_time': self.timestamp_to_regulartime( int(str(item.get('begin_time'))[0:10])), 'miaosha_end_time': self.timestamp_to_regulartime( int(str(item.get('end_time'))[0:10])), } # 折800商品地址 tmp['zid'] = item.get('zid') # 是否包邮 # tmp['is_baoyou'] = item.get('is_baoyou', 0) # 限时秒杀的库存信息 tmp['stock_info'] = { 'activity_stock': item.get('activity_stock', 0), # activity_stock为限时抢的剩余数量 'stock': item.get('stock', 0), # stock为限时秒杀的总库存 } # 原始价格 tmp['price'] = float(item.get('list_price')) # 秒杀的价格, float类型 tmp['taobao_price'] = float(item.get('price')) # 子标题 tmp['sub_title'] = item.get('description', '') miaosha_goods_list.append(tmp) # pprint(miaosha_goods_list) return miaosha_goods_list def timestamp_to_regulartime(self, timestamp): ''' 将时间戳转换成时间 ''' # 利用localtime()函数将时间戳转化成localtime的格式 # 利用strftime()函数重新格式化时间 # 转换成localtime time_local = time.localtime(timestamp) # 转换成新的时间格式(2016-05-05 20:28:54) dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) return dt def __del__(self): try: del self.my_phantomjs except: pass gc.collect()
async def get_goods_data(self, jumei_pintuan_url): ''' 异步模拟得到原始data :param goods_id: :return: ''' goods_id = await self.get_goods_id_from_url(jumei_pintuan_url) if goods_id == []: self.result_data = {} return {} ''' 原先采用requests被过滤无返回结果, 于是用aiohttp无奈速度过慢, 换用phantomjs ''' # 拼团商品手机地址 goods_url = 'https://s.h5.jumei.com/yiqituan/detail?item_id={0}&type={1}'.format( goods_id[0], goods_id[1]) self.msg = '------>>>| 对应手机端地址为: ' + goods_url self.my_lg.info(self.msg) #** 获取ajaxDetail请求中的数据 tmp_url = 'https://s.h5.jumei.com/yiqituan/ajaxDetail?item_id={0}&type={1}'.format( str(goods_id[0]), [goods_id[1]][0]) # self.headers['Referer'] = goods_url # params = { # 'item_id': str(goods_id[0]), # 'type': [goods_id[1]][0], # } # body = await MyAiohttp.aio_get_url_body(url=tmp_url, headers=self.headers, params=params, timeout=JUMEIYOUPIN_PINTUAN_GOODS_TIMEOUT) # # 获取原始url的tmp_body # tmp_body = await MyAiohttp.aio_get_url_body(url=goods_url, headers=self.headers, timeout=JUMEIYOUPIN_PINTUAN_GOODS_TIMEOUT) # # print(tmp_body) ''' 换用phantomjs ''' my_phantomjs = MyPhantomjs() body = my_phantomjs.use_phantomjs_to_get_url_body(url=tmp_url) # print(body) try: body = re.compile('<pre .*?>(.*)</pre>').findall(body)[0] # print(body) except IndexError: body = '' tmp_body = my_phantomjs.use_phantomjs_to_get_url_body(url=goods_url) # print(tmp_body) try: del my_phantomjs except: pass if body == '' or tmp_body == '': self.msg = '获取到的body为空str!' + ' 出错地址: ' + goods_url self.my_lg.error(self.msg) self.result_data = {} return {} data = await self.json_2_dict(json_str=body) if data == {}: self.msg = '出错地址: ' + goods_url self.my_lg.error(self.msg) self.result_data = {} return {} data = await self.wash_data(data=data) data = data.get('data', {}) # pprint(data) try: data['title'] = data.get('share_info', [])[1].get('text', '') data['title'] = re.compile(r'聚美').sub('', data['title']) if len(data.get('buy_alone', {})) == 1: data['sub_title'] = '' else: data['sub_title'] = data.get('buy_alone', {}).get('name', '') data['sub_title'] = re.compile(r'聚美').sub( '', data['sub_title']) # print(data['title']) if data['title'] == '': self.my_lg.error('获取到的title为空值, 请检查!') raise Exception # shop_name if data.get('shop_info') == []: data['shop_name'] = '' else: data['shop_name'] = data.get('shop_info', {}).get('store_title', '') # print(data['shop_name']) # 获取所有示例图片 all_img_url = await self.get_all_img_url(data=data) data['all_img_url'] = all_img_url # 获取p_info p_info = await self.get_p_info(body=tmp_body) data['p_info'] = p_info # 获取div_desc div_desc = await self.get_div_desc(body=tmp_body) div_desc = await MyAiohttp.wash_html(div_desc) # print(div_desc) data['div_desc'] = div_desc # 上下架时间(拼团列表数据接口里面有这里先不获取) # 设置detail_name_list detail_name_list = await self.get_detail_name_list( size_attr=data.get('buy_alone', {}).get('size_attr', [])) data['detail_name_list'] = detail_name_list # 获取每个规格对应价格以及库存 true_sku_info = await self.get_true_sku_info( buy_alone_size=data.get('buy_alone', {}).get('size', []), size=data.get('size', []), group_single_price=data.get('group_single_price', '')) data['price_info_list'] = true_sku_info # is_delete product_status = data.get('product_status', '') is_delete = await self.get_is_delete(product_status=product_status, true_sku_info=true_sku_info) data['is_delete'] = is_delete # all_sell_count all_sell_count = data.get('buyer_number_text', '') if all_sell_count != '': all_sell_count = re.compile(r'(\d+\.?\d*)').findall( all_sell_count)[0] is_W = re.compile(r'万').findall(all_sell_count) if is_W != []: all_sell_count = str(int(float(all_sell_count) * 10000)) else: all_sell_count = '0' data['all_sell_count'] = all_sell_count data['goods_url'] = goods_url except Exception as e: self.msg = '遇到错误如下: ' + str(e) + ' 出错地址: ' + goods_url self.my_lg.error(self.msg) self.my_lg.exception(e) self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} if data != {}: # pprint(data) self.result_data = data return data else: self.msg = 'data为空!' + ' 出错地址: ' + goods_url self.my_lg.error(self.msg) self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {}
class ALi1688LoginAndParse(object): def __init__(self): super().__init__() self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': '1688.com', 'User-Agent': HEADERS[randint(0, 34)] # 随机一个请求头 } self.result_data = {} self.is_activity_goods = False self.my_phantomjs = MyPhantomjs() def get_ali_1688_data(self, goods_id): if goods_id == '': self.result_data = {} return {} # 阿里1688手机版地址: https://m.1688.com/offer/559836312862.html wait_to_deal_with_url = 'https://m.1688.com/offer/' + str( goods_id) + '.html' print('------>>>| 待处理的阿里1688地址为: ', wait_to_deal_with_url) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=wait_to_deal_with_url, css_selector='div.d-content') # print(body) if body == '': print('获取到的body为空str!请检查!') self.result_data = {} return {} # ''' # 改用requests # ''' # body = MyRequests.get_url_body(url=wait_to_deal_with_url, headers=self.headers) # # print(body) # # if body == '': # return {} # print(body) tmp_body = body try: pull_off_shelves = Selector( text=body).css('div.d-content p.info::text').extract_first() except: pull_off_shelves = '' if pull_off_shelves == '该商品无法查看或已下架': # 表示商品已下架, 同样执行插入数据操作 try: tmp_my_pipeline = SqlServerMyPageInfoSaveItemPipeline() is_in_db = list( tmp_my_pipeline.select_the_goods_id_is_in_ali_1688_table( goods_id=goods_id)) # print(is_in_db) except Exception: print('数据库连接失败!') self.result_data = {} return {} if is_in_db != []: # 表示该goods_id以前已被插入到db中, 于是只需要更改其is_delete的状态即可 tmp_my_pipeline.update_ali_1688_expired_goods_id_to_is_delete( goods_id=goods_id) print('@@@ 该商品goods_id原先存在于db中, 此处将其is_delete=1') tmp_data_s = self.init_pull_off_shelves_goods() # 初始化下架商品的属性 tmp_data_s['before'] = True # 用来判断原先该goods是否在db中 self.result_data = {} return tmp_data_s else: # 表示该goods_id没存在于db中 print('@@@ 该商品已下架[但未存在于db中], ** 此处将其插入到db中...') tmp_data_s = self.init_pull_off_shelves_goods() # 初始化下架商品的属性 tmp_data_s['before'] = False self.result_data = {} return tmp_data_s body = re.compile(r'{"beginAmount"(.*?)</script></div></div>').findall( body) if body != []: body = body[0] body = r'{"beginAmount"' + body # print(body) body = json.loads(body) # pprint(body) if body.get('discountPriceRanges') is not None: # 过滤无用属性 try: body.pop('action') body.pop('offerSign') body.pop('rateDsrItems') body.pop('rateStarLevelMapOfMerge') body.pop('wirelessVideoInfo') body.pop('freightCost') except KeyError as e: print('KeyError错误, 此处跳过!') pass # pprint(body) self.result_data = body return self.result_data else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: print('解析ing..., 该商品正在参与火拼, 此处为火拼价, 为短期活动价格!') body = re.compile( r'{"activityId"(.*?)</script></div></div>').findall(tmp_body) if body != []: body = body[0] body = r'{"activityId"' + body # print(body) body = json.loads(body) # pprint(body) if body.get('discountPriceRanges') is not None: # 过滤无用属性 try: body.pop('action') body.pop('offerSign') body.pop('rateDsrItems') body.pop('rateStarLevelMapOfMerge') body.pop('wirelessVideoInfo') body.pop('freightCost') except KeyError as e: print('KeyError错误, 此处跳过!') pass # pprint(body) self.result_data = body self.is_activity_goods = True return self.result_data else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: print('这个商品对应活动属性未知, 此处不解析, 设置为跳过!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} def deal_with_data(self): ''' 处理返回的result_data, 并返回需要的信息 :return: 字典类型 ''' data = self.result_data if data != {}: # 公司名称 company_name = data.get('companyName') # 商品名称 title = data.get('subject') # 卖家姓名 link_name = '' # 商品价格信息, 及其对应起批量 [{'price': '119.00', 'begin': '3'}, ...] price_info = [] if self.is_activity_goods: # 火拼商品处理 tmp = {} tmp_price = data.get('ltPromotionPriceDisplay') tmp_trade_number = data.get('beginAmount') tmp['begin'] = tmp_trade_number tmp['price'] = tmp_price price_info.append(tmp) else: # 常规商品处理 if data.get( 'isLimitedTimePromotion' ) == 'false': # isLimitedTimePromotion 限时优惠, 'true'表示限时优惠价, 'flase'表示非限时优惠 price_info = data.get('discountPriceRanges') for item in price_info: try: item.pop('convertPrice') except KeyError: pass # print(price_info) else: # 限时优惠 tmp = { 'begin': data.get('beginAmount', ''), 'price': data.get('skuDiscountPrice', '') } price_info.append(tmp) # print(price_info) # 标签属性名称及其对应的值 (可能有图片(url), 无图(imageUrl=None)) [{'value': [{'imageUrl': 'https://cbu01.alicdn.com/img/ibank/2017/520/684/4707486025_608602289.jpg', 'name': '白色'}, {'imageUrl': 'https://cbu01.alicdn.com/img/ibank/2017/554/084/4707480455_608602289.jpg', 'name': '卡其色'}, {'imageUrl': 'https://cbu01.alicdn.com/img/ibank/2017/539/381/4705183935_608602289.jpg', 'name': '黑色'}], 'prop': '颜色'}, {'value': [{'imageUrl': None, 'name': 'L'}, {'imageUrl': None, 'name': 'XL'}, {'imageUrl': None, 'name': '2XL'}], 'prop': '尺码'}] sku_props = data.get('skuProps') # print(sku_props) if sku_props is not None: # 这里还是保留unit为单位值 # for item in sku_props: # try: # item.pop('unit') # except KeyError: # print('KeyError, [unit], 此处跳过') pass else: sku_props = [] # 存在没有规格属性的 # print(sku_props) # 每个规格对应价格, 及其库存量 ''' skuMap == SKUInfo ''' tmp_sku_map = data.get('skuMap') if tmp_sku_map is not None: sku_map = [] for key, value in tmp_sku_map.items(): tmp = {} # 处理key得到需要的值 key = re.compile(r'>').sub('|', key) tmp['spec_type'] = key # 处理value得到需要的值 # pprint(price_info) if value.get('discountPrice') is None: # 如果没有折扣价, 价格就为起批价 try: value['discountPrice'] = price_info[0].get('price') except IndexError: print('获取价格失败, 此处跳过!') self.result_data = {} return {} else: if self.is_activity_goods: pass else: if data.get('isLimitedTimePromotion') == 'false': if float(value.get('discountPrice')) < float( price_info[0].get('price')): value['discountPrice'] = price_info[0].get( 'price') else: pass else: pass try: value.pop('skuId') except KeyError: pass try: value.pop('specId') except KeyError: pass try: value.pop('saleCount') except KeyError: pass try: value.pop('discountStandardPrice') except KeyError: pass try: value.pop('price') except KeyError: pass try: value.pop('retailPrice') except KeyError: pass try: value.pop('standardPrice') except KeyError: # print('KeyError, [skuId, specId, saleCount]错误, 此处跳过') pass tmp['spec_value'] = value sku_map.append(tmp) else: sku_map = [] # 存在没有规格时的情况 # pprint(sku_map) # 所有示例图片地址 tmp_all_img_url = data.get('imageList') if tmp_all_img_url is not None: all_img_url = [] for item in tmp_all_img_url: tmp = {} try: item.pop('size310x310URL') except KeyError: # print('KeyError, [size310x310URL], 此处设置为跳过') pass tmp['img_url'] = item['originalImageURI'] all_img_url.append(tmp) else: all_img_url = [] # pprint(all_img_url) # 详细信息的标签名, 及其对应的值 tmp_property_info = data.get('productFeatureList') if tmp_property_info is not None: property_info = [] for item in tmp_property_info: try: item.pop('unit') except KeyError: # print('KeyError, [unit], 此处设置为跳过') pass item['id'] = '0' property_info = tmp_property_info else: property_info = [] # pprint(property_info) # 下方详细div块 detail_info_url = data.get('detailUrl') if detail_info_url is not None: # print(detail_info_url) detail_info = self.get_detail_info_url_div(detail_info_url) else: detail_info = '' # print(detail_info) if re.compile(r'下架').findall(title) != []: if re.compile(r'待下架').findall(title) != []: is_delete = 0 else: is_delete = 1 else: is_delete = 0 # 逻辑删除, 未删除为0, 删除为1 result = { 'company_name': company_name, # 公司名称 'title': title, # 商品名称 'link_name': link_name, # 卖家姓名 'price_info': price_info, # 商品价格信息, 及其对应起批量 'sku_props': sku_props, # 标签属性名称及其对应的值 (可能有图片(url), 无图(imageUrl=None)) 'sku_map': sku_map, # 每个规格对应价格, 及其库存量 'all_img_url': all_img_url, # 所有示例图片地址 'property_info': property_info, # 详细信息的标签名, 及其对应的值 'detail_info': detail_info, # 下方详细div块 'is_delete': is_delete, # 判断是否下架 } # pprint(result) # print(result) # print('------>>>| 爬到goods_id(%s)对应的数据: |', result) # print() # wait_to_send_data = { # 'reason': 'success', # 'data': result, # 'code': 1 # } # json_data = json.dumps(wait_to_send_data, ensure_ascii=False) # print(json_data) # 重置self.is_activity_goods = False self.is_activity_goods = False return result else: print('待处理的data为空值!') self.is_activity_goods = False return {} def to_right_and_update_data(self, data, pipeline): data_list = data tmp = {} tmp['goods_id'] = data_list['goods_id'] # 官方商品id # now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') ''' 时区处理,时间处理到上海时间 ''' tz = pytz.timezone('Asia/Shanghai') # 创建时区对象 now_time = datetime.datetime.now(tz) # 处理为精确到秒位,删除时区信息 now_time = re.compile(r'\..*').sub('', str(now_time)) # 将字符串类型转换为datetime类型 now_time = datetime.datetime.strptime(now_time, '%Y-%m-%d %H:%M:%S') # tmp['deal_with_time'] = now_time # 操作时间 tmp['modfiy_time'] = now_time # 修改时间 tmp['company_name'] = data_list['company_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['link_name'] = data_list['link_name'] # 卖家姓名 # 设置最高价price, 最低价taobao_price if len(data_list['price_info']) > 1: tmp_ali_price = [] for item in data_list['price_info']: tmp_ali_price.append(float(item.get('price'))) if tmp_ali_price == []: tmp['price'] = Decimal(0).__round__(2) tmp['taobao_price'] = Decimal(0).__round__(2) else: tmp['price'] = Decimal(sorted(tmp_ali_price)[-1]).__round__( 2) # 得到最大值并转换为精度为2的decimal类型 tmp['taobao_price'] = Decimal( sorted(tmp_ali_price)[0]).__round__(2) elif len( data_list['price_info'] ) == 1: # 由于可能是促销价, 只有一组然后价格 类似[{'begin': '1', 'price': '485.46-555.06'}] if re.compile(r'-').findall( data_list['price_info'][0].get('price')) != []: tmp_price_range = data_list['price_info'][0].get('price') tmp_price_range = tmp_price_range.split('-') tmp['price'] = tmp_price_range[1] tmp['taobao_price'] = tmp_price_range[0] else: tmp['price'] = Decimal( data_list['price_info'][0].get('price')).__round__( 2) # 得到最大值并转换为精度为2的decimal类型 tmp['taobao_price'] = tmp['price'] else: # 少于1 tmp['price'] = Decimal(0).__round__(2) tmp['taobao_price'] = Decimal(0).__round__(2) tmp['price_info'] = data_list['price_info'] # 价格信息 # print(tmp['price'], print(tmp['taobao_price'])) # print(tmp['price_info']) spec_name = [] for item in data_list['sku_props']: tmp_dic = {} tmp_dic['spec_name'] = item.get('prop') spec_name.append(tmp_dic) tmp['spec_name'] = spec_name # 标签属性名称 """ 得到sku_map """ tmp['sku_map'] = data_list.get('sku_map') # 每个规格对应价格及其库存 tmp['all_img_url_info'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['property_info'] = data_list.get('property_info') # 详细信息 tmp['detail_info'] = data_list.get('detail_info') # 下方div tmp['is_delete'] = data_list.get('is_delete') tmp['my_shelf_and_down_time'] = data_list.get('my_shelf_and_down_time') tmp['delete_time'] = data_list.get('delete_time') # print('------>>> | 待存储的数据信息为: |', tmp) pipeline.update_table(tmp) def init_pull_off_shelves_goods(self): ''' 初始化原先就下架的商品信息 :return: ''' is_delete = 1 result = { 'company_name': '', # 公司名称 'title': '', # 商品名称 'link_name': '', # 卖家姓名 'price_info': [], # 商品价格信息, 及其对应起批量 'sku_props': [], # 标签属性名称及其对应的值 (可能有图片(url), 无图(imageUrl=None)) 'sku_map': [], # 每个规格对应价格, 及其库存量 'all_img_url': [], # 所有示例图片地址 'property_info': [], # 详细信息的标签名, 及其对应的值 'detail_info': '', # 下方详细div块 'is_delete': is_delete, # 判断是否下架 } return result def old_ali_1688_goods_insert_into_new_table(self, data, pipeline): data_list = data tmp = {} tmp['main_goods_id'] = data_list['main_goods_id'] tmp['username'] = data_list['username'] tmp['goods_id'] = data_list['goods_id'] # 官方商品id tmp['spider_url'] = data_list['goods_url'] # now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') ''' 时区处理,时间处理到上海时间 ''' tz = pytz.timezone('Asia/Shanghai') # 创建时区对象 now_time = datetime.datetime.now(tz) # 处理为精确到秒位,删除时区信息 now_time = re.compile(r'\..*').sub('', str(now_time)) # 将字符串类型转换为datetime类型 now_time = datetime.datetime.strptime(now_time, '%Y-%m-%d %H:%M:%S') tmp['deal_with_time'] = now_time # 操作时间 tmp['modfiy_time'] = now_time # 修改时间 tmp['company_name'] = data_list['company_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['link_name'] = data_list['link_name'] # 卖家姓名 # 设置最高价price, 最低价taobao_price if len(data_list['price_info']) > 1: tmp_ali_price = [] for item in data_list['price_info']: tmp_ali_price.append(float(item.get('price'))) if tmp_ali_price == []: tmp['price'] = Decimal(0).__round__(2) tmp['taobao_price'] = Decimal(0).__round__(2) else: tmp['price'] = Decimal(sorted(tmp_ali_price)[-1]).__round__( 2) # 得到最大值并转换为精度为2的decimal类型 tmp['taobao_price'] = Decimal( sorted(tmp_ali_price)[0]).__round__(2) elif len( data_list['price_info'] ) == 1: # 由于可能是促销价, 只有一组然后价格 类似[{'begin': '1', 'price': '485.46-555.06'}] if re.compile(r'-').findall( data_list['price_info'][0].get('price')) != []: tmp_price_range = data_list['price_info'][0].get('price') tmp_price_range = tmp_price_range.split('-') tmp['price'] = tmp_price_range[1] tmp['taobao_price'] = tmp_price_range[0] else: tmp['price'] = Decimal( data_list['price_info'][0].get('price')).__round__( 2) # 得到最大值并转换为精度为2的decimal类型 tmp['taobao_price'] = tmp['price'] else: # 少于1 tmp['price'] = Decimal(0).__round__(2) tmp['taobao_price'] = Decimal(0).__round__(2) tmp['price_info'] = data_list['price_info'] # 价格信息 # print(tmp['price'], print(tmp['taobao_price'])) # print(tmp['price_info']) spec_name = [] for item in data_list['sku_props']: tmp_dic = {} tmp_dic['spec_name'] = item.get('prop') spec_name.append(tmp_dic) tmp['spec_name'] = spec_name # 标签属性名称 """ 得到sku_map """ tmp['sku_map'] = data_list.get('sku_map') # 每个规格对应价格及其库存 tmp['all_img_url_info'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['property_info'] = data_list.get('property_info') # 详细信息 tmp['detail_info'] = data_list.get('detail_info') # 下方div tmp['site_id'] = 2 # 阿里1688 tmp['is_delete'] = data_list['is_delete'] # tmp['my_shelf_and_down_time'] = data_list.get('my_shelf_and_down_time') # tmp['delete_time'] = data_list.get('delete_time') # print('------>>> | 待存储的数据信息为: |', tmp) pipeline.old_ali_1688_goods_insert_into_new_table(tmp) def get_detail_info_url_div(self, detail_info_url): ''' 此处过滤得到data_tfs_url的div块 :return: ''' detail_info = '' # print(detail_info_url) if re.compile(r'https').findall(detail_info_url) == []: detail_info_url = 'https:' + detail_info_url # print(detail_info_url) else: pass # data_tfs_url_response = requests.get(detail_info_url, headers=self.headers) # data_tfs_url_body = data_tfs_url_response.content.decode('gbk') data_tfs_url_body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=detail_info_url) # ''' # 改用requests # ''' # body = MyRequests.get_url_body(url=detail_info_url, headers=self.headers) # print(body) # if body == '': # detail_info = '' # # data_tfs_url_body = body is_offer_details = re.compile(r'offer_details').findall( data_tfs_url_body) if is_offer_details != []: data_tfs_url_body = re.compile(r'.*?{"content":"(.*?)"};').findall( data_tfs_url_body) # print(body) if data_tfs_url_body != []: detail_info = data_tfs_url_body[0] detail_info = re.compile(r'\\').sub('', detail_info) detail_info = re.compile(r'<').sub( '<', detail_info ) # self.driver.page_source转码成字符串时'<','>'都被替代成><此外还有其他也类似被替换 detail_info = re.compile(r'>').sub('>', detail_info) detail_info = re.compile(r'&').sub('&', detail_info) detail_info = re.compile(r' ').sub(' ', detail_info) else: detail_info = '' else: is_desc = re.compile(r'var desc=').findall(data_tfs_url_body) if is_desc != []: desc = re.compile(r'var desc=\'(.*)\';').findall( data_tfs_url_body) if desc != []: detail_info = desc[0] detail_info = re.compile(r'<').sub('<', detail_info) detail_info = re.compile(r'>').sub('>', detail_info) detail_info = re.compile(r'&').sub('&', detail_info) detail_info = re.compile(r' ').sub(' ', detail_info) detail_info = re.compile(r'src=\"https:').sub( 'src=\"', detail_info) # 先替换部分带有https的 detail_info = re.compile(r'src="').sub( 'src=\"https:', detail_info) # 再把所欲的换成https的 else: detail_info = '' # print(detail_info) return detail_info def get_goods_id_from_url(self, ali_1688_url): # https://detail.1688.com/offer/559526148757.html?spm=b26110380.sw1688.mof001.28.sBWF6s is_ali_1688_url = re.compile( r'https://detail.1688.com/offer/.*?').findall(ali_1688_url) if is_ali_1688_url != []: ali_1688_url = re.compile( r'https://detail.1688.com/offer/(.*?).html.*?').findall( ali_1688_url)[0] print('------>>>| 得到的阿里1688商品id为:', ali_1688_url) return ali_1688_url else: print( '阿里1688商品url错误, 非正规的url, 请参照格式(https://detail.1688.com/offer/)开头的...' ) return '' def __del__(self): try: del self.my_phantomjs except Exception: print("self.my_phantomjs释放失败!") pass gc.collect()
class ALi1688LoginAndParse(object): def __init__(self): super().__init__() self._set_headers() self.result_data = {} self.is_activity_goods = False self.my_phantomjs = MyPhantomjs() def _set_headers(self): self.headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Encoding:': 'gzip', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Cache-Control': 'max-age=0', 'Connection': 'keep-alive', 'Host': '1688.com', 'User-Agent': HEADERS[randint(0, 34)] # 随机一个请求头 } def get_ali_1688_data(self, goods_id): if goods_id == '': self.result_data = {} return {} # 阿里1688手机版地址: https://m.1688.com/offer/559836312862.html wait_to_deal_with_url = 'https://m.1688.com/offer/' + str( goods_id) + '.html' print('------>>>| 待处理的阿里1688地址为: ', wait_to_deal_with_url) body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=wait_to_deal_with_url, css_selector='div.d-content') # print(body) if body == '': print('获取到的body为空str!请检查!') self.result_data = {} return {} # ''' # 改用requests # ''' # body = MyRequests.get_url_body(url=wait_to_deal_with_url, headers=self.headers) # # print(body) # # if body == '': # return {} # print(body) tmp_body = body try: pull_off_shelves = Selector( text=body).css('div.d-content p.info::text').extract_first() except: pull_off_shelves = '' if pull_off_shelves == '该商品无法查看或已下架': # 表示商品已下架, 同样执行插入数据操作 # print('test') try: tmp_my_pipeline = SqlServerMyPageInfoSaveItemPipeline() sql_str = 'select GoodsID from dbo.GoodsInfoAutoGet where SiteID=2 and GoodsID=%s' is_in_db = tmp_my_pipeline._select_table( sql_str=sql_str, params=(str(goods_id), )) # print(is_in_db) except Exception as e: print('遇到错误:', e) print('数据库连接失败!') self.result_data = {} return {} if is_in_db != []: # 表示该goods_id以前已被插入到db中, 于是只需要更改其is_delete的状态即可 sql_str = 'update dbo.GoodsInfoAutoGet set IsDelete=1 where GoodsID=%s' tmp_my_pipeline._update_table(sql_str=sql_str, params=(goods_id)) print('@@@ 该商品goods_id原先存在于db中, 此处将其is_delete=1') tmp_data_s = self.init_pull_off_shelves_goods() # 初始化下架商品的属性 tmp_data_s['before'] = True # 用来判断原先该goods是否在db中 self.result_data = {} return tmp_data_s else: # 表示该goods_id没存在于db中 print('@@@ 该商品已下架[但未存在于db中], ** 此处将其插入到db中...') tmp_data_s = self.init_pull_off_shelves_goods() # 初始化下架商品的属性 tmp_data_s['before'] = False self.result_data = {} return tmp_data_s body = re.compile(r'{"beginAmount"(.*?)</script></div></div>').findall( body) if body != []: body = body[0] body = r'{"beginAmount"' + body # print(body) body = json.loads(body) # pprint(body) if body.get('discountPriceRanges') is not None: self.result_data = self._wash_discountPriceRanges(body=body) return self.result_data else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: print('解析ing..., 该商品正在参与火拼, 此处为火拼价, 为短期活动价格!') body = re.compile( r'{"activityId"(.*?)</script></div></div>').findall(tmp_body) if body != []: body = body[0] body = r'{"activityId"' + body # print(body) body = json.loads(body) # pprint(body) if body.get('discountPriceRanges') is not None: self.result_data = self._wash_discountPriceRanges( body=body) self.is_activity_goods = True return self.result_data else: print('data为空!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} else: print('这个商品对应活动属性未知, 此处不解析, 设置为跳过!') self.result_data = {} # 重置下,避免存入时影响下面爬取的赋值 return {} def deal_with_data(self): ''' 处理返回的result_data, 并返回需要的信息 :return: 字典类型 ''' data = self.result_data if data != {}: # 公司名称 company_name = data.get('companyName', '') # 商品名称 title = self._wash_sensitive_words(data.get('subject', '')) # 卖家姓名 link_name = '' # 商品价格信息, 及其对应起批量 [{'price': '119.00', 'begin': '3'}, ...] price_info = self._get_price_info(data=data) # print(price_info) # 标签属性名称及其对应的值 # (可能有图片(url), 无图(imageUrl=None)) [{'value': [{'imageUrl': 'https://cbu01.alicdn.com/img/ibank/2017/520/684/4707486025_608602289.jpg', 'name': '白色'}, {'imageUrl': 'https://cbu01.alicdn.com/img/ibank/2017/554/084/4707480455_608602289.jpg', 'name': '卡其色'}, {'imageUrl': 'https://cbu01.alicdn.com/img/ibank/2017/539/381/4705183935_608602289.jpg', 'name': '黑色'}], 'prop': '颜色'}, {'value': [{'imageUrl': None, 'name': 'L'}, {'imageUrl': None, 'name': 'XL'}, {'imageUrl': None, 'name': '2XL'}], 'prop': '尺码'}] sku_props = self._get_sku_props(data=data) # print(sku_props) # 每个规格对应价格, 及其库存量 try: sku_map = self._get_sku_map(data=data, price_info=price_info) # pprint(sku_map) except Exception as e: print('获取sku_map时, 遇到错误:', e) self.is_activity_goods = False self.result_data = {} return {} # 最高价和最低价 price, taobao_price = self._get_price(price_info=price_info) # 所有示例图片地址 all_img_url = self._get_all_img_url(data=data) # pprint(all_img_url) # 详细信息的标签名, 及其对应的值 property_info = self._get_p_info(data=data) # pprint(property_info) # 下方详细div块 detail_info_url = data.get('detailUrl') if detail_info_url is not None: # print(detail_info_url) detail_info = self.get_detail_info_url_div(detail_info_url) else: detail_info = '' # print(detail_info) is_delete = self._get_is_delete(title=title) result = { 'company_name': company_name, # 公司名称 'title': title, # 商品名称 'link_name': link_name, # 卖家姓名 'price_info': price_info, # 商品价格信息, 及其对应起批量 'price': price, # 起批的最高价 'taobao_price': taobao_price, # 起批的最低价 'sku_props': sku_props, # 标签属性名称及其对应的值 (可能有图片(url), 无图(imageUrl=None)) 'sku_map': sku_map, # 每个规格对应价格, 及其库存量 'all_img_url': all_img_url, # 所有示例图片地址 'property_info': property_info, # 详细信息的标签名, 及其对应的值 'detail_info': detail_info, # 下方详细div块 'is_delete': is_delete, # 判断是否下架 } # pprint(result) # print(result) # wait_to_send_data = { # 'reason': 'success', # 'data': result, # 'code': 1 # } # json_data = json.dumps(wait_to_send_data, ensure_ascii=False) # print(json_data) # 重置self.is_activity_goods = False self.is_activity_goods = False return result else: print('待处理的data为空值!') self.is_activity_goods = False return {} def to_right_and_update_data(self, data, pipeline): data_list = data tmp = GoodsItem() tmp['goods_id'] = data_list['goods_id'] # 官方商品id now_time = get_shanghai_time() # tmp['deal_with_time'] = now_time # 操作时间 tmp['modify_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['company_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['link_name'] = data_list['link_name'] # 卖家姓名 # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['price_info'] = data_list['price_info'] # 价格信息 # print(tmp['price'], print(tmp['taobao_price'])) # print(tmp['price_info']) spec_name = [] for item in data_list['sku_props']: tmp_dic = {} tmp_dic['spec_name'] = item.get('prop') spec_name.append(tmp_dic) tmp['detail_name_list'] = spec_name # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get('sku_map') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('property_info') # 详细信息 tmp['div_desc'] = data_list.get('detail_info') # 下方div tmp['is_delete'] = data_list.get('is_delete') tmp['my_shelf_and_down_time'] = data_list.get('my_shelf_and_down_time') tmp['delete_time'] = data_list.get('delete_time') tmp['is_price_change'] = data_list.get('_is_price_change') tmp['price_change_info'] = data_list.get('_price_change_info') # print('------>>> | 待存储的数据信息为: |', tmp) params = self._get_db_update_params(item=tmp) # 改价格的sql语句 # sql_str = r'update dbo.GoodsInfoAutoGet set ModfiyTime = %s, ShopName=%s, GoodsName=%s, LinkName=%s, Price=%s, TaoBaoPrice=%s, PriceInfo=%s, SKUName=%s, SKUInfo=%s, ImageUrl=%s, DetailInfo=%s, PropertyInfo=%s, MyShelfAndDownTime=%s, delete_time=%s, IsDelete=%s, IsPriceChange=%s, PriceChangeInfo=%s where GoodsID = %s' # 不改价格的sql语句 sql_str = r'update dbo.GoodsInfoAutoGet set ModfiyTime = %s, ShopName=%s, GoodsName=%s, LinkName=%s, PriceInfo=%s, SKUName=%s, SKUInfo=%s, ImageUrl=%s, DetailInfo=%s, PropertyInfo=%s, MyShelfAndDownTime=%s, delete_time=%s, IsDelete=%s, IsPriceChange=%s, PriceChangeInfo=%s where GoodsID = %s' pipeline._update_table(sql_str=sql_str, params=params) def _get_sku_props(self, **kwargs): ''' 得到sku_props :param kwargs: :return: ''' data = kwargs.get('data', {}) sku_props = data.get('skuProps') # print(sku_props) if sku_props is not None: # 这里还是保留unit为单位值 # for item in sku_props: # try: # item.pop('unit') # except KeyError: # print('KeyError, [unit], 此处跳过') pass else: sku_props = [] # 存在没有规格属性的 return sku_props def _get_price_info(self, **kwargs): ''' 得到price_info :return: ''' data = kwargs.get('data', {}) # 商品价格信息, 及其对应起批量 [{'price': '119.00', 'begin': '3'}, ...] price_info = [] if self.is_activity_goods: # 火拼商品处理 tmp = {} tmp_price = data.get('ltPromotionPriceDisplay') tmp_trade_number = data.get('beginAmount') tmp['begin'] = tmp_trade_number tmp['price'] = tmp_price price_info.append(tmp) else: # 常规商品处理 if data.get( 'isLimitedTimePromotion', 'true' ) == 'false': # isLimitedTimePromotion 限时优惠, 'true'表示限时优惠价, 'flase'表示非限时优惠 price_info = data.get('discountPriceRanges') for item in price_info: try: item.pop('convertPrice') except KeyError: pass # print(price_info) else: # 限时优惠 tmp = { 'begin': data.get('beginAmount', ''), 'price': data.get('skuDiscountPrice', '') } price_info.append(tmp) return price_info def _get_sku_map(self, **kwargs): ''' 得到sku_map :param kwargs: :return: ''' # 每个规格对应价格, 及其库存量 '''skuMap == SKUInfo''' data = kwargs.get('data', {}) price_info = kwargs.get('price_info', []) tmp_sku_map = data.get('skuMap') if tmp_sku_map is not None: sku_map = [] for key, value in tmp_sku_map.items(): tmp = {} # 处理key得到需要的值 key = re.compile(r'>').sub('|', key) tmp['spec_type'] = key # 处理value得到需要的值 # pprint(price_info) if value.get('discountPrice') is None: # 如果没有折扣价, 价格就为起批价 try: value['discountPrice'] = price_info[0].get('price') except IndexError: print('获取价格失败, 此处跳过!') raise IndexError else: if self.is_activity_goods: pass else: if data.get('isLimitedTimePromotion') == 'false': if float(value.get('discountPrice')) < float( price_info[0].get('price')): value['discountPrice'] = price_info[0].get( 'price') else: pass else: pass tmp['spec_value'] = self._wash_sku_value(value=value) sku_map.append(tmp) else: sku_map = [] # 存在没有规格时的情况 return sku_map def _get_all_img_url(self, **kwargs): ''' 得到all_img_url :param kwargs: :return: ''' data = kwargs.get('data', {}) tmp_all_img_url = data.get('imageList') if tmp_all_img_url is not None: all_img_url = [] for item in tmp_all_img_url: tmp = {} try: item.pop('size310x310URL') except KeyError: # print('KeyError, [size310x310URL], 此处设置为跳过') pass tmp['img_url'] = item['originalImageURI'] all_img_url.append(tmp) else: all_img_url = [] return all_img_url def _get_p_info(self, **kwargs): ''' 得到p_info :param kwargs: :return: ''' data = kwargs.get('data', {}) property_info = [] tmp_property_info = data.get('productFeatureList') if tmp_property_info is not None: for item in tmp_property_info: try: item.pop('unit') except KeyError: # print('KeyError, [unit], 此处设置为跳过') pass item['id'] = '0' property_info = tmp_property_info else: pass return property_info def _get_is_delete(self, **kwargs): ''' 得到is_delete :param kwargs: :return: ''' title = kwargs.get('title') is_delete = 0 if re.compile(r'下架').findall(title) != []: if re.compile(r'待下架').findall(title) != []: pass else: is_delete = 1 else: pass return is_delete def _wash_sku_value(self, value): ''' 清洗value :param value: :return: ''' try: value.pop('skuId') except KeyError: pass try: value.pop('specId') except KeyError: pass try: value.pop('saleCount') except KeyError: pass try: value.pop('discountStandardPrice') except KeyError: pass try: value.pop('price') except KeyError: pass try: value.pop('retailPrice') except KeyError: pass try: value.pop('standardPrice') except KeyError: # print('KeyError, [skuId, specId, saleCount]错误, 此处跳过') pass return value def _wash_sensitive_words(self, word): ''' 清洗敏感字眼 :param word: :return: ''' word = re.compile(r'淘宝网').sub('', word) return word def _wash_discountPriceRanges(self, body): ''' 清洗discountPriceRanges :param body: :return: ''' # 过滤无用属性 try: body.pop('action') body.pop('offerSign') body.pop('rateDsrItems') body.pop('rateStarLevelMapOfMerge') body.pop('wirelessVideoInfo') body.pop('freightCost') except KeyError: print('KeyError错误, 此处跳过!') pass return body def _get_db_update_params(self, item): ''' 得到待存储的params :param item: :return: tuple ''' params = ( item['modify_time'], item['shop_name'], item['title'], item['link_name'], # item['price'], # item['taobao_price'], dumps(item['price_info'], ensure_ascii=False), dumps(item['detail_name_list'], ensure_ascii=False), dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), item['div_desc'], dumps(item['p_info'], ensure_ascii=False), dumps(item['my_shelf_and_down_time'], ensure_ascii=False), item['delete_time'], item['is_delete'], item['is_price_change'], dumps(item['price_change_info'], ensure_ascii=False), item['goods_id'], ) return params def _get_price(self, price_info): ''' 获取商品的最高价跟最低价 :param price_info: :return: price, taobao_price type float ''' # 设置最高价price, 最低价taobao_price if len(price_info) > 1: tmp_ali_price = [] for item in price_info: tmp_ali_price.append(float(item.get('price'))) if tmp_ali_price == []: price = Decimal(0).__round__(2) taobao_price = Decimal(0).__round__(2) else: price = Decimal(sorted(tmp_ali_price)[-1]).__round__( 2) # 得到最大值并转换为精度为2的decimal类型 taobao_price = Decimal(sorted(tmp_ali_price)[0]).__round__(2) elif len( price_info ) == 1: # 由于可能是促销价, 只有一组然后价格 类似[{'begin': '1', 'price': '485.46-555.06'}] if re.compile(r'-').findall(price_info[0].get('price')) != []: tmp_price_range = price_info[0].get('price') tmp_price_range = tmp_price_range.split('-') price = tmp_price_range[1] taobao_price = tmp_price_range[0] else: price = Decimal(price_info[0].get('price')).__round__( 2) # 得到最大值并转换为精度为2的decimal类型 taobao_price = price else: # 少于1 price = Decimal(0).__round__(2) taobao_price = Decimal(0).__round__(2) return float(price), float(taobao_price) def init_pull_off_shelves_goods(self): ''' 初始化原先就下架的商品信息 :return: ''' is_delete = 1 result = { 'company_name': '', # 公司名称 'title': '', # 商品名称 'link_name': '', # 卖家姓名 'price_info': [], # 商品价格信息, 及其对应起批量 'price': 0, 'taobao_price': 0, 'sku_props': [], # 标签属性名称及其对应的值 (可能有图片(url), 无图(imageUrl=None)) 'sku_map': [], # 每个规格对应价格, 及其库存量 'all_img_url': [], # 所有示例图片地址 'property_info': [], # 详细信息的标签名, 及其对应的值 'detail_info': '', # 下方详细div块 'is_delete': is_delete, # 判断是否下架 } return result def old_ali_1688_goods_insert_into_new_table(self, data, pipeline): data_list = data tmp = GoodsItem() tmp['main_goods_id'] = data_list.get('main_goods_id') tmp['goods_id'] = data_list['goods_id'] # 官方商品id tmp['goods_url'] = data_list['goods_url'] tmp['username'] = data_list['username'] now_time = get_shanghai_time() tmp['create_time'] = now_time # 操作时间 tmp['modify_time'] = now_time # 修改时间 tmp['shop_name'] = data_list['company_name'] # 公司名称 tmp['title'] = data_list['title'] # 商品名称 tmp['link_name'] = data_list['link_name'] # 卖家姓名 # 设置最高价price, 最低价taobao_price tmp['price'] = Decimal(data_list['price']).__round__(2) tmp['taobao_price'] = Decimal(data_list['taobao_price']).__round__(2) tmp['price_info'] = data_list['price_info'] # 价格信息 # print(tmp['price'], print(tmp['taobao_price'])) # print(tmp['price_info']) spec_name = [] for item in data_list['sku_props']: tmp_dic = {} tmp_dic['spec_name'] = item.get('prop') spec_name.append(tmp_dic) tmp['detail_name_list'] = spec_name # 标签属性名称 """ 得到sku_map """ tmp['price_info_list'] = data_list.get('sku_map') # 每个规格对应价格及其库存 tmp['all_img_url'] = data_list.get('all_img_url') # 所有示例图片地址 tmp['p_info'] = data_list.get('property_info') # 详细信息 tmp['div_desc'] = data_list.get('detail_info') # 下方div tmp['site_id'] = 2 # 阿里1688 tmp['is_delete'] = data_list['is_delete'] # tmp['my_shelf_and_down_time'] = data_list.get('my_shelf_and_down_time') # tmp['delete_time'] = data_list.get('delete_time') # print('------>>> | 待存储的数据信息为: |', tmp) params = self._get_db_insert_params(item=tmp) if tmp.get('main_goods_id') is not None: sql_str = r'insert into dbo.GoodsInfoAutoGet(GoodsID, GoodsUrl, UserName, CreateTime, ModfiyTime, ShopName, GoodsName, LinkName, Price, TaoBaoPrice, PriceInfo, SKUName, SKUInfo, ImageUrl, DetailInfo, PropertyInfo, SiteID, IsDelete, MainGoodsID) values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' else: sql_str = r'insert into dbo.GoodsInfoAutoGet(GoodsID, GoodsUrl, UserName, CreateTime, ModfiyTime, ShopName, GoodsName, LinkName, Price, TaoBaoPrice, PriceInfo, SKUName, SKUInfo, ImageUrl, DetailInfo, PropertyInfo, SiteID, IsDelete) values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)' result = pipeline._insert_into_table(sql_str=sql_str, params=params) return result def _get_db_insert_params(self, item): params = [ item['goods_id'], item['goods_url'], item['username'], item['create_time'], item['modify_time'], item['shop_name'], item['title'], item['link_name'], item['price'], item['taobao_price'], dumps(item['price_info'], ensure_ascii=False ), # 把list转换为json才能正常插入数据(并设置ensure_ascii=False) dumps(item['detail_name_list'], ensure_ascii=False), dumps(item['price_info_list'], ensure_ascii=False), dumps(item['all_img_url'], ensure_ascii=False), item['div_desc'], # 存入到DetailInfo dumps(item['p_info'], ensure_ascii=False), # 存入到PropertyInfo item['site_id'], item['is_delete'], ] if item.get('main_goods_id') is not None: params.append(item.get('main_goods_id')) return tuple(params) def get_detail_info_url_div(self, detail_info_url): ''' 此处过滤得到data_tfs_url的div块 :return: ''' detail_info = '' # print(detail_info_url) if re.compile(r'https').findall(detail_info_url) == []: detail_info_url = 'https:' + detail_info_url # print(detail_info_url) else: pass # data_tfs_url_response = requests.get(detail_info_url, headers=self.headers) # data_tfs_url_body = data_tfs_url_response.content.decode('gbk') data_tfs_url_body = self.my_phantomjs.use_phantomjs_to_get_url_body( url=detail_info_url) # ''' # 改用requests # ''' # body = MyRequests.get_url_body(url=detail_info_url, headers=self.headers) # print(body) # if body == '': # detail_info = '' # # data_tfs_url_body = body is_offer_details = re.compile(r'offer_details').findall( data_tfs_url_body) detail_info = '' if is_offer_details != []: data_tfs_url_body = re.compile(r'.*?{"content":"(.*?)"};').findall( data_tfs_url_body) # print(body) if data_tfs_url_body != []: detail_info = data_tfs_url_body[0] detail_info = re.compile(r'\\').sub('', detail_info) detail_info = self._wash_div_desc(detail_info=detail_info) else: is_desc = re.compile(r'var desc=').findall(data_tfs_url_body) if is_desc != []: desc = re.compile(r'var desc=\'(.*)\';').findall( data_tfs_url_body) if desc != []: detail_info = desc[0] detail_info = self._wash_div_desc(detail_info=detail_info) detail_info = re.compile(r'src=\"https:').sub( 'src=\"', detail_info) # 先替换部分带有https的 detail_info = re.compile(r'src="').sub( 'src=\"https:', detail_info) # 再把所欲的换成https的 # print(detail_info) return detail_info def _wash_div_desc(self, detail_info): ''' 清洗detail_info :param detail_info: :return: ''' detail_info = re.compile(r'<').sub( '<', detail_info ) # self.driver.page_source转码成字符串时'<','>'都被替代成><此外还有其他也类似被替换 detail_info = re.compile(r'>').sub('>', detail_info) detail_info = re.compile(r'&').sub('&', detail_info) detail_info = re.compile(r' ').sub(' ', detail_info) return detail_info def get_goods_id_from_url(self, ali_1688_url): # https://detail.1688.com/offer/559526148757.html?spm=b26110380.sw1688.mof001.28.sBWF6s is_ali_1688_url = re.compile( r'https://detail.1688.com/offer/.*?').findall(ali_1688_url) if is_ali_1688_url != []: ali_1688_url = re.compile( r'https://detail.1688.com/offer/(.*?).html.*?').findall( ali_1688_url)[0] print('------>>>| 得到的阿里1688商品id为:', ali_1688_url) return ali_1688_url else: print( '阿里1688商品url错误, 非正规的url, 请参照格式(https://detail.1688.com/offer/)开头的...' ) return '' def __del__(self): try: del self.my_phantomjs except Exception: print("self.my_phantomjs释放失败!") pass gc.collect()