Featured image of post Selenium學習筆記-以104人力銀行為例

Selenium學習筆記-以104人力銀行為例

「因為我真的很懶,能坐我就不會站,能躺就不會坐」-By Me

前陣子被公司抓去當了QA,不得不說QA真的很無聊,就是一直在按一些button,大概就是一些重複性高,又沒什麼產值的工作,剛好想到之前在前前公司有聽過Selenium這個東西,於是來研究一下。

bored-and-gif

介紹

可以先看一下底下的gif,就可以更了解selenium的作用

iShot_2023-08-02_14.40.51

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import time

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

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.google.com/')

search = driver.find_element(By.NAME, 'q')
search.send_keys('武嶺')
search.send_keys(Keys.ENTER)

time.sleep(100)

這邊這段程式,就是指

image-20230803174957281

然後Chrome就會自己動起來了。有時候我們可能需要某些網站的資訊,但它本身不是走前後端分離,資訊都是透過session、cookie來傳遞,而不是json,像這種情況,如果對速度沒有特別的要求,就可以使用Selenium來處理。接著我們就以104網頁的爬蟲為範例,示範看看Selenium使用

安裝

老樣子,使用Anaconda

  1. 創建conda環境
1
conda create --name selenium python=3.9 -y
  1. 進入環境
1
conda activate selenium
  1. 安裝所需要的pip
1
pip install selenium
  1. 查看pip是否安裝成功
1
pip list

image-20230803152113458

開頭

程式碼放在這:https://github.com/Hoxton019030/Selenium

先寫一段Demo用的Code

1
2
3
4
5
6
7
8
from selenium import webdriver

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.google.com/')
driver.close()
print("成功")

效果:

demo

讓我們現在把頁面改成104的頁面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from selenium import webdriver

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.104.com.tw/jobs/search/?keyword=Java&order=1&jobsource=2018indexpoc&ro=0')
driver.close()

print("成功")

接下來這樣就可以開啟104的頁面了,那接下來我們就是要透過driver這個物件,來找到我們目前頁面的一些資訊,我們可以使用

1
driver.page_source

來查看頁面的一些資訊

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from selenium import webdriver

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.104.com.tw/jobs/search/?keyword=Java&order=1&jobsource=2018indexpoc&ro=0')
print(driver.page_source)
driver.close()

print("成功")

這個driver.page_source所包含的其實就是整個網頁的DOM,既然有html的東西,那接下來就要介紹各種不同的選擇器,來取得想要的資訊

選擇器

Selenium提供了許多不同的選擇器,讓我們來定位我們的html內容,

image-20230803143636714

接下來會講幾個比較常用的,其實也就是css_selector跟class name,

Class選擇器

我們現在想要找到每份職缺的title

image-20230803143007904

查看DOM之後我們發現,它的html class 為 js-job-link

image-20230803143101505

那我們就可以這樣寫

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.104.com.tw/jobs/search/?keyword=Java&order=1&jobsource=2018indexpoc&ro=0')

titles = driver.find_elements(By.CLASS_NAME, "js-job-link") 
for title in titles:
    print(title.text)
driver.close()

效果:

demo

CSS 選擇器

這是Selenium官方最為推薦的選擇方式,最大的優勢就在於它可以使用css選擇器來定位元素,比如說nth-child、first-child之類的,所以它可以找到幾乎所有的元素,比如說我想要找到工作的描述

image-20230803144104901

它的css選擇器就是這樣讀

「想要找到tag為p,並且有job-list-item__info, b-clearfix, b-content 這些class名稱的地方」

那python就是這樣寫

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.104.com.tw/jobs/search/?keyword=Java&order=1&jobsource=2018indexpoc&ro=0')

titles = driver.find_elements(By.CLASS_NAME, "js-job-link")
jobInfos = driver.find_elements(By.CSS_SELECTOR, "p.job-list-item__info.b-clearfix.b-content")
for element in jobInfos:
    print(element.text)
driver.close()

其實觀察一下可以發現,Google的開發人員工具已經幫我們把這個地方的css selector標出來了

image-20230803144508527

我們可以直接複製貼上就好

剛剛有提到說可以使用css selector來做更仔細的定位,那麼該怎麼用呢?以104中的工作要求為例

image-20230803144756998

我們注意到,它其實是一個ul的結構,想要挑出「工作經驗」這一個區塊的東西,想要用class選擇器來定位基本上不可能,這時候就要使用css selector來達到我們想要的效果,而這邊的寫法就是這樣

1
experiences = driver.find_elements(By.CSS_SELECTOR,"ul.b-list-inline.b-clearfix.job-list-intro.b-content li:nth-child(3)")

來通過選擇器找到li的第三個child,也就是工作經歷

demo

ID 選擇器

id選擇器就是id選擇,使用方式就如同大家想的那樣,通常是用來定位一些unique的元素

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.104.com.tw/jobs/search/?keyword=Java&order=1&jobsource=2018indexpoc&ro=0')

titles = driver.find_elements(By.CLASS_NAME, "js-job-link")
jobInfos = driver.find_elements(By.CSS_SELECTOR, "p.job-list-item__info.b-clearfix.b-content")
experiences = driver.find_elements(By.CSS_SELECTOR,
                                   "ul.b-list-inline.b-clearfix.job-list-intro.b-content li:nth-child(3)")
mainContent = driver.find_element(By.ID, "main-content")
print(mainContent.text)
driver.close()

透過element來找到裡面的attribute

現在想要把每個工作的超連結取出來,要怎麼做呢?

可以先透過選擇器選出想要的tag,變成一個webElement物件(相當於底下的hrefs),然後再透過get_attribute來取得標籤內的值,我們就可以透過這樣的方式來取出想要的資料了

image-20230803150217709

1
2
3
4
5
hrefs = driver.find_elements(By.CLASS_NAME, "js-job-link")

for href in hrefs:
    link = href.get_attribute('href')
    print(link)

demo

點擊的觸發

當我們取得這個頁面的資料完後,想要接著點擊下一頁,那我們該怎麼做呢?

首先先透過選擇器,來選到下一頁的button

image-20230803150842760

1
link = driver.find_element(By.CLASS_NAME, "js-next-page")

接著可以透過link.click()來觸發點擊事件,切換到下一頁

demo

疑!!幹幹幹幹幹,怎麼出錯了

breaking-bad-walter-white-gif

看了一下console區,它的回應是這樣

image-20230803151400266

「網頁中沒有可以互動的元素」,像這種情況多半是因為DOM元件還沒load進來,我們可以透過一些方式來延緩它加載的時間,最爛的做法其實就是用time.sleep()的方式來,但因為我現在只會用這個方式(✿◡‿◡)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from selenium import webdriver
from selenium.webdriver.common.by import By

options = webdriver.ChromeOptions()
driver = webdriver.Chrome()

driver.get('https://www.104.com.tw/jobs/search/?keyword=Java&order=1&jobsource=2018indexpoc&ro=0')

for i in range(10):
    time.sleep(2)
    link = driver.find_element(By.CLASS_NAME, "js-next-page")
    link.click()

demo

實際的程式碼

最後我們把上面的東西整合一下,並且把資料存到excel裡面,就可以達到我們想要的效果嚕

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import time

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

from JobDetail import JobDetail

options = webdriver.ChromeOptions()
options.add_argument('--ignore-certificate-errors')
options.add_argument("--test-type")
options.binary_location = "/usr/bin/chromium"
driver = webdriver.Chrome()
url = "https://www.104.com.tw/jobs/search/?https://www.104.com.tw/jobs/search/?ro=0&kwop=7&keyword=Java&expansionType=area%2Cspec%2Ccom%2Cjob%2Cwf%2Cwktm&area=6001001001%2C6001001007%2C6001002000%2C6001001003%2C6001001006%2C6001001002%2C6001001012%2C6001001004%2C6001001005&order=15&asc=0&page=1&mode=s&jobsource=2018indexpoc&langFlag=0&langStatus=0&recommendJob=1&hotJob=1"

driver.get(url)
driver.implicitly_wait(10)
workbook = openpyxl.Workbook()
sheet = workbook.active
headers = ["職務名稱", "公司名稱", "相關描述", "工作簡述", "公司地址", "工作經驗要求", "網址連結"]
sheet.append(headers)

for i in range(113):  # 頁面的interator
    try:

        titles = driver.find_elements(By.CLASS_NAME, "js-job-link")
        companyNames = driver.find_elements(By.CSS_SELECTOR, "ul.b-list-inline.b-clearfix a")
        hrefs = driver.find_elements(By.CLASS_NAME, "js-job-link")
        # 這邊先註解掉,不知道怎麼把薪資抓出來
        # tags = driver.find_elements(By.CLASS_NAME, "b-tag--default")
        # stringTag = ""
        # for tag in tags:
        #     stringTag += tag.text
        # jobInfos = driver.find_elements(By.CLASS_NAME, "job-list-item__info")
        jobInfos = driver.find_elements(By.CSS_SELECTOR, "p.job-list-item__info.b-clearfix.b-content")
        # for jobInfo in jobInfos:
        #     print(jobInfo.text)
        addresses = driver.find_elements(By.CSS_SELECTOR,
                                         "ul.b-list-inline.b-clearfix.job-list-intro.b-content li:first-child")
        experiences = driver.find_elements(By.CSS_SELECTOR,
                                           "ul.b-list-inline.b-clearfix.job-list-intro.b-content li:nth-child(3)")

        job_details_list = []
        for j in range(len(titles)):
            name = ""
            title = titles[j].text
            if "/" in title:
                break
            companyName = companyNames[j].text
            href = hrefs[j].get_attribute('href')
            jobInfo = jobInfos[j].text
            address = addresses[j].text
            experience = experiences[j].text
            job_detail = JobDetail(title, companyName, href, name, jobInfo, address, experience)
            job_details_list.append(job_detail)

        for job in job_details_list:
            rowData = [job.title, job.companyName, job.tags, job.jobInfo, job.address, job.experience, job.href]
            sheet.append(rowData)

        link = driver.find_element(By.CLASS_NAME, "js-next-page")
        link.click()
        time.sleep(2)
        print("目前處理到第", i, "頁")
        workbook.save("test.xlsx")
        print("儲存")

    except Exception as e:

        print(e)
        time.sleep(1)
        continue

print("儲存")
workbook.close()

# for some in hrefs:
#     print(some.get_dom_attribute('href'))
# print(titles)
# time.sleep(10)
# a = JobDetail()
# for title in titles:
#     print(title.text)
# for companyName in companyNameList:
#     print(companyName.text)
# for tag in tags:
#     print(tag.text)

# print(driver.page_source)
# 取得上一頁的文章標題


# time.sleep(10)
# driver.close()

image-20230803152251360

這樣就可以囉!

Licensed under CC BY-NC-SA 4.0