# @author: zly# @function: Touch verification code# @time: 2020-09-15# @copyright: All Rights Reversedimport time
import randomfrom selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChainsfrom chaojiying import Chaojiying_Client
from constants import*classMakeTrack:"""Track generator, need to pass a distance parameter"""def__init__(self, distance=DISTANCE):self.distance = distancedefsegmentate(self, s):"""Track splitter, the size of each piece of track is not dividedReturns a list object of a track block:params --> Tracks to be segmented, int"""if SEGMENTNUM1 <=abs(s)< SEGMENTNUM2:s =[round(s /3)-3,round(s /3)+3]elifabs(s)>= SEGMENTNUM2:s =[round(s /5)-5,round(s /5)-3,round(s /5),round(s /5)+3,round(s /5)+5]else:s =[round(s)]return sdefmake_track(self):"""Make sliding track to simulate human normal movementReturn a list object of sliding track"""track =[]current = v0 =0while self.distance > current:# 隨機(jī)事件,隨機(jī)加速度,生成隨機(jī)位移t = random.randint(1,4)/2a = random.randint(1,3)# 速度、位移v0 += a * ts = v0 * t +0.5* a * t **2# 將不和規(guī)則的較大的位移進(jìn)行分割seg = self.segmentate(round(s))track.extend(seg)current += s# 對不超過目標(biāo)位移或者不足位移做補(bǔ)償whileTrue:ifsum(track)== self.distance:breakelifsum(track)> self.distance:track.pop()else:track.append(self.distance -sum(track))iflen(track)> TRACKMAXLENGTH:self.make_track()return trackclassLogin12306(Chaojiying_Client):""":paramusername 12306賬號 --> strpassword 12306密碼 --> strcusername 超級鷹賬號 --> strcpassword 超級鷹密碼 --> strsoft_id 軟件ID --> strcodetype 驗(yàn)證類型 --> intpath 驗(yàn)證碼圖片路徑 --> strThere are three to config your init configration1. by set constant2. by set config dict3. Direct set init configration"""def__init__(self, username=None, password=None,cusername=None, cpassword=None, soft_id=None,codetype=None, path=None,*args,**kwargs):# 配置優(yōu)化,可以字典的形式傳遞參數(shù)if kwargs.get('configs','None'):# 連接超級鷹,初始化super().__init__(username=kwargs['configs'].get('cusername',''),password=kwargs['configs'].get('cpassword',''),soft_id=kwargs['configs'].get('soft_id',''))self.username = kwargs['configs'].get('username','')self.password = kwargs['configs'].get('password','')self.cusername = kwargs['configs'].get('cusername','')self.cpassword = kwargs['configs'].get('cpassword','')self.soft_id = kwargs['configs'].get('soft_id','')self.codetype = kwargs['configs'].get('codetype','')self.path = kwargs['configs'].get('path','')elif USERNAME:self.username = USERNAMEself.password = PASSWORDself.cusername = CUSERNAMEself.cpassword = CPASSWORDself.soft_id = SOFTIDself.codetype = CODETIPEself.path = PATHelse:# 連接超級鷹,初始化super().__init__(username=cusername,password=cpassword,soft_id=soft_id)self.username = usernameself.password = passwordself.cusername = cusernameself.cpassword = cpasswordself.soft_id = soft_idself.codetype = codetypeself.path = pathself.run@propertydefrun(self):"""You can call the run method directly for login verification,or you can also call other methods to achieve this function:returnReturn false means login verification failedReturn true means login verification success"""self.driver = self.prepares()self.driver.get('https://kyfw.12306.cn/otn/resources/login.html')self.driver.implicitly_wait(IMPLICITLYWAIT)self.driver.maximize_window()time.sleep(1)# 1.輸入賬號密碼self.input_user_pwd(username=self.username, password=self.password)# 2.獲取驗(yàn)證圖片self.get_pic()whileTrue:# 3.識別圖片,獲取坐標(biāo)position, pic_id = self.get_position(codetype=self.codetype)ifnot position:position, pic_id = self.get_position(codetype=self.codetype)# 4.圖片驗(yàn)證self.img_click(position)# 5.登錄login = self.login(pic_id)ifnot login:self.driver.refresh()self.input_user_pwd(username=self.username, password=self.password)self.get_pic()continue# 6.滑動滑塊returnTrueif self.slide()elseFalsedefprepares(self):"""Break through 12306 webriverReturns a webdrive after anti pickling"""# 12306通過圖片驗(yàn)證之后依然登陸不上,其中的原因是有webdriver反扒# 要想突破反扒,就必須修改帶有webdrive的標(biāo)志,我們用selenium打開的瀏覽器# 上面往往都會顯示 Chrome正受到自動測試軟件的控制# 因此我們需要修改Options和selenium瀏覽器的js標(biāo)志navigator# selenium控制的瀏覽器默認(rèn)是true/false,正常的是undefinedoptions = webdriver.ChromeOptions()options.add_experimental_option("excludeSwitches",["enable-automation"])options.add_experimental_option('useAutomationExtension',False)driver = webdriver.Chrome(options=options)driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{"source":"Object.defineProperty(""navigator, 'webdriver', ""{get: () => undefined})"})return driverdefinput_user_pwd(self, username=None, password=None):"""Enter 12306 account and password@username: 12306賬號 --> str, defalut is None@password: 12306密碼 --> str, defalut is NoneThe return 0 here has no effect, it just means the end of the function"""# 切換至賬號密碼登錄self.driver.find_element_by_xpath('//li[@class="login-hd-account"]/a').click()# 這里需要睡1-2秒,否則會報錯,加載js,瀏覽器js沒有代碼快time.sleep(2)# 輸入賬號密碼self.driver.find_element_by_id('J-userName').send_keys(username)self.driver.find_element_by_id('J-password').send_keys(password)return0defget_pic(self):"""Get touch captcha imageThe return 0 here has no effect, it just means the end of the function"""# 截圖self.driver.find_element_by_id('J-loginImg').screenshot(self.path)return0defget_position(self, codetype=None):"""Get the touch coordinates of super Eagle verification@soft_id: 軟件ID --> str, defalut is None@codetype: 驗(yàn)證類型 --> int, defalut is None:returna list object [position, pic_id]"""# 發(fā)送圖片,獲取坐標(biāo)是verify_data = self.PostPic(self.path, codetype)print(verify_data)# 如果成功獲取坐標(biāo)則格式化,否則return Noneif verify_data['err_no']==0:temp = verify_data['pic_str'].split('|')position =[i.split(',')for i in temp]return[position, verify_data['pic_id']]else:self.ReportError(verify_data['pic_id'])return[None, verify_data['pic_id']]defimg_click(self, position):"""Get the touch coordinates of super Eagle verification@position: 點(diǎn)觸坐標(biāo) --> Nested list, [['55', '55'], ['88', '88']...]The return 0 here has no effect, it just means the end of the function"""# 要點(diǎn)觸的圖片element = self.driver.find_element_by_id('J-loginImg')# 按照坐標(biāo)值點(diǎn)擊for k in position:# x、y需要int的原因:move_to_element_with_offset中x、y只能是int型x =int(k[0])y =int(k[1])ActionChains(self.driver).move_to_element_with_offset(element, x, y).click().perform()return0deflogin(self, pic_id=None):"""Its role is to log in and get cookiesReturn true means the verification is successful, otherwise it fails"""# 登錄,獲取cookiesself.driver.find_element_by_id('J-login').click()# 判斷圖片驗(yàn)證是否驗(yàn)證成功verify_tag = self.driver.find_element_by_xpath('//*[@class="lgcode-error"]')# 看verify_tag的display屬性是否可見,可見則表示驗(yàn)證失敗if verify_tag.is_displayed():# 別浪費(fèi)錢,向超級鷹報個錯self.ReportError(pic_id)print("圖片驗(yàn)證失敗,報錯成功")returnFalseprint("圖片驗(yàn)證成功")time.sleep(3)returnTruedefslide(self):"""Sliding verification,if it's successful return cookies, or return False"""try:# 定位滑塊element = self.driver.find_element_by_id('nc_1_n1z')# 生成軌跡track = MakeTrack().make_track()# 滑動ActionChains(self.driver).click_and_hold(element).perform()[ActionChains(self.driver).move_by_offset(i,0).perform()for i in track]ActionChains(self.driver).release(element).perform()# 時間取決于網(wǎng)速time.sleep(5)except Exception as e:# stale element reference: element is not attached to the page document# 頁面刷新導(dǎo)致獲取不到元素,若能夠滑動通過此錯誤無需再管,不是每次都會發(fā)生print(str(e))time.sleep(10)self.driver.quit()returnFalse# 判斷是否登陸成功try:self.driver.find_element_by_xpath('//*[@class="btn btn-primary ok"]').click()cookies = self.driver.get_cookies()print("恭喜您登陸成功")print(cookies)time.sleep(10)self.driver.quit()returnTrueexcept Exception as e:print(str(e))print("恭喜您登陸失敗,再來一次吧")time.sleep(10)self.driver.quit()returnFalseconfigs ={'username':'',# 12306賬號'password':'',# 12306密碼'cusername':'',# 超級鷹賬號'cpassword':'',# 超級鷹密碼'soft_id':'',# 軟件ID'codetype':9004,# 驗(yàn)證類型'path':''# 驗(yàn)證碼圖片路徑}Login12306(configs=configs)