iT邦幫忙

0

Python Selenium自動化抓取網站問題請教

  • 分享至 

  • xImage

您好:
因為參考的書籍較舊,完全抓不到範例
要用selenium去抓資料 URL = "https://tw.hotels.com/"

但目前該網站已經改版,
想請問 他會跳出彈掉視窗,要如何銷掉
目前到這一步 就錯了
https://ithelp.ithome.com.tw/upload/images/20241130/20104095FA4GNiS8BV.png

2.這些元件要如何選擇 或直接填入值?
https://ithelp.ithome.com.tw/upload/images/20241130/20104095ys9wujEK6U.png

3.下拉選單這要如何選值 或填入?
https://ithelp.ithome.com.tw/upload/images/20241130/20104095MizIWwo1zq.png

有試過用copilot問,但不能執行

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 初始化WebDriver
driver = webdriver.Chrome()  # 请确保chromedriver路径正确
driver.get('https://tw.hotels.com/')

# 输入搜索条件
search_box = driver.find_element(By.ID, 'qf-0q-destination')
search_box.send_keys('台北市')
search_box.send_keys(Keys.RETURN)

# 选择入住和退房日期
check_in = driver.find_element(By.ID, 'qf-0q-localised-check-in')
check_in.clear()
check_in.send_keys('2024-12-12')
check_out = driver.find_element(By.ID, 'qf-0q-localised-check-out')
check_out.clear()
check_out.send_keys('2024-12-14')
check_out.send_keys(Keys.RETURN)

# 等待页面加载
time.sleep(5)

# 抓取酒店信息
hotels = driver.find_elements(By.CLASS_NAME, 'hotel')
for hotel in hotels:
    name = hotel.find_element(By.CLASS_NAME, 'hotel-name').text
    price = hotel.find_element(By.CLASS_NAME, 'price').text
    print(f'Hotel: {name}, Price: {price}')

# 关闭浏览器
driver.quit()

謝謝

froce iT邦大師 1 級 ‧ 2024-12-01 08:58:12 檢舉
1 那個彈跳視窗只有第一次進入的時候有,然後按下其他網頁元素就能關掉。
2、3都是input,找元素直接填值就好。你範例就有send_keys了

然後每次我看到sleep都會念一句的就是,selenium有幫你寫好更好用的wait until。
https://selenium-python-zh.readthedocs.io/en/latest/waits.html
noway iT邦研究生 1 級 ‧ 2024-12-01 20:26:22 檢舉
您好:第一個地點,我有試著去找INPUT,但填值沒反應
第二個時間,他似乎就不是 INPUT了
謝謝
ccutmis iT邦高手 2 級 ‧ 2024-12-02 11:10:54 檢舉
手動設定好條件按下搜尋後,您應該會看到網址有變動,例如原本是:
https://tw.hotels.com/
變成:
https://tw.hotels.com/Hotel-Search?destination=台中%2C%20台灣&regionId=.... 略...
通過觀察網址列參數可以看到目的地、日期、人數、房間數等設定,因此在這個例子中,只需使用 driver.get() 帶入修改參數的網址,就可以獲得相關資訊,範例如下:

from selenium import webdriver
driver = webdriver.Chrome()

destination="台中%2C%20台灣"
day1="2024-12-08"
day2="2024-12-10"
adults="2"
rooms="1"

url=f"https://tw.hotels.com/Hotel-Search?destination={destination}&flexibility=0_DAY&d1={day1}&startDate={day1}&d2={day2}&endDate={day2}&adults={adults}&rooms={rooms}&theme=&userIntent=&semdtl=&useRewards=false&sort=RECOMMENDED"

driver.get(url)
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 個回答

0
oxiaoyueo
iT邦新手 5 級 ‧ 2024-12-01 04:42:11

你好~
我有稍微測試了一下你的程式碼
你使用了ID搜尋,但程式在瀏覽器找不到"qf-0q-destination"、"qf-0q-localised-check-in"等ID的物件,我自己用瀏覽器的開發人員工具搜尋也確實如此。
你在文中提到參考書籍較舊,不確定你是不是按照書上key進程式碼裡,程式才會找不到這些ID。
建議你先使用瀏覽器內建的開發人員工具(快捷鍵F12),或是對目標(button、input)右鍵>檢查,來查看物件有什麼名稱,例如ID、Class、Name等。
開發人員工具
找好對應的名稱後,可以先測試程式能不能抓到該物件,再繼續往下走,避免只有開發人員工具抓到但程式抓不到的狀況。
程式抓取方面有分Name、ID、XPath等,我自己是使用XPath,如何使用XPath就請自行搜尋使用方式,這邊就不細講。
以下是範例,在網站選擇一個地點:

from selenium import webdriver
from selenium.webdriver.common.by import By
import time

# 初始化WebDriver
driver = webdriver.Edge()
driver.get('https://tw.hotels.com/')

# 先隨意選一個選項
target_div = driver.find_element(By.XPATH, "//div[contains(@class, 'uitk-layout-grid-has-auto-rows')][1]") # 使用XPATH方式找尋物件,選擇找到的第一個
target_div.click() # 對目標點擊

# 等待選項出現
time.sleep(2)

select_one = driver.find_element(By.XPATH, "//ul/div[1]")
select_one.click()

至於你說的會跳出彈掉視窗,我這邊運行上他會自動消失,並沒有遇到這個狀況。
希望能幫助到你:-)

noway iT邦研究生 1 級 ‧ 2024-12-01 20:25:35 檢舉

您好:謝謝您
我參考您的範例,先單純填地點
但 地點參數,台北市
他並不會秀在INPUT 上
再按搜尋鈕
並沒有反映
ElementNotInteractableException: element not interactable
(Session info: chrome=131.0.6778.86)


def search_hotels(searchKey, checkInDate, checkOutDate):
    global driver
    
   # xx=driver.find_elements(By().XPATH ,'//*[@id="app-layer-base"]/div[1]/div[2]/div[1]/div[1]/div') # 空白地方
 
    
    
    target_div = driver.find_element(By.XPATH, "//div[contains(@class, 'uitk-layout-grid-has-auto-rows')][1]") # 使用XPATH方式找尋物件,選擇找到的第一個
    target_div.click() # 對目標點擊    
    # 等待選項出現
    time.sleep(2)   
    
    #select_one = driver.find_element(By.XPATH, "//ul/div[1]")
    #select_one.click()
 
    
    search_button=driver.find_elements(By().XPATH ,'//*[@id="search_button"]') # 查詢按鈕
    
    
    #driver.find_elements_by_xpath('//*[@id="app-layer-base"]/div[1]/div[1]/header/div/section/div/div/div[2]/div[2]/div[2]')
    #xx=  driver.find_elements_by_xpath('//*[@id="app-layer-base"]/div[1]/div[2]/div[1]/div[1]/div/div/div') #空白地方
    
    # 找出表單的HTML元素
    
    #searchEle =driver.find_elements(By().XPATH ,'//input[contains(@id,"destination")]')
    
    
    #searchEle =driver.find_elements(By.XPATH ,'//input[contains(@class,"uitk-field-input") and  contains(@class,"is-hidden")]')
    #searchEle =driver.find_elements(By.XPATH ,'//input[contains(@class,"uitk-field-input") and  contains(@class,"typeahead-custom-text-input")]' )
    searchEle =driver.find_elements(By.XPATH ,'//input[contains(@id,"destination_form_field-input")]')
    
    
    #//*[@id="lodging_search_form"]/div/div/div[1]/div/div/div/div[1]/div
        #EGDSSearchFormLocationField-Location-destination_form_field
  #  print("searchEle",searchEle)
    
    
    #checkInEle = driver.find_elements(By().XPATH ,'//input[contains(@class,"check-in")]')
    
   # print("checkInEle", checkInEle)
    
   # checkOutEle = driver.find_elements(By().XPATH ,'//input[contains(@class,"check-out")]')
    
    
       #能夠找到 3個HTML元素,找到就建立動作鏈,關閉JS彈出框(有時會有廣告) ,然後送出各輸入欄位的搜尋條件和日期
    ##if searchEle and checkInEle and checkOutEle:    
    if searchEle :    
        actions = ActionChains(driver)     # 關閉彈出框
        '''
        actions.send_keys(Keys.TAB)
        actions.send_keys(Keys.TAB)
        actions.send_keys(Keys.TAB)
        actions.send_keys(Keys.TAB)
        ''' 
        actions.send_keys(Keys.ENTER)
        actions.perform() #執行儲存動作
        print("searchKey:",searchKey)      
        searchEle[0].send_keys(searchKey)  # 輸入搜尋條件
        searchEle[0].send_keys(Keys.TAB)        
        '''
        checkInEle[0].clear()
        checkInEle[0].send_keys(checkInDate)
        checkOutEle[0].clear()
        checkOutEle[0].send_keys(checkOutDate)
'''
        #checkOutEle[0].send_keys(Keys.ENTER)  # 送出搜尋        
        search_button.click()
        
        time.sleep(15)
        
        '''
        menu = driver.find_elements_by_xpath('//*[@id="sort-filter-dropdown-sort"][2]') #已改為Options
        
        if menu:
            actions = ActionChains(driver)    # 選排序選單
            actions.move_to_element(menu[0])
            actions.perform()
            # 找出價格從低至商排序
            price=driver.find_elements_by_xpath('//*[@id="sort-submenu-price"]/li[2]/a')
            if price:
                price[0].click()
                time.sleep(10)
                return True  
         '''   
    return False
oxiaoyueo iT邦新手 5 級 ‧ 2024-12-03 15:45:58 檢舉

你好
先給你個建議,貼程式碼時,請盡量貼需要的程式碼,用不到的程式碼請刪除,這樣方便其他人看你的程式碼
https://ithelp.ithome.com.tw/upload/images/20241203/20109337nQpoO6EHMy.png
分析你的程式碼之後,發現你的操作是要直接填入格子中,但hotels網站並不是這樣搜尋的,就算成功填入這個格子,搜尋結果並非是你想要的結果。該網站查詢地點時會呼叫一個API進行ID查詢的動作:API查詢高雄結果
https://ithelp.ithome.com.tw/upload/images/20241203/20109337vhg7bvQi0o.png
你可以看到圖中高雄的ID是1808,標記為CITY。如果是圖中的高雄萬豪酒店,標記為HOTEL,ID為66588735。當使用者點擊任一選項後,網站會自動帶入ID數值到隱藏輸入。
所以你有兩種方式可以用,一種是用程式操作的方式,對"想要去哪裡?"點擊後,追蹤目的地輸入框並輸入文字,接著追蹤選項並點擊,網站的JS會自動帶入ID及文字等。
另一種方式是需要接API的方式查詢,解析JSON的內容後,因為selenium不能修改隱藏物件的value,因此你必須使用JS修改隱藏數值
以下是小範例:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 初始化WebDriver
driver = webdriver.Edge()
driver.get('https://tw.hotels.com/')

# 先隨意選一個選項
target_div = driver.find_element(By.XPATH, "//div[contains(@class, 'uitk-layout-grid-has-auto-rows')][1]")
target_div.click()

# 等待選項出現
time.sleep(2)

select_one = driver.find_element(By.XPATH, "//ul/div[1]")
select_one.click()

# 修改數值區域
''' 
查詢API的程式碼...
'''
location_id = "1808" # 高雄的地區代碼
location_input = driver.find_element(By.XPATH, "//input[@name='EGDSSearchFormLocationField-RegionId-destination_form_field']") # 找尋隱藏RegionId 的 input
driver.execute_script("arguments[0].value = arguments[1];", location_input, location_id) # 使用 JS 修改該值
print("新的 value 值:", location_input.get_attribute("value")) # 確認值是否被修改

# 修改入住日期的值
check_in_date = '2025-01-12'
check_in = driver.find_element(By.XPATH, "//input[@name='EGDSDateRangePicker-StartDate-date_form_field']")
driver.execute_script("arguments[0].value = arguments[1];", check_in, check_in_date)
print("新的 value 值:", check_in.get_attribute("value"))

# 修改退房日期
check_out_date = '2025-01-14'
check_out = driver.find_element(By.XPATH, "//input[@name='EGDSDateRangePicker-EndDate-date_form_field']")
driver.execute_script("arguments[0].value = arguments[1];", check_out, check_out_date)
print("新的 value 值:", check_out.get_attribute("value"))

# 按下搜尋
search_button = driver.find_element(By.ID, "search_button")
search_button.click()

# 等待頁面跳轉
time.sleep(5)

# 關閉瀏覽器
driver.quit()
0
itren789
iT邦新手 5 級 ‧ 2024-12-03 11:48:46

您好!根據您提到的問題,您的 Selenium 自動化腳本遇到了一些挑戰,這裡我會幫您逐一解決,並提供一些具體的解決方案。

1. 處理彈跳視窗
網站上常常會彈出提示框或 cookie 同意視窗,這些視窗會影響您的操作。您可以透過以下方法來關閉這些視窗:

方法一:直接關閉彈出視窗 您可以嘗試定位彈出視窗的“關閉”按鈕,並進行點擊操作。首先要找到該視窗的定位方式,這可以使用瀏覽器開發者工具來獲取該元素的 XPath 或 CSS Selector。

假設彈出視窗有一個 close-button 類名,您可以這樣寫:

python
Copy code
# 等待彈出視窗並關閉
time.sleep(2)  # 等待幾秒鐘確保視窗已經顯示
close_button = driver.find_element(By.CLASS_NAME, 'close-button')
close_button.click()

如果視窗的關閉按鈕是由其他 ID 或 XPath 定位,請根據實際情況修改。

方法二:使用 WebDriverWait 等待 另外,為了避免 sleep 造成的延遲,您可以使用顯式等待來確保元素在可點擊時被操作。

python
Copy code
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

# 等待彈出視窗出現並點擊關閉
WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CLASS_NAME, "close-button"))
).click()

2. 填寫表單字段
在您的範例中,您需要填寫搜尋框、入住日期和退房日期。

搜索框 (qf-0q-destination): 您的代碼已經能夠正確地填寫搜索框,這部分不需要更改。

python
Copy code
search_box = driver.find_element(By.ID, 'qf-0q-destination')
search_box.send_keys('台北市')
search_box.send_keys(Keys.RETURN)

入住和退房日期 (qf-0q-localised-check-in, qf-0q-localised-check-out): 根據您的描述,您需要進行日期的填入,這部分的代碼基本正確,您只需要確保日期格式與網站要求的格式匹配。以下是填入日期的代碼:

python
Copy code
check_in = driver.find_element(By.ID, 'qf-0q-localised-check-in')
check_in.clear()
check_in.send_keys('2024-12-12')

check_out = driver.find_element(By.ID, 'qf-0q-localised-check-out')
check_out.clear()
check_out.send_keys('2024-12-14')
check_out.send_keys(Keys.RETURN)

如果填寫日期仍然不成功,您可以檢查日期欄位是否會觸發日曆選擇器,並通過直接選擇日期來填寫。

3. 選擇下拉選單中的選項
如果您需要選擇下拉選單中的項目,可以使用 Select 類來操作。

假設您的下拉選單是基於 ID 或 name 屬性識別,您可以這樣操作:

python
Copy code
from selenium.webdriver.support.ui import Select

# 假設有一個下拉選單 `room-selection`
select_element = driver.find_element(By.ID, 'room-selection')
select = Select(select_element)

# 選擇一個選項,可以通過索引、選項文字或值來選擇
select.select_by_visible_text('2 位成人')
# 或者
select.select_by_value('2')
# 或者
select.select_by_index(1)  # 根據選項的順序選擇

4. 等待網頁加載
在進行交互之前,最好等到網頁完全加載。使用顯式等待可以幫助您避免因為網頁尚未加載完成而造成的錯誤。

python
Copy code
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 等待搜索結果加載
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CLASS_NAME, 'hotel'))
)

5. 抓取資料
當網頁加載完成後,您可以繼續抓取酒店信息。假設您希望抓取酒店名稱和價格:

python
Copy code
hotels = driver.find_elements(By.CLASS_NAME, 'hotel')
for hotel in hotels:
    try:
        name = hotel.find_element(By.CLASS_NAME, 'hotel-name').text
        price = hotel.find_element(By.CLASS_NAME, 'price').text
        print(f'Hotel: {name}, Price: {price}')
    except Exception as e:
        print("Error:", e)

完整範例
綜合上述,以下是完整的範例代碼:

python
Copy code
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select
import time

# 初始化WebDriver
driver = webdriver.Chrome()  # 请确保chromedriver路径正确
driver.get('https://tw.hotels.com/')

# 等待彈出視窗並關閉
WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CLASS_NAME, 'close-button'))
).click()

# 输入搜索条件
search_box = driver.find_element(By.ID, 'qf-0q-destination')
search_box.send_keys('台北市')
search_box.send_keys(Keys.RETURN)

# 等待網頁加載完成
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.ID, 'qf-0q-localised-check-in'))
)

# 选择入住和退房日期
check_in = driver.find_element(By.ID, 'qf-0q-localised-check-in')
check_in.clear()
check_in.send_keys('2024-12-12')

check_out = driver.find_element(By.ID, 'qf-0q-localised-check-out')
check_out.clear()
check_out.send_keys('2024-12-14')
check_out.send_keys(Keys.RETURN)

# 等待頁面加載酒店信息
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CLASS_NAME, 'hotel'))
)

# 抓取酒店信息
hotels = driver.find_elements(By.CLASS_NAME, 'hotel')
for hotel in hotels:
    try:
        name = hotel.find_element(By.CLASS_NAME, 'hotel-name').text
        price = hotel.find_element(By.CLASS_NAME, 'price').text
        print(f'Hotel: {name}, Price: {price}')
    except Exception as e:
        print("Error:", e)

# 关闭浏览器
driver.quit()

這樣您應該可以成功處理彈出視窗、填寫表單欄位和選擇下拉選單。希望這些建議能幫助您順利完成自動化任務!

更多推荐阅读:
https://momoproxy.com/blog/Python?query=category

我要發表回答

立即登入回答