easonlai0707
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    1
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    ### 課程:進階程式設計1A 最後更新:2024/06/14 ### 分工表 :::warning |組員 | 學號 | 分工 | | -------- | -------- | -------- | | 藍家鎧 | 112021003 | 程式編寫(驗證碼、高鐵第一頁and座位偏好、車廂、高鐵第三頁) 60% 、報告 | | 謝秉宏 | 112021014 | 內容統整,參與討論、報告 | | 賴以宸 | 112021134 | 程式編寫(post請求、防反爬蟲、表格列印、高鐵第一頁)40%,報告製作 、報告 | | 游嘉旭 | 112021031 | 資料查詢,參與討論、報告 | | 童承逸 | 112021139 | 報告,參與討論 | ::: --- # **實行目標** 1. 依照填入資料實現列印高鐵時刻表 > 這邊的基本資料如站點、抵達站等,可以順便引用至2.selenium的部分 2. selenium自動填入資料 > 使用Selenium進行自動化操作的基本流程:準備WebDriver、與頁面交互、處理驗證碼、填寫表單信息、最終提交。考慮頁面加載時間、元素查找的穩定性等因素,以提高自動化腳本的可靠性和穩定性。 # 程式部分 **引用套件** ``` import requests import json import os from selenium.webdriver.chrome.service import Service from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.select import Select from selenium.common.exceptions import NoSuchElementException import time import prettytable as pt from prettytable import PrettyTable import tkinter as tk from tkinter import simpledialog from selenium.webdriver.common.by import By import cv2 from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression import numpy as np from PIL import Image import matplotlib.pyplot as plt from selenium.common.exceptions import NoSuchWindowException ``` > requests, json, os:用於發送HTTP請求、處理JSON數據和文件操作。 selenium相關模組:用於操作瀏覽器進行自動化。 cv2:OpenCV,用於圖像處理。 sklearn:機器學習庫,用於多項式回歸。 numpy, PIL, matplotlib:分別用於數值運算、圖像處理和繪圖。 tkinter:用於顯示簡單的對話框。 **定義一些圖像處理和匹配的函數** ``` def mse(imageA, imageB): imageB = cv2.cvtColor(imageB, cv2.COLOR_GRAY2RGB) err = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2) err /= float(imageA.shape[0] * imageA.shape[1]) return err def getNumber(pic): min_a = 9999999999 min_png = None for png in os.listdir('A-Z1'): ref = cv2.imread("A-Z1/" + png) if mse(ref, pic) < min_a: min_a = mse(ref, pic) min_png = png return min_png, min_a ``` > mse(imageA, imageB):計算兩幅圖像之間的均方誤差,用於衡量圖像相似度。 getNumber(pic):在指定目錄中尋找與給定圖像最相似的參考圖像。 >這邊會用到之後處理完的圖片去比對(後續程式有) **讀取車站數據** ``` # 指定要讀取的檔案路徑 file_path = "THSrjson/output.json" # 以 'r' 模式開啟檔案 with open(file_path, 'r') as file: # 讀取 JSON 資料 dataT = json.load(file) def find_station(name): for station in dataT: if station['StationName']['Zh_tw'] == name: return station['StationID'], station['StationCode'], station['StationName']['En'] return None, None, None ``` > 從文件THSrjson/output.json中讀取車站數據,並定義find_station(name)函數用於查找車站ID和代碼。 > 有關THSrjson/output.json需要去url = "https://www.thsrc.com.tw/TimeTable/Search"抓所需的資料 **發送查詢請求並處理結果** ``` url = "https://www.thsrc.com.tw/TimeTable/Search" data = { 'SearchType': 'S', 'Lang': 'TW', 'StartStation': start_station_nameEN, 'EndStation': to_station_nameEN, 'OutWardSearchDate': time2, 'OutWardSearchTime': date, 'ReturnSearchDate': time2, 'ReturnSearchTime': date, 'DiscountType': '' } # 發送 POST 請求 response = requests.post(url, data=data) # 將回應的內容轉換為 JSON 格式 json_data = response.json() # 創建新的資料夾 folder_name = "THSrjson" if not os.path.exists(folder_name): os.makedirs(folder_name) # 將 JSON 資料寫入檔案 file_path = os.path.join(folder_name, "output.json") with open(file_path, "w") as file: json.dump(json_data, file) result = json.loads(response.text) ``` > 這邊選擇的方式是利用post請求網頁,下載到本地處理 * > 會這樣選擇是因為之前嘗試直接發送請求,就算嘗試寫偽裝與payload,會回報一些奇怪的資訊而不是我想要的json檔(這邊猜測他會防這種請求)以下是錯誤的請求方式![螢幕擷取畫面 (75)](https://hackmd.io/_uploads/BJiXZ3_HR.png) ![螢幕擷取畫面 (76)](https://hackmd.io/_uploads/HynQZ3OHC.png) **用戶輸入** ``` # 讓使用者輸入資訊 """出發站點""" start_station_nameEN = input("1.南港 - NanGang\n2.台北 - TaiPei\n3.板橋 - BanQiao\n4.桃園 - TaoYuan\n5.新竹 - XinZhu\n6.苗栗 - MiaoLi\n7.台中 - TaiZhong\n8.彰化 - ZhangHua\n9.雲林 - YunLin\n10.嘉義 - JiaYi\n11.台南 - TaiNan\n12.左營 - ZuoYing\n選擇起程站(請輸入英文):") """抵達站點""" to_station_nameEN = input("1.南港 - NanGang\n2.台北 - TaiPei\n3.板橋 - BanQiao\n4.桃園 - TaoYuan\n5.新竹 - XinZhu\n6.苗栗 - MiaoLi\n7.台中 - TaiZhong\n8.彰化 - ZhangHua\n9.雲林 - YunLin\n10.嘉義 - JiaYi\n11.台南 - TaiNan\n12.左營 - ZuoYing\n選擇到達站(請輸入英文):") """日期""" time2 = input("請輸入出發日期(EX:2024/06/11)還是需要手動修改:") """時間""" date = input("請輸入出發時間(EX:16:00):") departure_time=str(date) '''車種''' car = int(input("選擇車廂 1.標準車廂 2.商務車廂 (請輸入數字):")) if car==2: car='商務車廂' else: car='標準車廂' '''座位偏好''' seatlike = int(input("選擇座位偏好 1.無座位偏好 2.靠窗 3.靠走道 (請輸入數字):")) if seatlike==2: seatlike='靠窗優先' elif seatlike ==3: seatlike='走道優先' else: seatlike='無座位偏好' Adult_ticket=str(input("幾張全票:")) child_ticket=str(input("幾張孩童票(6~11):")) Adult_child_ticket=int(Adult_ticket)+int(child_ticket) love_ticket=str(input("幾張愛心票:")) love_tickets = [] if int(love_ticket) > 0: for i in range(1, int(love_ticket) + 1): id_number = input(f"愛心票{i}身分證字號: ") love_tickets.append(id_number) old_ticket=str(input("幾張敬老票(65+):")) if int(old_ticket) > 0: for i in range(1, int(old_ticket) + 1): id_number = input(f"敬老票{i}身分證字號: ") love_tickets.append(id_number) IDch=int(input("選擇 1.身分證字號 2.護照/居留證/入出境許可證號碼(輸入數字):")) if IDch==2: ID=str(input("護照/居留證/入出境許可證號碼:")) IDch="護照/居留證/入出境許可證號碼" else: ID=str(input("身分證字號:")) IDch="身分證字號" membership=int(input("選擇 1.非高鐵會員 TGo/企業會員 2.有高鐵會員 TGo 帳號 3.企業會員統編(請輸入數字):")) cheak=1 TGo="" Compiled="" if membership==2: cheak=int(input("TGo帳號是否同取票人身分證字號 1.是 2.否(輸入數字):")) if cheak==2: TGo=str(input("身分證字號/會員卡號/手機號碼(已認證):")) else: cheak=1 elif membership==3: Compiled=input("企業統一編號:") else: pass ``` > 此處無難點,找到位置操作即可 > 但發現一個小問題有關time2 的,在請求網頁內容時可以正常操作,但到後面selenium,改以網站接受的aria_labele格式輸入,會有問題,後續會有程式碼。 **列印高鐵時刻表展示部分** ``` page = 0 tb =pt.PrettyTable() for t in result['data']['DepartureTable']['TrainItem']: if t['DepartureTime'] >= time2: Train_Num = t['TrainNumber'] Departure_Time = t['DepartureTime'] Destination_Time = t['DestinationTime'] Duration = t['Duration'] NonRC = t["NonReservedCar"] if t['Discount'] !=[]: for k in t['Discount']: discount = k['Name'] + k['Value'] else: discount = "-" tb.field_names = ['序列','車號','出發時間','抵達時間','預計耗時','自由座車廂','優惠類型'] tb.add_row([page, Train_Num, Departure_Time, Destination_Time, Duration, NonRC, discount]) page +=1 if page == 10: break print("\n","高鐵時刻表(目標時間後十筆資料)") print(tb, "\n") ``` 效果圖:![螢幕擷取畫面 (77)(1)](https://hackmd.io/_uploads/BkbWNndrR.png) **列印票價查詢表展示部分** ``` tb1 = PrettyTable() # 設定表格的欄位名稱 tb1.field_names = [' ', '全票', '孩童票/敬老票/愛心票','團體票'] # 添加行到表格中 tb1.add_row(['標準車廂', result['data']['PriceTable']['Coach'][0],result['data']['PriceTable']['Coach'][1],result['data']['PriceTable']['Coach'][2]]) tb1.add_row(['商務車廂'] + result['data']['PriceTable']['Business']) tb1.add_row(['自由座車廂'] + result['data']['PriceTable']['Unreserved']) # 如果有優惠票價,則添加到表格中 if result['data']['PriceTable']['Column'] != []: for column in result['data']['PriceTable']['Column']: tb1.add_column(column['ColumnName'], [column['CoachPrice'], column['BusinessPrice'], column['Unreserved']]) print('票價查詢') print(tb1) ``` 效果圖:![螢幕擷取畫面 (77)](https://hackmd.io/_uploads/r1R8V2dBC.png) **selenium.開啟網站** ``` driverPath = "/**自己放路徑**/(記得改斜線喔)" url = "https://irs.thsrc.com.tw/IMINT/?utm_source=thsrc&utm_medium=btnlink&utm_term=booking" chrome_options = Options() chrome_options.add_argument("--start-maximized") chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) chrome_options.add_experimental_option("useAutomationExtension", False) chrome_options.add_argument("--disable-blink-features=AutomationControlled") service = Service(driverPath) driver = webdriver.Chrome(service=service, options=chrome_options) driver.get(url) driver.implicitly_wait(10) ``` > 這邊特別去除了headline,因為我們在執行原本的程式發現只要是自動操作的selenium都會檔,(微笑)。去除headline即可 > ![螢幕擷取畫面 (78)](https://hackmd.io/_uploads/HyTcL2OrC.png) > 去除這個 **selenium.點選接受cookie** ``` button = driver.find_element(By.ID,"cookieAccpetBtn") button.click() ``` > 就點他 > ![99844](https://hackmd.io/_uploads/BJfLD3dBA.jpg) **selenium.選擇座位** ``` #填入選擇車廂 select_element0 = driver.find_element(By.NAME, "trainCon:trainRadioGroup") select0 = Select(select_element0) select0.select_by_visible_text(car) #填入座位偏好 select_element01 = driver.find_element(By.NAME, "seatCon:seatRadioGroup") select01 = Select(select_element01) select01.select_by_visible_text(seatlike) ``` > 簡單來說car:value0標準車廂value1有錢人做的商務, > seatlike:value0沒差的人value1靠窗的人value2靠走道的人 > ![螢幕擷取畫面 (79)](https://hackmd.io/_uploads/rJUfo3OSR.png) > **selenium.填入基本資料** ``` station_name_map = { "NanGang": "南港", "TaiPei": "台北", "BanQiao": "板橋", "TaoYuan": "桃園", "XinZhu": "新竹", "MiaoLi": "苗栗", "TaiZhong": "台中", "ZhangHua": "彰化", "YunLin": "雲林", "JiaYi": "嘉義", "TaiNan": "台南", "ZuoYing": "左營" } # 獲取中文站名 start_station_nameCN = station_name_map[start_station_nameEN] to_station_nameCN = station_name_map[to_station_nameEN] # 選擇下拉選單的選項 select_element1 = driver.find_element(By.NAME, "selectStartStation") Select(select_element1).select_by_visible_text(start_station_nameCN) select_element2 = driver.find_element(By.NAME, "selectDestinationStation") # 使用Select類來選擇下拉選單中的選項 select2 = Select(select_element2) select2.select_by_visible_text(to_station_nameCN) departure_time_select = Select(driver.find_element(By.NAME, "toTimeTable")) departure_time_select.select_by_visible_text(departure_time) #未解決的選日期問題(暫時用手點) """ driver.find_element(By.XPATH,"//input[@class='uk-input' and @readonly='readonly']").click() time.sleep(2) #替換成aria-label內可以接受的格式 year , month , day = time2.split("/") month_dict = {"01": "一月", "02": "二月", "03": "三月", "04": "四月", "05": "五月", "06": "六月", "07": "七月", "08": "八月", "09": "九月", "10": "十月", "11": "十一月", "12": "十二月"} ppp = month_dict[month] # 根據輸入日期找到對應的日期並點擊#class會變環不知道怎麼抓 date_xpath = f"//div[@class='flatpickr-months']//span[@aria-label='{month} {day},{year}' and @tabindex='-1']" WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, date_xpath))).click() """ #填入全票票數 select_element3 = driver.find_element(By.NAME, "ticketPanel:rows:0:ticketAmount") select3 = Select(select_element3) select3.select_by_visible_text(Adult_ticket) #填入孩童票票數 select_element4 = driver.find_element(By.NAME, "ticketPanel:rows:1:ticketAmount") select4 = Select(select_element4) select4.select_by_visible_text(child_ticket) #填入愛心票票數 select_element5 = driver.find_element(By.NAME, "ticketPanel:rows:2:ticketAmount") select5 = Select(select_element5) select5.select_by_visible_text(love_ticket) #填入敬老票票數 select_element6 = driver.find_element(By.NAME, "ticketPanel:rows:3:ticketAmount") select6 = Select(select_element6) select6.select_by_visible_text(old_ticket) #填入大學生票票數 #select_element7 = driver.find_element(By.NAME, "ticketPanel:rows:4:ticketAmount") #select7 = Select(select_element7) #select7.select_by_visible_text(College_student_tickets) ``` > 我自己觀察標籤的變化,就需要@class=flatday跟@aria-label > 先講@aria-label,這個他的格式是{月份(中文)} {日期},{年} > 所以我先寫一個字典讓程式對照中文的月份,但問題可能出在@class=flatday,很酷的是他是一個會變的標籤,選中後會變成@class=flatday_select(?嗎我記得 > 我不會選這個,而且找不到,也還在找怎麼解決,所以暫時用手點的![OIP](https://hackmd.io/_uploads/B1A5anurR.jpg) **selenium.驗證碼** ``` '''驗證碼''' while True: driver.save_screenshot('test.png') # 儲存當前網頁截圖為 test.png element = driver.find_element(By.ID, "BookingS1Form_homeCaptcha_passCode")# 查找驗證碼元素 # 獲取驗證碼元素的位置和大小 left = element.location['x'] right = element.location['x'] + element.size['width'] top = element.location['y'] bottom = element.location['y'] + element.size['height'] # 裁剪驗證碼圖像 img = Image.open('test.png') # 打開截取的網頁截圖 img = img.crop((left, top, right, bottom))# 根據位置裁剪驗證碼圖像 img = img.resize((119, 47))# 調整裁剪後的驗證碼圖像大小為 119x47 像素 captcha_filename = os.path.join(folder_name, 'captcha.png') # 儲存裁剪後的驗證碼圖像 img.save(captcha_filename, 'png') # 圖像處理 img = cv2.imread(captcha_filename)# 讀取裁剪後的驗證碼圖像 dst = cv2.fastNlMeansDenoisingColored(img, None, 30, 30, 7, 21) # 降噪處理 gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)# 將圖像轉換為灰度圖像 ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)# 二值化圖像,反轉顏色 imgarr = thresh.copy() # 複製二值化圖像數組 imgarr[:, 5:115] = 0# 將5到115的像素值設置為 0 # 提取白色像素座標 imagedata = np.where(imgarr == 255) X = np.array([imagedata[1]])# 提取白色像素的 X 坐標 Y = 47 - imagedata[0]# 計算白色像素的 Y 坐標,並調整座標系統 # 使用多項式回歸進行擬合 poly_reg = PolynomialFeatures(degree=2)# 創建二次多項式特徵 X_ = poly_reg.fit_transform(X.T) # 將 X 轉換為多項式特徵 regr = LinearRegression()# 創建線性回歸模型 regr.fit(X_, Y) # 對白色像素進行多項式回歸擬合 X2 = np.array([[i for i in range(0, 119)]])# 創建新的 X 值範圍 X2_ = poly_reg.fit_transform(X2.T)# 將 X2 轉換為多項式特徵 # 圖像標記預測結果 newimg = thresh.copy()# 複製二值化圖像 for ele in np.column_stack([regr.predict(X2_).round(0), X2[0]]): pos = 47 - int(ele[0])# 計算標記位置 newimg[pos-3:pos+3, int(ele[1])] = 255 - newimg[pos-3:pos+3, int(ele[1])]# 標記像素位置 # 保存图像 plt.figure(figsize=(10, 5)) plt.subplot(121) plt.imshow(thresh, cmap='gray') plt.title('Threshold Image')# 顯示原始二值化圖像 plt.subplot(122) plt.imshow(newimg, cmap='gray') plt.title('Processed Image')# 顯示處理後的圖像 plt.tight_layout() processed_image_filename = os.path.join(folder_name, 'processed_images.png')# 儲存處理後的圖像 plt.savefig(processed_image_filename)# 保存處理後的圖像 plt.close()# 關閉圖像視窗 # 切割圖像 height, width = newimg.shape# 獲取處理後圖像的高度和寬度 for cut_point in cut_points:# 切割點 #print(cut_point) if 0 < cut_point < width:# 確認切割點在圖像範圍內 cv2.line(newimg, (cut_point, 0), (cut_point, height), (0, 0, 0), 1)# 在圖像上繪製切割線 cut_images = [] for i, cut_point in enumerate(cut_points):# 遍歷切割點 #print(i) if i == 0: cut_images.append(newimg[:, :cut_point])# 切割第一個圖像區域 elif i == len(cut_points) - 1: cut_images.append(newimg[:, cut_points[i-1]:])# 切割最後一個圖像區域 else: cut_images.append(newimg[:, cut_points[i-1]:cut_point])# 切割中間的圖像區域 # 移動 if 條件至外層循環 for i, cut_image in enumerate(cut_images): # 切割後的圖像區域 # 跳過 cut_image_0 和 cut_image_5 if i == 0 or i == 5:# 跳過第一個和第五個圖像區域 continue # 调整图像大小为 50x50 像素 resized_cut_image = cv2.resize(cut_image, (50, 50))# 調整切割後的圖像大小為 50x50 像素 # 保存处理后的切割图像 cut_image_filename = os.path.join(folder_name, f'cut_image_{i}.png')# 儲存切割圖像 cv2.imwrite(cut_image_filename, resized_cut_image) # 保存處理後的切割圖像 number, error = getNumber(resized_cut_image) """print(f'Predicted Number for cut_image_{i}: {number}, Error: {error}')""" if i == 1: c1 = number[0] elif i == 2: c2 = number[0] elif i == 3: c3 = number[0] elif i == 4: c4 = number[0] c1234 = c1 + c2 + c3 + c4 input_element = driver.find_element(By.ID, "securityCode") # 根據ID查找<input>元素 input_element.send_keys(c1234)# 輸入拼接後的驗證碼 time.sleep(5)# 等待一段時間以便修改日期 submit_button = driver.find_element(By.ID, "SubmitButton")# 查找提交按鈕元素 # 點擊提交按鈕 submit_button.click() time.sleep(3) error_elements = driver.find_elements(By.CLASS_NAME, "feedbackPanelERROR")# 查找錯誤信息元素 # 檢查是否存在錯誤信息元素 try: if error_elements: for element in error_elements: if "檢測碼輸入錯誤,請確認後重新輸入,謝謝!" in element.text: print("檢測碼輸入錯誤,將重新輸入,謝謝!") break else: print("驗證碼檢測成功") pass1 = False # 設置標誌變量以終止循環 except NoSuchWindowException: pass finally: if pass1==False:# 如果標誌變量為 False,則退出循環 break else: continue # 否則繼續下一輪循環 '''驗證碼''' print("continue") ``` > 擷取圖片+處理圖片講解 > driver.save_screenshot('test.png') > 會得到![要用的](https://hackmd.io/_uploads/rkxryTOBC.png) > element = driver.find_element(By.ID,"BookingS1Form_homeCaptcha_passCode") > left = element.location['x'] > right = element.location['x'] + element.size['width'] > top = element.location['y'] > bottom = element.location['y'] + element.size['height'] > # 裁剪驗證碼圖像 > img = Image.open('test.png') # 確保文件名以 .png 結尾 > img = img.crop((left, top, right, bottom)) > img = img.resize((119, 47)) > captcha_filename = os.path.join(folder_name, f'captcha{A}.png') # 改變擴展名為 .png > img.save(captcha_filename, 'png') > 得到![要用](https://hackmd.io/_uploads/H1bDkTurA.png) > img = cv2.imread(captcha_filename) > dst = cv2.fastNlMeansDenoisingColored(img, None, 30, 30, 7, 21) > ![要用1](https://hackmd.io/_uploads/rJSO1auB0.png) > cv2.imwrite(processed_image_filename, dst) > gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY) > ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV) > ![要用2](https://hackmd.io/_uploads/r1hF1puBC.png) > > cv2.imwrite(processed_image_filename, thresh) > imgarr = thresh.copy() > imgarr[:, 5:115] = 0 > # 提取白色像素座標 > imagedata = np.where(imgarr == 255) > # 檢查是否找到了有效的像素座標 > if imagedata[0].size == 0: > print("未找到白色像素,請檢查圖像處理步驟") > continue # 跳過這一輪迭代 > X = np.array([imagedata[1]]) > Y = 47 - imagedata[0] > # 使用多項式回歸進行擬合 > poly_reg = PolynomialFeatures(degree=2) > X_ = poly_reg.fit_transform(X.T) > regr = LinearRegression() > regr.fit(X_, Y) > X2 = np.array([[i for i in range(0, 119)]]) > X2_ = poly_reg.fit_transform(X2.T) > # 圖像標記預測結果 > newimg = thresh.copy() > for ele in np.column_stack([regr.predict(X2_).round(0), X2[0]]): > pos = 47 - int(ele[0]) > newimg[pos-3:pos+3, int(ele[1])] = 255 - newimg[pos-3:pos+3, int(ele[1])] > ![3要用](https://hackmd.io/_uploads/rJcokauBC.png) > cv2.imwrite(processed_image_filename, newimg) > # 保存图像 > plt.figure(figsize=(10, 5)) > plt.subplot(121) > plt.imshow(thresh, cmap='gray') > plt.title('Threshold Image') > plt.subplot(122) > plt.imshow(newimg, cmap='gray') > plt.title('Processed Image') > plt.tight_layout() > ![要用6](https://hackmd.io/_uploads/SkJ61pdH0.png) > height, width = newimg.shape > for cut_point in cut_points: > if 0 < cut_point < width: > cv2.line(newimg, (cut_point, 0), (cut_point, height), (0, 0, 0), 1) > cut_images = [] > for i, cut_point in enumerate(cut_points): > if i == 0: > cut_images.append(newimg[:, :cut_point]) > elif i == len(cut_points) - 1: > cut_images.append(newimg[:, cut_points[i-1]:]) > else: > cut_images.append(newimg[:, cut_points[i-1]:cut_point]) > # 移動 if 條件至外層循環 > for i, cut_image in enumerate(cut_images): > # 跳過 cut_image_0 和 cut_image_5 > if i == 0 or i == 5: > continue > # 调整图像大小为 50x50 像素 > resized_cut_image = cv2.resize(cut_image, (50, 50)) > # 保存处理后的切割图像 > cut_image_filename = os.path.join(folder_name, f'cut_image_{i}.png') > cv2.imwrite(cut_image_filename, resized_cut_image) > ![螢幕擷取畫面 2024-06-14 024834](https://hackmd.io/_uploads/HkSkg6_rR.png) > (四張切割的) **等一下第二頁的內容** ``` time.sleep(10)#暫停10秒 ``` **填入資料** ``` select_element8 = driver.find_element(By.NAME, "idInputRadio")#選擇身分證字號或護照/居留證/入出境許可證號碼 select8 = Select(select_element8) select8.select_by_visible_text(IDch) input_element1 = driver.find_element(By.ID, "idNumber")#輸入身分證字號 input_element1.send_keys(ID) loveandlodid0='BookingS3Form_TicketPassengerInfoInputPanel_passengerDataView__passengerDataView2_passengerDataIdNumber'#輸入愛心票及敬老票的身分證字號 for idx, ticket in enumerate(range(Adult_child_ticket, Adult_child_ticket + len(love_tickets))): # 在這裡處理每個票的操作,ticket 是從 Adult_child_ticket 開始的連續整數 loveandlodid1=loveandlodid0.find("BookingS3Form_TicketPassengerInfoInputPanel_passengerDataView_")+len('BookingS3Form_TicketPassengerInfoInputPanel_passengerDataView_') loveandlodid2 = loveandlodid0[:loveandlodid1]+str(ticket)+loveandlodid0[loveandlodid1:] input_element1 = driver.find_element(By.ID, loveandlodid2) input_element1.send_keys(love_tickets[idx]) if membership==2: # 找到高鐵會員 TGo 帳號的 radio 按鈕,並點擊它 driver.find_element(By.ID, "memberSystemRadio1").click() if cheak==2: input_element1 = driver.find_element(By.ID, "msNumber") input_element1.send_keys(TGo) else: time.sleep(1) checkbox = driver.find_element(By.ID, "memberShipCheckBox")# 點選TGo帳號是否同取票人身分證字號 checkbox.click() elif membership==3: # 找到企業會員統編的 radio 按鈕,並點擊它 driver.find_element(By.ID, "memberSystemRadio2").click() input_element1 = driver.find_element(By.ID, "guNumber") #輸入企業會員統編 input_element1.send_keys(Compiled) checkbox = driver.find_element(By.NAME, "agree")# 查找同意和取方塊 checkbox.click() submit_button = driver.find_element(By.ID, "isSubmit")# 查找提交按鈕元素 # 點擊提交按鈕 submit_button.click() ``` ### 參考資料 headline去除、表格演示 > [高鐵自動訂票(Python) - HackMD](https://hackmd.io/@easonlai0707/Syh7UKdBA) > 部分思路大概架構 > [超简单,Python实现12306查票抢票](https://www.bilibili.com/video/BV1pM411V7Lp/?vd_source=004dfa4d7c2d18194ea4fdfdf9d2afaa)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully