大鍋在做外賣,給我說能否統(tǒng)計(jì)出這半年點(diǎn)餐次數(shù)多的10個(gè)顧客,我不知道APP本身是否有這個(gè)功能,想了下近用selenium較多,用selenium嘗試下吧。
1、定義一個(gè)類,這里先描述需要的屬性和方法,后面再依次具體分析:
1 class Order:
2 def __init__(self, url, username, password):
3 # URL以及用戶名和密碼
4 self.url = url
5 self.username = username
6 self.password = password
7 # webdriver對(duì)象
8 self.driver = None
9 # 日期列表
10 self.dateList = list()
11 # 存儲(chǔ)訂單的dict
12 self.orderDict = dict()
13
14 # 設(shè)置訂單的時(shí)間范圍
15 def setDate(self, startdate, enddate):
16 pass
17 # 登錄
18 def login(self):
19 pass
20 # 退出
21 def logout(self):
22 pass
23 # 切換到歷史訂單頁面
24 def switchToHistoryOrder(self):
25 pass
26 # 選擇一個(gè)時(shí)間范圍
27 def selectDate(self, startDate, endDate):
28 pass
29 # 訂單信息存入self.orderDict
30 def saveOrderIntoDict(self, tel, name, address):
31 pass
32 # 處理當(dāng)前頁面的訂單
33 def searchOrderListInCurrentPage(self):
34 pass
35 # 判斷是否還有下一頁
36 def hasNextPage(self):
37 pass
38 # 切換到下一頁
39 def enterNextPage(self):
40 pass
41 # 抓取設(shè)定日期范圍內(nèi)的所有訂單
42 def getAllOrders(self, startdate, enddate):
43 pass
44 # 篩選出點(diǎn)餐次數(shù)排名前N的顧客
45 def getTopN(self, n=10):
46 pass
2、設(shè)置欲篩選的訂單日期范圍
這里設(shè)置的日期格式必須是'yyyy-mm-dd',由于該網(wǎng)站在查詢訂單的時(shí)候,時(shí)間范圍必須是7天以內(nèi),比如直接查詢2016-01-01到2016-02-28之間的訂單是不行的,因此需要先將這段時(shí)間以7天為周期分割為多個(gè)時(shí)間段,然后再分段處理;
分割后的時(shí)間段存放在self.dateList中,list的元素為tuple,一個(gè)tuple表示一個(gè)時(shí)間段:
1 # 設(shè)置訂單的時(shí)間范圍,日期格式必須是'yyyy-mm-dd'
2 # 然后以7天為周期,將日期范圍分割成list,list的每個(gè)元素為一個(gè)tuple,分別存放起止日期
3 # 例如 [('2016-01-01', '2016-01-07'), ('2016-01-08', '2016-01-14')]
4 def setDate(self, startdate, enddate):
5 # 通過正則表達(dá)式檢查日期格式
6 pdate = re.compile('d{4}-d{2}-d{2}')
7 if pdate.search(startdate) and pdate.search(enddate):
8 # 轉(zhuǎn)換為datetime格式,便于日期計(jì)算
9 startdate = datetime.datetime.strptime(startdate, '%Y-%m-%d')
10 enddate = datetime.datetime.strptime(enddate, '%Y-%m-%d')
11
12 # 將日期范圍以7天為周期分割
13 days = (enddate - startdate).days + 1
14 cnt = days / 7
15 left = days - 7*cnt
16 for x in range(cnt):
17 d1 = (startdate + datetime.timedelta(days=7*x))
18 d2 = (d1 + datetime.timedelta(days=6))
19 # datetime轉(zhuǎn)換為str,再加入list中
20 self.dateList.append((d1.strftime('%Y-%m-%d'), d2.strftime('%Y-%m-%d')))
21 if left > 0:
22 self.dateList.append(((startdate+datetime.timedelta(days=cnt*7)).strftime('%Y-%m-%d'), enddate.strftime('%Y-%m-%d')))
23 else:
24 print u'日期格式錯(cuò)誤,必須為yyyy-mm-dd格式'
25 exit(1)
測(cè)試一下:
1 order = Order('url', 'username', 'password')
2 order.setDate('2016-01-01', '2016-01-31')
3 print order.dateList
4 #輸出為
5 [('2016-01-01', '2016-01-07'), ('2016-01-08', '2016-01-14'), ('2016-01-15', '2016-01-21'), ('2016-01-22', '2016-01-28'), ('2016-01-29', '2016-01-31')]
3、登錄與退出
登錄比較簡(jiǎn)單,直接過id定位到用戶名和密碼輸入框,然后定位登錄按鈕點(diǎn)擊登錄即可,只是定位到輸入框后需要先將框內(nèi)的提示信息清除掉。
退出更簡(jiǎn)單了,直接關(guān)閉瀏覽器即可。
1 # 登錄
2 def login(self):
3 # 采用chrome瀏覽器
4 self.driver = webdriver.Chrome()
5 # 窗口大化
6 self.driver.maximize_window()
7 # 設(shè)置超時(shí)時(shí)間
8 self.driver.implicitly_wait(10)
9 self.driver.get(self.url)
10
11 # 查找用戶名輸入框,先清除提示信息,再輸入用戶名
12 usr = self.driver.find_element_by_id('account-name')
13 usr.clear()
14 usr.send_keys(self.username)
15
16 # 查找密碼輸入框,先清除提示信息,再輸入密碼
17 passwd = self.driver.find_element_by_id('account-password')
18 passwd.clear()
19 passwd.send_keys(self.password)
20
21 # 點(diǎn)擊登錄
22 self.driver.find_element_by_id('account-login-btn').click()
23 return
24
25 # 退出
26 def logout(self):
27 self.driver.close()
28 return
4、切換到歷史訂單頁面
登錄后點(diǎn)擊訂單管理,然后點(diǎn)擊歷史訂單,切換到歷史訂單頁面,如下圖所示:
由于“訂單管理”和“歷史訂單”這兩個(gè)元素都是超鏈接,因此可以直接用超鏈接定位:
1 # 切換到歷史訂單頁面
2 def switchToHistoryOrder(self):
3 self.driver.find_element_by_partial_link_text(u'訂單管理').click()
4 self.driver.find_element_by_partial_link_text(u'歷史訂單').click()
5
6 # 切換frame,因?yàn)楹罄m(xù)的所有處理都是在hashframe中,所有在這里切換
7 self.driver.switch_to.frame('hashframe')
8 return
注意該方法后有個(gè)切換frame的操作,下一步知道為什么要添加這句了。
5、選擇訂單日期范圍,篩選出該日期范圍內(nèi)的訂單
注意這里的日期范圍與第二步設(shè)置的日期范圍不一樣,第二步設(shè)置的日期范圍是我們想要篩選的起止時(shí)間,這里的日期范圍是第二步分割出來的其中一段。
與用戶名密碼框一樣,這里的日期輸入框也可采用id定位,同樣定位后需要先將輸入框預(yù)置的日期清除:
1 # 在頁面上設(shè)置訂單的時(shí)間范圍,并篩選出該時(shí)間范圍內(nèi)的所有訂單
2 # 調(diào)用該方法時(shí),參數(shù)需從self.dateList中獲取,dateList中的每一個(gè)tuple對(duì)應(yīng)一組參數(shù)
3 def selectDate(self, startDate, endDate):
4 # 設(shè)置起始日期
5 s = self.driver.find_element_by_id('J-start')
6 s.clear()
7 s.send_keys(startDate)
8
9 # 設(shè)置終止日期
10 e = self.driver.find_element_by_id('J-end')
11 e.clear()
12 e.send_keys(endDate)
13 return
這里說一下第4步中的切換frame,我們先將切換frame這一句注釋掉,然后來測(cè)試下選擇日期:
1 order.login()
2 order.switchToHistoryOrder()
3 order.selectDate('2016-07-01', '2016-07-02')
4 # 輸出
5 selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"id","selector":"J-start"}
可以看到出錯(cuò)了,提示沒有需要定位的元素,為什么呢?通過HTML代碼可以看到,這里采用了框架frame,所定位的元素在frame里面,如下所示:
因此需要先切換到該frame里面,然后才能定位到frame里面的元素;由于后續(xù)所有的訂單操作都在該frame里面,因此在上一步切換到歷史訂單頁面后,先切換到該frame,便于后續(xù)操作。
6、將訂單信息存入self.orderDict
該方法是下一步需要調(diào)用的,因此這里先實(shí)現(xiàn)。每一個(gè)訂單我們只需3個(gè)信息:姓名、電話、地址,然后將這三個(gè)元素表示的訂單存入orderDict,但是由于我們要統(tǒng)計(jì)出點(diǎn)餐次數(shù)多的10個(gè)顧客,
因此還要保存每個(gè)顧客的點(diǎn)餐次數(shù)。這里dict的元素格式為{'tel': ['name', 'address', cnt]},由于姓名可能重復(fù),因此采用了電話作為key值。如果某個(gè)顧客第一次點(diǎn)餐,保存時(shí)將點(diǎn)餐次數(shù)cnt初始化為1;
如果不是第一次點(diǎn)餐,則將該顧客對(duì)應(yīng)的cnt值加1。
1 # 將tel, name, address表示的訂單信息存入self.orderDict
2 # self.orderDict元素的形式為 {'tel': ['name', 'address', cnt]}
3 # 以電話號(hào)碼為key,以姓名、地址、點(diǎn)餐次數(shù)組成的list為value
4 # 當(dāng)要添加的key不存在時(shí),將此訂單加入orderDict,并且cnt初始化為1
5 # 當(dāng)要添加的key已經(jīng)存在時(shí),直接將該key對(duì)應(yīng)的cnt加1
6 def saveOrderIntoDict(self, tel, name, address):
7 if self.orderDict.has_key(tel):
8 self.orderDict[tel][2] += 1
9 else:
10 self.orderDict[tel] = [name, address, 1]
11 return