"""
抓取
解析
存储
"""
import re
#import ast
from urllib import parse
from datetime import datetime
import requests
import time
from scrapy import Selector
from models import *
store_list_urls = [] # 用来保存商户url列表
product_list_urls = [] # 用来保存商品url列表
Product_attributes_list_url = [] # 用来保存商品详细描述的url
#product_level0 = [] # 用来保存商品系列目录首页的url
domain = "http://www.91jf.com/"
store_domain = "http://www.91jf.com/default.php?act=corp&sort=list&page="
store_url_domain = 'http://www.91jf.com/default.php?act=store_goods&storeid=' # 用于拼接商户id和url
stor_url_aptitude = 'http://www.91jf.com/default.php?act=corpcert&id=' # 用于拼接商户资质的url
#函数用来保存写入测试文本
def write_txt(html_data):
f = open("a.txt", 'w')
f.write(html_data)
f.close()
def get_nodes_json():
left_menu_text = requests.get("http://www.91jf.com/").text
#write_txt(left_menu_text)
#etree.HTML(res0.text)
sel = Selector(text=left_menu_text)
all_divs = sel.xpath("//div[@class='class_child_li']//a[@href]").extract()
if all_divs:
nodes_lists = []
for i in range(len(all_divs)):
nodes_str = all_divs[i]
nodes_str = nodes_str.replace("&","&") # 此处&由于被转义成&导致需要重新进行处理
nodes_lists.append(nodes_str)
return nodes_lists
return []
'''
url_list_names = []
def process_nodes_list(nodes_list):
#将js的格式提取出url到list中
for item in nodes_list:
#此处为对应的url数据
url = re.search('".*d"', item)
url = url.group(0).replace(""", "")
url = parse.urljoin(domain,url)
#此处为url对应的商品标签
name = re.search('<span>.*</span>',item)
name = name.group(0).replace("<span>","")
name = name.replace("</span>","")
url_list_name = [url,name] # 系列商品链接 ,商品系列名字
url_list_names.append(url_list_name)
return url_list_names
'''
def get_level1_list(nodes_list):
level1_url = []
#将js的格式提取出url到list中
for item in nodes_list:
#此处为对应的url数据
url = re.search('".*d"', item)
url = url.group(0).replace(""", "")
url1 = parse.urljoin(domain,url + "&okey=salenum&order=desc&page=1")
level1_url.append(url1)
return level1_url
def get_level0_urls():
#获取商品详情页的首目录分页
nodes_list = get_nodes_json()
level1_url = get_level1_list(nodes_list) # 所有系列商品对应的第一页url
return level1_url
def parse_product(url):
#获取商品的详情以及销售数量
res_text = requests.get(url).text
print(url)
#print(res_text)
sel = Selector(text=res_text)
res_li = sel.xpath("//div[@class='pro_list_div g-clearfix c']/ul//li[@class='goods_offset']")
flag_num = 0
for item in res_li:
product_id = item.xpath('./div[contains(@class,"pro_pic_box")]/a[@href]').extract() # 产品ID
product_id = re.search('id=.*d"',''.join(product_id))
product_id = product_id.group().replace("id=","")
product_id = product_id.replace(""","")
product_id = int(product_id)
name = item.xpath("./div[@class='row row-2 title']/a/text()").extract() # 产品名字
name = ''.join(name)
price = item.xpath('./div[@id="goods_detail_b"]/div[@class="row row-1"]/div[@class="g_price fm2"]/strong/text()').extract() # 显示价格
price = ''.join(price)
try:
price = float(price)
except:
print("价格会员可见|价格请咨询商家")
continue
sales_num = item.xpath("./div[@id='goods_detail_b']/div[2]/p[1]/text()").extract() # 销售数量
sales_num= ''.join(sales_num)
sales_num = sales_num.split('销量:')[1]
sales_num = int(sales_num)
flag_num = sales_num
if sales_num < 1:
continue
merchant = item.xpath("./div[@id='goods_detail_b']/div[2]/p[2]/text()").extract() # 商家
merchant = ''.join(merchant)
main_Products = item.xpath("./div[@id='goods_detail_b']/div[2]/p[3]/text()").extract() # 主营
main_Products = ''.join(main_Products)
merchant_Place = item.xpath("./div[@id='goods_detail_b']/div[2]/p[4]/text()").extract() # 地址
merchant_Place = ''.join(merchant_Place)
product = Product()
product.name = name
product.price = price
product.sales_num = sales_num
product.merchant = merchant
product.main_Products = main_Products
product.merchant_Place = merchant_Place
existed_name = Product.select().where(Product.name==product.name)
if existed_name:
product.save()
else:
product.save(force_insert=True)
next_page = sel.xpath("//*[@class='pagination2']/a[@href]").extract()
if len(next_page) > 2 and flag_num > 0:
url_next = re.search('".*d"',next_page[-1])
url_next = url_next.group().replace("&","&") # 此处&由于被转义成&导致需要重新进行处理
url_next = url_next.replace(""","")
url_next = parse.urljoin(domain,url_next)
#print(url_next)
parse_product(url_next)
else:
pass
#获取商品链接,上一级url为商品详情页,返回值为同一品类的所有商品id以及url
def parse_data_last(url):
url_list = []
store_id_list = []
flag_num = 0
#获取商品的详情标签
res_text = requests.get(url).text
sel = Selector(text=res_text)
res_li = sel.xpath("//div[@class='pro_list_div g-clearfix c']/ul//li[@class='goods_offset']")
for item in res_li:
sales_num = item.xpath("./div[@id='goods_detail_b']/div[2]/p[1]/text()").extract() # 销售数量
sales_num= ''.join(sales_num)
sales_num = sales_num.split('销量:')[1]
sales_num = int(sales_num)
flag_num = int(sales_num)
data = item.xpath("./div[@class='pro_pic_box']/a").extract()
data = re.search('".*d"',data[0])
data = data.group().replace("&","&")
data = data.replace(""","")
data_url = parse.urljoin(domain,data) # 链接为销量排序之后的单个商品链接,传出链接
print("开始获取商品:{}".format(data_url))
store_id = parse_store_id(data_url)
store_id_list.append(store_id)
#parse_product_data(data_url)
url_list.append(data_url)
#此处代码用来切到下一页链接数据,商品的详情排布页
next_page = sel.xpath("//*[@class='pagination2']/a[@href]").extract()
if len(next_page) > 2 and flag_num > 0:
url_next = re.search('".*d"',next_page[-1])
url_next = url_next.group().replace("&","&") # 此处&由于被转义成&导致需要重新进行处理
url_next = url_next.replace(""","")
url_next = parse.urljoin(domain,url_next)
parse_data_last(url_next)
return url_list ,store_id_list
#获取商品详细数据
def parse_product_data(url):
#获取商品的详情以及销售数量
#print(url) # 打印当前商品页的url用来定位
product_id = url.split('id=')[1] # 对商品id进行切片处理,用来获取ajax数据
res_text = requests.get(url).text
sel = Selector(text=res_text)
#筛选规则,当is_price之后的value属性值为0的时候,说明不需要咨询商家,同时需要注意的是,商品会有打折批次数量的差异导致价格差异,
#这一点需要根据具体的显示页面来处理,现在忽略,由于可能存在打折段的数据差异,所以暂时不考虑
Is_price = sel.xpath("//input[contains(@id,'is_price')]").extract()#取到的数据用来判断价格是否需要咨询商家
print(Is_price)
if len(Is_price) < 1:
print("页面数据为空")
else :
is_value = re.search('d',Is_price[0])
if is_value.group() == '0': # 0表示商品价格不需要咨询商户
#datas = sel.xpath("//table[contains(@class,'goods_spec_list')]").extract()
datas = sel.xpath("//div[contains(@class,'show_all')]/table[contains(@class,'goods_spec_list')]//tr")
#price_base
price_base = 0.0
for item in range(len(datas)):
price = datas[item].xpath("./input[3]").extract()
price = re.search('value=".*"',price[0])
price = re.search('d.*d',price[0])
price = price.group()
price_base = price_base + float(price)
price_base = price_base / len(datas) # 商品基准价格计算
#此处获取商品的描述信息
attributes_list = sel.xpath("//span[contains(@class,'attributes-list')]//li/text()").extract()
str_attributes = ' '.join(attributes_list)
str_attributes = str_attributes.replace(" "," ") # 商品信息描述
#此处发送请求获取商品购买数据
url_sales = parse.urljoin(domain,'default.php?act=evallist')
data = {
'id': product_id,
'page': '0',
'info_type': 'sale'
}
response = requests.post(url_sales, data=data)
buyer_num = response.json().get("member") # 购买人数
sale_num = response.json().get('num') # 销售数量
buyer_rate = response.json().get('re_buyer_rate') # 商品复购率
product_id = int(product_id) # 此处对商品ID进行转换
product_attributes = Product_attributes()
product_attributes.product_id = product_id
product_attributes.price_base = price_base
product_attributes.attributes = str_attributes
product_attributes.buyer_num = buyer_num
product_attributes.sale_num = sale_num
product_attributes.buyer_rate = buyer_rate
existed_id = Product_attributes.select().where(Product_attributes.product_id==product_id)
if existed_id:
product_attributes.save()
else:
product_attributes.save(force_insert=True)
else :
price = "价格请咨询商家"
#获取商户详细数据,处理逻辑为根据单个商品目录来获取对应的商户id
def parse_store_id(url):
#print(url) # 打印当前商品页的url用来定位
res_text = requests.get(url).text
sel = Selector(text=res_text)
store_id = 0
#筛选规则,当is_price之后的value属性值为0的时候,说明不需要咨询商家,同时需要注意的是,商品会有打折批次数量的差异导致价格差异,
#这一点需要根据具体的显示页面来处理,现在忽略,由于可能存在打折段的数据差异,所以暂时不考虑
Is_price = sel.xpath("//input[contains(@id,'is_price')]").extract()#取到的数据用来判断价格是否需要咨询商家
if len(Is_price) < 1:
print("页面数据为空")
else:
is_value = re.search('d',Is_price[0])
if is_value.group() == '0': # 0表示商品价格不需要咨询商户
store_id = sel.xpath('//span[@class="container_title_span"]/a[@href]').extract()
store_id = ''.join(store_id)
store_id = re.search('storeid=d*"',store_id)
store_id = store_id.group()
store_id = store_id.split('storeid=')[1]
store_id = store_id.replace(""","")
store_id = int(store_id) # 商户的id
else :
pass
return store_id
#根据store_id获取的url用来抓取商户的数据,
def parse_store_data(url):
print(url) # 打印当前商品页的url用来定位
res_text = requests.get(url).text
sel = Selector(text=res_text)
if len(res_text) > 10:
store_name = sel.xpath('//span[contains(@class,"container_title_span")]/a[@href]/text()').extract()
store_name = ''.join(store_name) # 商户的名字
store_id = sel.xpath('//span[@class="container_title_span"]/a[@href]').extract()
store_id = ''.join(store_id)
store_id = re.search('storeid=d*"',store_id)
store_id = store_id.group()
store_id = store_id.split('storeid=')[1]
store_id = store_id.replace(""","")
store_id = int(store_id) # 商户的id
store_level = ''
store_place = ''
store_describe = ''
store_supply = ''
store_service = ''
store_data = sel.xpath('//ul[contains(@class,"gy_info_list")]/li/text()').extract()
if len(store_data) > 3:
store_level = store_data[2] # 商户等级
store_level = store_level.replace(" ","")
store_level = store_level.replace("
","")
store_place = store_data[3] # 商户地址
store_place = store_place.replace(" ","")
store_aptitude = stor_url_aptitude + str(store_id) # 商户的资质
temp_datas = sel.xpath('//li[contains(@class,"evaluate")]//div[@style]//text()').extract()
if len(temp_datas) == 6:
store_describe = temp_datas[0] + ':' + temp_datas[1] # 商户描述
store_supply = temp_datas[2] + ':' + temp_datas[3] # 商户供货
store_service = temp_datas[4] + ':' + temp_datas[5] # 商户服务
store = Store()
store.store_id = store_id
store.store_name = store_name
store.store_level = store_level
store.store_place = store_place
store.store_aptitude = store_aptitude
store.store_describe = store_describe
store.store_supply = store_supply
store.store_service = store_service
existed_id = Store.select().where(Store.store_id==store_id)
if existed_id:
store.save()
else:
store.save(force_insert=True)
store_all_id_list = []
#获取所有商户id
def parse_store_all_id(url):
print(url) # 打印当前商户详情页的url用来定位
res_text = requests.get(url).text
sel = Selector(text=res_text)
res_li = sel.xpath("//div[contains(@class ,'corp_list')]//div[@class='supply-list']")
for item in res_li:
store_id = item.xpath(".//a[contains(@class,'supply-left-tltle')]").extract()
store_id = ''.join(store_id)
store_id = re.search('storeid=d*"',store_id)
store_id = store_id.group()
store_id = store_id.split('storeid=')[1]
store_id = store_id.replace(""","")
store_id = int(store_id) # 获取店铺id
store_all_id_list.append(store_id)
#此处代码用来切到下一页链接数据,商户的详情排布页
next_page = sel.xpath("//*[@class='pagination2']/a[@href][last()]/text()").extract()
next_page = ''.join(next_page)
try:
next_page = int(next_page)
except:
url_next = sel.xpath("//*[@class='pagination2']/a[@href][last()]").extract()
url_next = ''.join(url_next)
url_next = re.search('".*d"',url_next)
url_next = url_next.group().replace("&","&") # 此处&由于被转义成&导致需要重新进行处理
url_next = url_next.replace(""","")
url_next = parse.urljoin(domain,url_next)
parse_store_all_id(url_next)
return store_all_id_list
def get_last_store_id():
#获取最终需要抓取的店铺id,传回拼接之后的url
store_id_list = parse_store_all_id(store_domain)
level0_urls = get_level0_urls()
store_id_last_list = []
for url in level0_urls:
url_list ,store_id_list0 = parse_data_last(url)
temp_data = [item for item in store_id_list0 and item in store_id_list]
store_id_last_list.extend(temp_data)
print(store_id_last_list)
if __name__ == "__main__":
#level0_urls = get_level0_urls()
store_all_id_list = parse_store_all_id(store_domain)
print(len(store_all_id_list))
for item in store_all_id_list:
item = 'http://www.91jf.com/default.php?act=store_goods&storeid=' + str(item)
parse_store_data(item)
'''
for url in last_urls:
#parse_product_data(url)
#print("开始获取商品:{}".format(url))
'''