python(七):python記錄日志


Python日志采集(詳細)

 

通常在前期調試代碼的時候,我們會使用print在IDE控制台打印一些信息,判斷運行情況。但在運行整個自動化測試項目的過程中,通過print打印信息的方式獲取運行情況顯然行不通。
這時就需要收集日志,每次運行后通過查看日志來獲取項目運行情況。那么我們該如何獲取日志?

一,日志概述

1,日志作用

在項目開發或測試過程中,項目運行一旦出現問題,記錄日志信息就顯得尤為重要。主要通過日志來定位問題,就好比偵探人員要根據現場留下的線索來推斷案情。

2,日志級別

  • 代碼在運行的過程中會出現不同的情況,如調試信息、警告信息、報錯等,那么采集日志時就需要對這些日志區分級別管理,這樣才能更精確地定位問題。日志級別一般分類如下(以嚴重程度遞增排序):

    級別
    何時使用
    DEBUG 調試信息,也是最詳細的日志信息
    INFO 證明事情按預期工作
    WARNING 表明發生了一些意外,或不久的將來會發生問題(如 磁盤滿了),軟件還是正常工作
    ERROR 由於更嚴重的問題,軟件已經不能執行一些工作了
    CRITICAL 嚴重錯誤,表明軟件已經不能繼續運行了
  • 日志級別排序為:CRITICAL > ERROR > WARNING > INFO > DEBUG

    日志采集時設置低級別的日志,能采集到更高級別的日志,但不能采集到更低級別的日志。

    例如:設置的日志級別為info級別,就只能采集到info、warning、error、critical級別的日志,不能采集到debug級別的日志。設置的日志級別為debug級別的話則能采集到所有級別的日志。默認設置級別為WARNING

  • 在自動化測試項目中,通常在一般情況時使用info日志,預計報錯則使用error日志。

3,日志格式

將日志格式化是為了提高日志的可閱讀性,比如:時間+模塊+行數+日志級別+日志具體信息 的日志格式。如果輸出的日志信息雜亂無章,就不利於問題的定位。如下所示就是日志格式化輸出,非常便於閱讀查看。

2020-09-30 10:45:05,119 logging_test.py[line:7] DEBUG this is debug message. 2020-09-30 10:45:05,119 logging_test.py[line:9] INFO this is info message. 2020-09-30 10:45:05,119 logging_test.py[line:11] WARNING this is warning message. 2020-09-30 10:45:05,120 logging_test.py[line:13] ERROR this is error message. 2020-09-30 10:45:05,120 logging_test.py[line:15] CRITICAL this is critical message. 

4,日志位置

通常,在一個項目中會有很多的日志采集點,日志采集點的設置必須結合業務來確定。

比如在執行修改登錄密碼用例前插入“開始執行修改登錄密碼用例...”的日志信息。再比如在登錄代碼執行前可以插入“准備登錄...”日志信息。

如果在登錄完成后,再設置登錄的提示日志就會給人造成誤解,無法判斷到底是登錄之前的問題還是登錄之后的問題,因此日志采集點的位置很重要。

二,logging模塊

1,簡介

logging為python自帶的日志模塊,提供了通用的日志系統,包括不同的日志級別。logging可使用不同的方式記錄日志,如使用文件,HTTP GET/POST,SMTP,Socket等方式記錄。通常情況下,我們使用文件記錄日志信息,文件格式一般為.txt或.log文件。

2,文檔

詳細內容可查看logging模塊官方文檔,使用時需要導入:

import logging 

三,logging第一種使用方法:簡單配置使用

1,使用方法

logging.basicConfig(**kwargs)

2,basicConfig()部分參數說明

filename 指定日志名稱或完整路徑,如:E:/app-ui-autotest/log/log.txt

filemode 指定打開文件的模式(如果文件打開模式未指定,則默認為'a')

常見的文件讀寫方式:

  • w 以寫的方式打開
  • W 清空后寫入(文件已存在)
  • r 以讀的方式打開
  • a 以追加模式打開(即在文件原有的數據后面添加)

format 指定日志輸出格式

level 將根記錄器級別設置為指定級別

3,示例1:日志打印至控制台

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 import logging logging.basicConfig(filename='./log.txt', level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') logging.debug('This is debug message') logging.info('This is info message') logging.warning('This is warning message') logging.error('This is error message') logging.critical('This is critical message') 

控制台輸出結果:

2020-09-30 10:45:05,119 logging_test.py[line:7] DEBUG This is debug message. 2020-09-30 10:45:05,119 logging_test.py[line:9] INFO This is info message. 2020-09-30 10:45:05,119 logging_test.py[line:11] WARNING This is warning message. 2020-09-30 10:45:05,120 logging_test.py[line:13] ERROR This is error message. 2020-09-30 10:45:05,120 logging_test.py[line:15] CRITICAL This is critical message. 

4,示例2:日志保存至文件

logging.basicConfig(filename='log.txt', level=logging.INFO, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') logging.debug('This is debug message') logging.info('This is info message') logging.warning('This is warning message') logging.error('This is error message') logging.critical('This is critical message') 

輸出格式:

2020-09-30 10:45:05,119 logging_test.py[line:9] INFO This is info message. 2020-09-30 10:45:05,119 logging_test.py[line:11] WARNING This is warning message. 2020-09-30 10:45:05,120 logging_test.py[line:13] ERROR This is error message. 2020-09-30 10:45:05,120 logging_test.py[line:15] CRITICAL This is critical message. 

注意:

  • 相較於控制台打印日志,文件保存日志的區別在於basicConfig()方法中加入了filename參數(即文件的完整路徑)。

  • 保存日志至文件示例中,因為參數level=logging.INFO,所以DEBUG級別的日志未輸出

四,logging的第二種使用方式:日志流處理流程

1,logging四大組件介紹

logging模塊包括Logger,Handler,Filter,Formatter四個部分。

  • Logger 記錄器,用於設置日志采集。
  • Handler 處理器,將日志記錄發送至合適的路徑。
  • Filter 過濾器,提供了更好的粒度控制,它可以決定輸出哪些日志記錄。
  • Formatter 格式化器,指明了最終輸出中日志的格式。

2,Logger 記錄器

使用日志流采集日志時,須先創建Logger實例,即創建一個記錄器(如果沒有顯式的進行創建,則默認創建一個root logger,並應用默認的日志級別WARNING,Handler和Formatter),然后做以下三件事:

  • 為程序提供記錄日志的接口
  • 根據過濾器設置的級別對日志進行過濾
  • 將過濾后的日志根據級別分發給不同handler

3,Handler 處理器

Handler處理器作用是,將日志記錄發送至合適的路徑。如發送至文件或控制台,此時需要使用兩個處理器,用於輸出控制台的處理器,另一個是用於輸出文件的處理器。通過 addHandler() 方法添加處理器 。常用的處理器類型有以下兩種:

3.1,StreamHandler

  • 將日志信息發送至sys.stdout、sys.stderr或任何類似文件流對象,如在Pycharm IDE上顯示的日志信息。

  • 構造函數為:StreamHandler(strm)。參數strm是一個文件對象,默認是sys.stderr。

3.2,FileHandler

  • 將日志記錄輸出發送至磁盤文件。 它繼承了StreamHandler的輸出功能,不過FileHandler會幫你打開這個文件,用於向一個文件輸出日志信息。

  • 構造函數為:FileHandler(filename, mode)。參數filename為文件名(文件完整路徑),參數mode為文件打開方式,默認為'a'即在文末追加。

自動化測試使用這兩種類型就夠了,其他還有RotatingFileHandler、TimedRotatingFileHandler、NullHandler等處理器,有興趣可以查找資料了解。

4,Filter 過濾器

顧名思義是用於過濾,Handlers 與 Loggers 使用 Filters 可以完成比級別更復雜的過濾。不多做介紹,有興趣可以查找資料了解。

5,Formatter 格式化器

Formatter用於設置日志的格式與內容,默認的時間格式為%Y-%m-%d %H:%M:%S,更多格式如下:

格式
描述
%(levelno)s 打印日志級別的數值
%(levelname)s 打印日志級別的名稱
%(pathname)s 打印當前執行程序的路徑
%(filename)s 打印當前執行程序的名稱
%(funcName)s 打印日志的當前函數
%(lineno)d 打印日志的當前行號
%(asctime)s 打印日志的時間
%(thread)d 打印線程ID
%(threadName)s 打印線程名稱
%(process)d 打印進程ID
%(message)s 打印日志信息

6,使用示例:將日志輸出至控制台,同時保存至文件

根據logging的模塊化來編寫代碼,思路參考如下。

目錄結構

logging_test.py

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 import logging # 第一步,創建日志記錄器 # 1,創建一個日志記錄器logger logger = logging.getLogger() # 2,設置日志記錄器的日志級別,這里的日志級別是日志記錄器能記錄到的最低級別,區別於后面Handler里setLevel的日志級別 logger.setLevel(logging.DEBUG) # 第二步,創建日志處理器Handler。這里創建一個Handler,用於將日志寫入文件 # 3,創建一個Handler,用於寫入日志文件,日志文件的路徑自行定義 logFile = './log.txt' fh = logging.FileHandler(logFile, mode='a', encoding='utf-8') # 4,設置保存至文件的日志等級 fh.setLevel(logging.INFO) # 第三步,定義Handler的輸出格式 # 5,日志輸出格式定義如下 format= logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') # 6,設置 寫入日志文件的Handler 的日志格式 fh.setFormatter(format) # 第四步,將Handler添加至日志記錄器logger里 logger.addHandler(fh) # 同樣的,創建一個Handler用於控制台輸出日志 ch = logging.StreamHandler() ch.setLevel(logging.INFO) ch.setFormatter(format) logger.addHandler(ch) # 輸出日志 logger.info("This is info message") logger.warning("This is warning message") logger.error("This is error message") logger.critical("This is critical message") 

Pycharm運行logging_test.py模塊,log.txt以及Pycharm控制台得到如下結果:

2020-10-07 15:54:04,752 test.py[line:3] INFO This is info message 2020-10-07 15:54:04,752 test.py[line:4] WARNING This is warning message 2020-10-07 15:54:04,752 test.py[line:5] ERROR This is error message 2020-10-07 15:54:04,752 test.py[line:6] CRITICAL This is critical message 

五,logging 實戰

1,測試場景

給登錄今日頭條app的操作添加日志采集。

2,簡單配置代碼示例

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 import logging from appium import webdriver logging.basicConfig(filename='./testLog.log', level=logging.INFO, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') def android_driver(): desired_caps = { "platformName": "Android", "platformVersion": "10", "deviceName": "PCT_AL10", "appPackage": "com.ss.android.article.news", "appActivity": ".activity.MainActivity", "unicodeKeyboard": True, "resetKeyboard": True, "noReset": True, } logging.info("啟動今日頭條APP...") driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) return driver def login_opera(driver): '''登錄今日頭條操作''' logging.info("開始登陸今日頭條APP...") try: driver.find_element_by_id("com.ss.android.article.news:id/cji").click() # 點擊【我知道了】 driver.find_element_by_id("android:id/button1").click() # 點擊權限管理-確定按鈕 driver.find_element_by_xpath("//android.widget.TabWidget/android.widget.RelativeLayout[@index=3]").click() # 點擊未登錄 driver.find_element_by_id("com.ss.android.article.news:id/a10").click() # 未登錄頁點擊登錄按鈕 driver.find_element_by_id("com.ss.android.article.news:id/bgh").click() # 登錄頁點擊“。。。” driver.find_element_by_xpath("//android.widget.LinearLayout[@index=4]").click() # 選擇密碼登錄 driver.find_element_by_id("com.ss.android.article.news:id/bu").send_keys("18768124236") # 輸入賬號 driver.find_element_by_id("com.ss.android.article.news:id/c5").send_keys("xiaoqq3915172") # 輸入密碼 driver.find_element_by_id("com.ss.android.article.news:id/a2o").click() # 點擊登錄 except Exception as e: logging.error("登錄錯誤,原因為:{}".format(e)) else: logging.info("登陸成功...") driver = android_driver() login_opera(driver) 

登錄成功則日志輸出如下:

2020-09-30 18:20:05,119 logging_test.py[line:21] INFO 啟動今日頭條APP... 2020-09-30 18:20:10,119 logging_test.py[line:27] INFO 開始登陸今日頭條APP... 2020-09-30 18:21:07,120 logging_test.py[line:41] INFO 登陸成功... 

3,拋出問題

上面示例代碼成功地獲取了日志信息,但這種寫法只能作用於當前模塊。而一個自動化測試項目往往有多個模塊,如果在每個需要獲取日志的模塊都使用這樣的方式,顯然是不方便維護的。那么我們需要怎么解決呢?

4,解決思路

使用日志流處理流程。提供以下兩種思路:

思路1:使用python代碼實現日志配置。先創建日志記錄器,並設置好Handler與日志格式,如上面的logging_test.py模塊構造logger,其他模塊采集日志時直接調用。

思路2:將日志的格式、輸出路徑等參數抽離出來放置在專門的配置文件里,如logging.conf,使用專門的模塊處理,使用時直接在模塊調用即可。

5,思路1:使用python代碼實現日志配置示例

目錄結構

test.py中需要采集日志時,從logging_test.py導入logger即可。也可以將logging_test.py里的代碼進行進一步的封裝,再調用,這里僅僅只是示例。
logging_test.py

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 import logging # 創建日志記錄器 logger = logging.getLogger() logger.setLevel(logging.DEBUG) # 設置日志輸出格式 format= logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s') # 創建一個Handler用於將日志寫入文件 logFile = './log.txt' fh = logging.FileHandler(logFile, mode='a', encoding='utf-8') fh.setLevel(logging.INFO) fh.setFormatter(format) logger.addHandler(fh) # 同樣的,創建一個Handler用於控制台輸出日志 ch = logging.StreamHandler() ch.setLevel(logging.INFO) ch.setFormatter(format) logger.addHandler(ch) 

test.py

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 from appium import webdriver from log.logging_test import logger def android_driver(): desired_caps = { "platformName": "Android", "platformVersion": "10", "deviceName": "PCT_AL10", "appPackage": "com.ss.android.article.news", "appActivity": ".activity.MainActivity", "unicodeKeyboard": True, "resetKeyboard": True, "noReset": True, } logger.info("啟動今日頭條APP...") driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) return driver def login_opera(driver): '''登錄今日頭條操作''' logger.info("開始登陸今日頭條APP...") try: driver.find_element_by_id("com.ss.android.article.news:id/cji").click() # 點擊【我知道了】 driver.find_element_by_id("android:id/button1").click() # 點擊權限管理-確定按鈕 driver.find_element_by_xpath("//android.widget.TabWidget/android.widget.RelativeLayout[@index=3]").click() # 點擊未登錄 driver.find_element_by_id("com.ss.android.article.news:id/a10").click() # 未登錄頁點擊登錄按鈕 driver.find_element_by_id("com.ss.android.article.news:id/bgh").click() # 登錄頁點擊“。。。” driver.find_element_by_xpath("//android.widget.LinearLayout[@index=4]").click() # 選擇密碼登錄 driver.find_element_by_id("com.ss.android.article.news:id/bu").send_keys("18768124236") # 輸入賬號 driver.find_element_by_id("com.ss.android.article.news:id/c5").send_keys("xiaoqq3915172") # 輸入密碼 driver.find_element_by_id("com.ss.android.article.news:id/a2o").click() # 點擊登錄 except Exception as e: logger.error("登錄錯誤,原因為:{}".format(e)) else: logger.info("登陸成功...") driver = android_driver() login_opera(driver) 

運行test.py,結果如下:

2020-10-07 18:45:05,119 logging_test.py[line:21] INFO 啟動今日頭條APP... 2020-10-07 18:45:11,119 logging_test.py[line:27] INFO 開始登陸今日頭條APP... 2020-10-07 18:45:20,120 logging_test.py[line:41] INFO 登陸成功... 

6,思路2:日志格式配置示例

6.1,logger.conf文件

[loggers]   # loggers日志器對象列表,必須包含
keys=root, exampleLogger    # 一定要包含root這個值,當使用無參函數logging.getLogger()時,默認返回root這個logger,其他自定義logger可以通過logging.getLogger("exampleLogger")方式進行調用

[handlers] # handlers處理器對象列表,必須包含
keys=consoleHandler, fileHandler    # 定義聲明handlers信息

[formatters] # formatters格式對象列表,必須包含
keys=form01,form02

[logger_root] # 對loggers中聲明的logger進行逐個配置,且要一一對應,在所有的logger中,必須制定lebel和handlers這兩個選項。對於非roothandler,還需要添加一些額外的option,如qualname、propagate等。handlers可以指定多個,中間用逗號隔開,比如handlers=fileHandler,consoleHandler,同時制定使用控制台和文件輸出日志
level=DEBUG
handlers=consoleHandler, fileHandler

[logger_exampleLogger]  # 配置日志處理器exampleLogger:設置日志級別、日志輸出指定的處理器配置文件,如consoleHandler,fileHandler
level=DEBUG
handlers=consoleHandler, fileHandler
qualname=exampleLogger  # qualname 表示它在logger層級中的名字,在應用代碼中通過這個名字制定所使用的handler
propagate=0 # 可選項,其默認是為1,表示消息將會傳遞給高層次logger的handler

[handler_consoleHandler]    # 日志處理器consoleHandler的配置文件
class=StreamHandler   # 定控制台輸出。將日志消息發送到輸出到Stream,如std.out, std.err或任何file-like對象
level=DEBUG   # 日志級別
formatter=form01    # 輸出格式
args=(sys.stdout,)

[handler_fileHandler]   # 日志處理器fileHandler的配置文件
class=FileHandler    # 將日志輸出至磁盤文件
level=DEBUG # 日志級別
formatter=form02    # 輸出格式
args=('./log.txt', 'a', 'UTF-8') # 參數如未設置絕對路徑,則默認生成在執行文件log.py的工作目錄。指定日志文件的打開模式,默認為’a’

[formatter_form01]  # 格式配置1
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]  # 格式配置2
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

注意:

  • 為了說明配置結構,這里的配置文件 logger.conf 里加了中文注釋,實際使用時需要將注釋去掉或改寫成英文注釋,否則會報編碼錯誤。

  • 配置文件中包含三大主要模塊:loggers,handlers,formatters。這三個主要模塊包含的內容都是通過keys進行指定,然后通過logger_key、handler_key、formatter_key對里面的key進行具體的設置。

  • 配置handlers中的handler_consoleHandler的參數:指定日志輸出到控制台、級別、輸出格式、參數。

  • 配置handlers中的handler_fileHandlers的參數:指定將日志輸出至磁盤文件、設置日志級別、輸出格式、參數等。

  • 配置日志輸出格式formatter_xxx,可配置多個,如:form01,form02。

6.2,讀取配置文件,創建日志記錄器logger

baseLog.py

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 import logging.config CON_LOG='./logger.conf' # 配置文件路徑 logging.config.fileConfig(CON_LOG) # '讀取日志配置文件' logger = logging.getLogger('exampleLogger') # 創建一個日志器logger 

6.3,調用示例

目錄結構如下

test.py

# -*- coding:utf-8 -*- # @author: 給你一頁白紙 from appium import webdriver from log.baseLog import logger def android_driver(): desired_caps = { "platformName": "Android", "platformVersion": "10", "deviceName": "PCT_AL10", "appPackage": "com.ss.android.article.news", "appActivity": ".activity.MainActivity", "unicodeKeyboard": True, "resetKeyboard": True, "noReset": True, } logger.info("啟動今日頭條APP...") driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) return driver def login_opera(driver): '''登錄今日頭條操作''' logger.info("開始登陸今日頭條APP...") try: driver.find_element_by_id("com.ss.android.article.news:id/cji").click() # 點擊【我知道了】 driver.find_element_by_id("android:id/button1").click() # 點擊權限管理-確定按鈕 driver.find_element_by_xpath("//android.widget.TabWidget/android.widget.RelativeLayout[@index=3]").click() # 點擊未登錄 driver.find_element_by_id("com.ss.android.article.news:id/a10").click() # 未登錄頁點擊登錄按鈕 driver.find_element_by_id("com.ss.android.article.news:id/bgh").click() # 登錄頁點擊“。。。” driver.find_element_by_xpath("//android.widget.LinearLayout[@index=4]").click() # 選擇密碼登錄 driver.find_element_by_id("com.ss.android.article.news:id/bu").send_keys("18768124236") # 輸入賬號 driver.find_element_by_id("com.ss.android.article.news:id/c5").send_keys("xiaoqq3915172") # 輸入密碼 driver.find_element_by_id("com.ss.android.article.news:id/a2o").click() # 點擊登錄 except Exception as e: logger.error("登錄錯誤,原因為:{}".format(e)) else: logger.info("登陸成功...") driver = android_driver() login_opera(driver) 

控制台、log.txt輸出結果如下:

2020-10-07 19:30:35,119 logging_test.py[line:21] INFO 啟動今日頭條APP... 2020-10-07 19:30:40,119 logging_test.py[line:27] INFO 開始登陸今日頭條APP... 2020-10-07 19:31:12,120 logging_test.py[line:41] INFO 登陸成功... 

7,總結

在實際使用python做自動化測試過程中兩種解決思路都可以使用,且都挺方便。其中對於思路1,還可以將代碼進行更進一步的封裝。

六、項目實戰(多線程)

1、日志類

#-*-coding:utf-8-*-
"""
This is to get loggrr
"""
import os
import logging
import time

class Logger(object):
    """
    This is get log class
    """
    def __init__(self, path, name, Flevel=logging.DEBUG):
        self.time = time.strftime("%Y-%m-%d")
        self.fileName = path + name + "-" + self.time + ".log"
        self.logger = logging.getLogger(self.fileName)
        self.logger.setLevel(Flevel) 
        fmt = logging.Formatter('[%(asctime)s %(threadName)s]\
                [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
        fh = logging.FileHandler(self.fileName)
        fh.setFormatter(fmt)
        fh.setLevel(Flevel)
        self.logger.addHandler(fh)
    
    def debug(self, message):
        """
        This is debug
        """
        self.logger.debug(message)

    def info(self, message):
        """
        This is info
        """
        self.logger.info(message)
    
    def warn(self, message):
        """
        This is warn
        """
        self.logger.warn(message)

    def error(self, message):
        """
        This is error
        """
        self.logger.error(message)

    def critical(self, message):
        """
        This is critical
        """
        self.logger.critical(message)

    
if __name__ == '__main__':
    logyyx = Logger("/home/map/workspace/\
            zhangxianrong/crawl-brand/lib/", "meizu")
    logyyx.debug('一個debug信息')
    logyyx.info('一個info信息')
    logyyx.warn('一個warning信息')
    logyyx.error('一個error信息')
    logyyx.critical('一個致命critical信息')

2、使用

#-*-coding:utf-8-*-
import urllib2
import json
import sys
from thePath import rootPath
sys.path.append(rootPath + "lib")
from superCrawl import SUPERCRAWL
import re
from StarBuck_conf import poi
from superLogger import Logger

class StarBucks(SUPERCRAWL):
    """
    This is StarBucks
    """
    def __init__(self):
        super(StarBucks, self).__init__()
        self.tokenurl = "https://www.starbucks.com.cn/TRIGGER/"\
                          "starbucks/getStoreLocator"
        dir = rootPath + "data/PrimaryData/HeadBrand/"
        name = "StarBucks.json"
        self.directory = self.getDirectory(dir)
        self.outName = self.getPath(self.directory, name)
        self.token = ""
        self.logyyx = Logger(rootPath, "starBucks")

    def getUrlDetail(self, point_x, point_y, token):
        """
        Get Url Detail
        """
        url = "https://openapi.starbucks.com/v1/"\
              "stores/nearby?limit=10&radius=10&latlng="\
              + str(point_y) + "," +str(point_x) + \
              "&access_token=" + token + \
              "&locale=zh&callback=jQuery19109599463"\
              "73590788_1512705345611&_=1512705345613"
        return url

    def washData(self, html):
        """
        Wash data from page
        """
        result = re.findall("\(([\d\D]*)\)", html)
        try:
            data = result[0]
            data_json = json.loads(data)
            items = []
            for dataItem in data_json["stores"]:
                dataItem = dataItem["store"]
                item = {}
                item["name"] = dataItem["name"]
                address = dataItem["address"]
                item["address"] = ""
                if address["streetAddressLine1"]:
                    item["address"] = item["address"] + address["streetAddressLine1"]
                if address["streetAddressLine3"]:
                    item["address"] = item["address"] + address["streetAddressLine3"]
                if address["streetAddressLine2"]:
                    item["address"] = item["address"] + address["streetAddressLine2"]
                item["address1"] = address["streetAddressLine1"]
                item["address3"] = address["streetAddressLine3"]
                item["address2"] = address["streetAddressLine2"]
                item["lat"] = dataItem["coordinates"]["latitude"]
                item["lng"] = dataItem["coordinates"]["longitude"]
                item["city"] = dataItem["address"]["city"]
                item["county"] = dataItem["address"]["streetAddressLine1"]
                time = dataItem["regularHours"]["monday"]
                item["oprntime"] = time["openTime"]
                item["closetime"] = time["closeTime"]
                items.append(item)
            return items
        except Exception as e:
            print e
            self.logyyx.warn(e)
            return None
            
    def getData(self, wFileName):
        """
        This is to get data
        """
        fp = open(wFileName, "w")
        self.token = self.getHtml(self.tokenurl)
        for pro, pointList in poi.items():       
            print pro
            for point in pointList:
                try:
                    xys = point.split(",")
                    point_x = xys[0]
                    point_y = xys[1]
                    try:
                        dataList = self.getDataList(point_x, point_y, self.token)
                    except Exception as e:
                        dataList = self.getDataList(point_x, point_y)
                    for data in dataList:
                        li = json.dumps(data, ensure_ascii = False) + "\n"
                        fp.write(li.encode("utf8").replace("\\t", "").\
                                replace("\\n", "").replace("\\r", ""))
                except Exception as e:
                    print e
                    continue    
        fp.close()

    def getDetailData(self, rFileName, wFileName):
        """
        This is get detail data
        """
        fp = open(rFileName, "r")
        fpNew = open(wFileName, "w")
        self.token = self.getHtml(self.tokenurl)
        for fpLine in fp:
            try:
                jsonData = json.loads(fpLine)
                point_x = jsonData["lng"]
                point_y = jsonData["lat"]
                try:
                    dataList = self.getDataList(point_x, point_y, self.token)
                except Exception as e:
                    dataList = self.getDataList(point_x, point_y)
                for data in dataList:
                    li = json.dumps(data, ensure_ascii = False) + "\n"
                    fpNew.write(li.encode("utf8").replace("\\t", "").\
                       replace("\\n", "").replace("\\r", ""))
            except Exception as e:
                print e
                continue
        fpNew.close()
        fp.close()
            
    def getDataList(self, point_x, point_y, token = "0"):
        """
        update token to get data list
        if token == "0", this is want to update
        else: token don,t to update
        """
        if token == "0":
            self.token = self.getHtml(self.tokenurl)
        url = self.getUrlDetail(point_x, point_y, self.token)
        page = self.getHtml(url)
        dataList = self.washData(page)
        return dataList

    def divSameData(self, rfile, wfile):
        fp = open(rfile, "r")
        fp1 = open(wfile, "w")
        items = []
        for fpLine in fp:
            try:
                data = json.loads(fpLine)
                name = data["name"]
                address = data["address"]
                item = name + address
                if item in items:
                    continue
                else:
                    items.append(item)
                    fp1.write(fpLine)
            except Exception as e:
                print e
                continue

   
    def mainSpider(self):
        """
        This is main spider
        """
        name1 =  self.getPath(self.directory, "1.json")
        name2 = self.getPath(self.directory, "2.json")
        name2new = self.getPath(self.directory, "2new.json")
        name3 = self.getPath(self.directory, "3.json")
        name3new = self.getPath(self.directory, "3new.json")
        name4 = self.getPath(self.directory, "4.json")
        name4new = self.getPath(self.directory, "4new.json")
        name5 = self.getPath(self.directory, "5.json")
        name5new = self.getPath(self.directory, "5new.json")
        name6 =  self.getPath(self.directory, "6.json")
        name6new = self.getPath(self.directory, "6new.json")
        name7 = self.getPath(self.directory, "7.json")
        name7new = self.getPath(self.directory, "7new.json")
        name8 = self.getPath(self.directory, "8.json")
        name8new = self.getPath(self.directory, "8new.json")
        name9 = self.getPath(self.directory, "9.json")
        name9new = self.getPath(self.directory, "9new.json")
        name10 = self.getPath(self.directory, "10.json")
        print name1
        print name2 
        print name2new
        print name3
        print name3new
        print name4
        print name4new
        self.getData(name1)
        self.getDetailData(name1, name2)
        self.divSameData(name2, name2new)
        self.getDetailData(name2new, name3)
        self.getDetailData(name3, name3new)
        self.getDetailData(name3new, name4)
        self.divSameData(name4, name4new)
        self.getDetailData(name4new, name5)
        self.divSameData(name5, name5new) 
        self.getDetailData(name5new, name6)
        self.divSameData(name6, name6new)
        self.getDetailData(name6new, name7)
        self.divSameData(name7, name7new)
        self.getDetailData(name7new, name8)
        self.divSameData(name8, name8new)
        self.getDetailData(name8new, name9)
        self.divSameData(name9, name9new)
        self.getDetailData(name9new, name10)
        self.divSameData(name10, self.outName)
        print self.outName
        
if __name__ == '__main__':
    StarBucks().mainSpider()

 

 

 

 

參考:

https://www.cnblogs.com/lfr0123/p/13781152.html

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM