根据用户输入的日期区间,获取已完成和配送中的相关订单信息,并生成表格。
一个订单可能包含多个产品,所以会有重复的订单号出现,其中运费、折扣金额、优惠券支付是根据当前订单中产品数量算出的平均值。
1、创建Scrapy项目
- scrapy startproject Order
2.进入项目目录,使用命令genspider创建Spider
- scrapy genspider order XXXX.com
3、定义要抓取的数据(处理items.py文件)
- # -*- coding: utf-8 -*-
- import scrapy
-
- class OrderItem(scrapy.Item):
- # 序号
- number = scrapy.Field()
- # 订单号
- order_number = scrapy.Field()
- # 卖家(企业)
- order_company = scrapy.Field()
- # 客户信息
- order_client = scrapy.Field()
- # 获取订单金额
- order_money = scrapy.Field()
- # 下单时间
- order_time = scrapy.Field()
- # 订单来源
- order_source = scrapy.Field()
- # 订单状态
- order_state = scrapy.Field()
- # 签收时间
- sign_time = scrapy.Field()
- # 商品名称
- product_name = scrapy.Field()
- # 商品分类
- product_class = scrapy.Field()
- # 商品金额小计
- product_money = scrapy.Field()
- # 平均运费
- freight = scrapy.Field()
- # 折扣金额
- discount_money = scrapy.Field()
- # 优惠券支付
- voucher = scrapy.Field()
- # 查询时间
- select_time = scrapy.Field()
4、编写提取item数据的Spider(在spiders文件夹下:order.py)
- # -*- coding: utf-8 -*-
- # 1、根据用户输入的日期区间,获取已完成和配送中的相关订单信息,并生成表格。
- # 2、一个订单可能包含多个产品,所以会有重复的订单号出现,其中运费、折扣金额、优惠券支付是根据当前订单中产品数量算出的平均值。
-
- import scrapy
- import re,time
- from Order.items import OrderItem
-
- class OrderSpider(scrapy.Spider):
- name = 'order'
- allowed_domains = ['XXXX.com']
- login_page = "https://pos.XXXX.com/login.html"
- start_urls = ['https://pos.XXXX.com/order/tolist.html']
- username = input("请输入账号:")
- password = input("请输入密码:")
-
- def start_requests(self):
- yield scrapy.Request(url=self.login_page,callback=self.login)
-
- def login(self,response):
- yield scrapy.FormRequest.from_response(
- response,
- formdata={"j_username":self.username, "j_password":self.password},
- callback = self.parse_page
- )
- def parse_page(self,response):
- if "loginerror" in response.body.decode("utf-8"):
- print("登录失败,错误的手机号或密码")
- if "</span>首页" in response.body.decode("utf-8"):
- print("欢迎您'%s',成功登录POS后台管理系统!"%(self.username))
- yield scrapy.Request(self.start_urls[0],callback=self.order_list)
- def order_list(self,response):
- # 存储用户输入的日期,前两个是已完成状态的日期,后两个是配送中的日期
- self.date_list = []
- # 日期格式正则规则
- patt = re.compile(r"\d{4}-\d{1,2}-\d{1,2}")
- for self.state in ['100','40']:
- while True:
- print("\n" + "=" * 87)
- print("开始日期如果不输入则为2015年最早日期,假设为2015-1-1,结束日期如果不输入则为当天日期。")
- print("注意:已完成订单中,如果开始和结束日期都不输入,则把签收日期为空的也会筛选出来。")
- print("=" * 87 + "\n")
- if self.state == "100":
- self.start_date = input("请输入已完成签收的开始日期(格式:2018-11-30):")
- self.end_date = input("请输入已完成签收的结束日期(格式:2018-11-30):")
- else:
- self.start_date = input("请输入配送中的下单开始日期(格式:2018-11-30):")
- self.end_date = input("请输入配送中的下单结束日期(格式:2018-11-30):")
- re_start_date = patt.findall(self.start_date)
- re_end_date = patt.findall(self.end_date)
- if self.start_date != '':
- if len(re_start_date) != 0:
- # 结束日期可以不输入
- if self.end_date == '':
- break
- else:
- if len(re_end_date) == 0:
- print("ERROR:结束日期格式输入有误,请重新输入!")
- continue
- else:
- break
- else:
- if self.end_date != '':
- if len(re_end_date) == 0:
- print("ERROR:开始和结束日期格式输入有误,请重新输入!")
- continue
- else:
- print("ERROR:开始日期格式输入有误,请重新输入!")
- continue
- else:
- if self.end_date != '':
- if len(re_end_date) == 0:
- print("ERROR:结束日期格式输入有误,请重新输入!")
- continue
- else:
- break
- else:
- break
- self.date_list.append(self.start_date)
- self.date_list.append(self.end_date)
- yield scrapy.FormRequest.from_response(
- response,
- formdata={
- # "d-5481-p": "1",
- "to.buyer": "",
- "to.endDate": self.end_date,
- "to.id": "",
- # 订单状态,-1代表全部
- "to.orderSource": "-1",
- "to.seller": "",
- "to.startDate": self.start_date,
- # 已完成是100,配送中是40
- "to.status": self.state,
- },
- callback=self.parse)
- # print("退出系统。。。") # 这个无法实现,未等产品获取完毕系统就已退出导致获取不到数据
- # yield scrapy.Request('https://pos.XXXX.com/j_spring_security_logout')
- def parse(self, response):
- # 获取当天的日期
- today_time = time.strftime("%Y-%m-%d", time.localtime())
- if self.date_list[1] == '':
- self.date_list[1] = today_time
- if self.date_list[3] == '':
- self.date_list[3] = today_time
- if self.date_list[0] == '':
- self.date_list[0] = '2015-1-1'
- if self.date_list[2] == '':
- self.date_list[2] = '2015-1-1'
- items = []
- next_url_list = list(set(response.xpath('//span[@class="pagelinks"]/a/@href').extract()))
- # 获取查询结果的数据,计算有多少页,是一个列表
- # page_list = response.xpath('//*[@id="signForm"]/span[1]/text()').extract()
- # if len(page_list) == 0:
- # print("抱歉,没有查询到相应条件的商品!")
- # else:
- # # 如果只有一条数据会显示One
- # if page_list[0].split(" ")[0] != "One":
- # total = int(page_list[0].split(" ")[0].replace(",", ""))
- # else:
- # total = 1
- # if total % 20 == 0:
- # end_page = total // 20
- # print('共获取到%d项订单数据,合计%d页' % (total, end_page))
- # else:
- # end_page = total // 20 + 1
- # print('共获取到%d项订单数据,合计%d页' % (total, end_page))
- order_state = ""
- for each in response.xpath('//div[@class="dataTables_wrapper"]'):
- # 序号
- number = each.xpath('//*[@id="to"]/tbody/tr/td[1]/text()').extract()
- # 订单号
- order_number = each.xpath('//*[@id="to"]/tbody/tr/td[2]/a/text()').extract()
- # 卖家(企业)
- order_company = each.xpath('//*[@id="to"]/tbody/tr/td[3]/a/text()').extract()
- # 客户信息
- order_client = each.xpath('//*[@id="to"]/tbody/tr/td[4]/text()').extract()
- # 获取订单金额
- order_money = each.xpath('//*[@id="to"]/tbody/tr/td[5]/text()').extract()
- # 下单时间,首尾无空格
- order_time = each.xpath('//*[@id="to"]/tbody/tr/td[6]/text()').extract()
- # 订单来源
- order_source = each.xpath('//*[@id="to"]/tbody/tr/td[10]/text()').extract()
- # 订单状态
- order_state = each.xpath('//*[@id="to"]/tbody/tr/td[11]/text()').extract()
- # 签收时间
- sign_time = each.xpath('//*[@id="to"]/tbody/tr/td[7]/text()').extract()
- for i in range(len(number)):
- item = OrderItem()
- item['number'] = number[i].strip()
- item['order_number'] = order_number[i].strip()
- item['order_company'] = order_company[i].replace("\n", "").replace("\t", "").replace("\r", "").\
- replace(" ","").replace(" ", "").strip()
- item['order_client'] = order_client[i].strip()
- item['order_money'] = order_money[i].strip()
- item['order_time'] = order_time[i].strip()
- item['order_source'] = order_source[i].strip()
- item['order_state'] = order_state[i].strip()
- if order_state[0].strip() == "已完成":
- item['sign_time'] = sign_time[i].strip()
- item['select_time'] = "查询签收日期:"+self.date_list[0]+"至"+self.date_list[1]
- # 配送中的订单没有签收时间
- else:
- item['sign_time'] = ""
- item['select_time'] = "查询下单日期:" + self.date_list[2] + "至" + self.date_list[3]
- items.append(item)
- # 根据订单号获取到产品链接
- for item in items:
- id_url = 'https://pos.XXXX.com/order/showto.html?to.id='+item['order_number']
- yield scrapy.Request(url=id_url,meta={'meta_1':item},callback=self.parse_id)
- # 处理下一页
- for page in next_url_list:
- if page[10:11] != '1':
- print("总共%s页,订单状态:%s"%((page[10:11]),order_state[0].strip()))
- order_list_url = "https://pos.XXXX.com/order/tolist.html" + str(page)
- # 无需处理第一页,因为order_list传过来的response已经处理
- yield scrapy.Request(url = order_list_url,callback=self.parse)
- # 根据订单号获取产品名称和运费、价格等
- def parse_id(self,response):
- meta_1 = response.meta['meta_1']
- item = OrderItem()
- # with open(meta_1['order_number']+".html","w",encoding='utf-8')as f:
- # f.write(response.text)
- # 获取商品名称,注意xpath的规则,//tr而不是/tbody/tr,有些是浏览器自动加的
- product_name = response.xpath('//*[@id="order_items"]//tr/td[1]/text()').extract()
- # product_name = response.xpath('//*[@id="order_items"]/tbody/tr/td[1]/text()').extract()
- # 获取商品分类
- # product_class = response.xpath('//div[@id="tabs-2"]/table[@id="order_items"]/tbody/tr/td[2]/text()').extract()
- product_class = response.xpath('//*[@id="order_items"]//tr/td[2]/text()').extract()
- # 商品金额小计
- product_money = response.xpath('//*[@id="order_items"]//tr/td[6]/text()').extract()
- # 运费,平分
- freight = response.xpath('//*[@id="order_items"]//tr[2]/th[2]/span/text()').extract()
- # 折扣金额,平分
- discount_money = response.xpath('//*[@id="order_items"]//tr[3]/th[2]/text()').extract()
- # 优惠券支付,平分
- voucher = response.xpath('//*[@id="order_items"]//tr[5]/th[2]/text()').extract()
- # 获取该订单下有多少个产品分类
- length = len(product_name)
- print("状态:%s,订单号:%s,有%s个产品,处理中...."%(meta_1['order_state'],meta_1['order_number'],length))
- for i in range(length):
- item['product_name'] = product_name[i].strip()
- item['product_class'] = product_class[i].strip()
- item['product_money'] = product_money[i].strip()
- item['freight'] = float(freight[0].strip())/length
- item['discount_money'] = float(discount_money[0].strip())/length
- item['voucher'] = float(voucher[0].strip())/length
- item['number'] = meta_1['number']
- item['order_number'] = meta_1['order_number']
- item['order_company'] = meta_1['order_company']
- item['order_client'] = meta_1['order_client']
- item['order_money'] = meta_1['order_money']
- item['order_time'] = meta_1['order_time']
- item['order_source'] = meta_1['order_source']
- item['order_state'] = meta_1['order_state']
- item['sign_time'] = meta_1['sign_time']
- item['select_time'] = meta_1['select_time']
- yield item
5.处理pipelines管道文件保存数据,可将结果保存到文件中(pipelines.py)
- # -*- coding: utf-8 -*-
- import json
- from openpyxl import Workbook
- import time
-
- # 转码操作,继承json.JSONEncoder的子类
- class MyEncoder(json.JSONEncoder):
- def default(self, o):
- if isinstance(o, bytes):
- return str(o, encoding='utf-8')
- return json.JSONEncoder.default(self, o)
-
- class OrderPipeline(object):
- def __init__(self):
- self.wb = Workbook()
- self.ws = self.wb.active
- self.ws.title = '订单数量列表'
- # 冻结首行
- self.ws.freeze_panes = 'A2'
- self.ws.column_dimensions['C'].width = 19
- self.ws.column_dimensions['D'].width = 21
- self.ws.column_dimensions['J'].width = 23
- self.ws.column_dimensions['N'].width = 9.4
- self.ws.column_dimensions['O'].width = 18
- self.ws.column_dimensions['P'].width = 35
- # 创建表头
- self.ws.append(['序号', '订单号', '卖家', '客户',
- '订单金额', '下单时间',
- '订单来源', '订单状态', '商品名称',
- '商品分类','商品金额小计','平均运费',
- '折扣金额','优惠券支付','签收时间','查询日期'])
-
- def process_item(self, item, spider):
- text = [item['number'], item['order_number'], item['order_company'], item['order_client'],
- item['order_money'], item['order_time'],
- item['order_source'], item['order_state'], item['product_name'],
- item['product_class'],item['product_money'],item['freight'],
- item['discount_money'],item['voucher'],item['sign_time'],item['select_time']]
- self.ws.append(text)
- return item
-
- def close_spider(self, spider):
- # 给保存的文件名字加上个当天的日期年月日
- file_end_name = time.strftime("%Y-%m-%d", time.localtime())
- self.wb.save("订单数量列表" + file_end_name + '.xlsx')
- print("数据处理完毕,谢谢使用!")
-
6.配置settings文件(settings.py)
- # Obey robots.txt rules,具体含义参照:https://www.cdsy.xyz/computer/programme/Python/241210/cd64912.html
- ROBOTSTXT_OBEY = False
-
- # Override the default request headers:添加User-Agent信息
- DEFAULT_REQUEST_HEADERS = {
- 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);',
- # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
- # 'Accept-Language': 'en',
- }
-
- # Configure item pipelines去掉下面注释,打开管道文件
- ITEM_PIPELINES = {
- 'Order.pipelines.OrderPipeline': 300,
- }
-
- # 还可以将日志存到本地文件中(可选添加设置)
- LOG_FILE = "order.log"
- LOG_LEVEL = "DEBUG"
- # 包含打印信息也一起写进日志里
- LOG_STDOUT = True
7.以上设置完毕,进行爬取:执行项目命令crawl,启动Spider:
- scrapy crawl order