python 爬蟲程式 -1

做一個抓取 104人力銀行 新鮮人找工作版 所有工作及公司資料的程式

首先要抓搜尋結果頁面的資訊


import requests
from bs4 import BeautifulSoup as bs
search_url = "https://www.104.com.tw/area/freshman/search?keyword=%E6%A9%9F%E5%99%A8%E4%BA%BA&area=6001001000,6001002000&jobcategory=2007000000&industry=&page=1&sortField=APPEAR_DATE&sortMode=DESC"
head = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
        'Accept-Language':'zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4'} 
res = requests.get(search_url, headers = head)
print(res.encoding)
res.encoding = 'utf8'
soup = bs(res.text)

Out[1]:ISO-8859-1
用request傳回網頁內容
res.encoding可得知網頁編碼方式,一開始我根據網站上寫utf-8編碼,跟python設定編碼一致,想說沒問題,之後才找到如何知道是用甚麼編碼的方法,print出來才知道被python判斷成ISO-8859-1,那就自行設定成utf-8
BeautifulSoup 是一個能解構及分析的一套很好用的工具,將res轉成文字"res.text",丟給 BeautifulSoup

抓取一個工作資料


用select擷取出我們要的資訊在哪個區域

job_box = soup.select(".job_box")[0]
job_box.select('.joblist_cont')[0]

job_name = joblist.select('a')[0]['title']    #job name
com_name = joblist.select('a')[1]['title']    #company name
edu = joblist.select(".edu")[0].text.replace('\t','').replace('\r\n','')   #學歷
area = joblist.select(".area")[0].text.replace('\t','').replace('\r\n','') #工作地點在哪個市區
job_url = "https://www.104.com.tw" + joblist.select('a')[0]['href']        #職缺網頁
com_url = "https://www.104.com.tw" + joblist.select('a')[1]['href']        #公司簡介網頁

開啟職缺網頁及公司簡介網頁並取得資料
def load_web(url):
    web_res = requests.get(url)
    web_res.encoding = 'utf8'
    #print(job_web_res.text)
    time.sleep(random.uniform(0,2))
    web_bs = bs(web_res.text)
    return web_bs

job_web_bs = load_web(job_url)
com_web_bs = load_web(com_url)
   
# number of people apply for a job
num = num_of_people(job_web_bs)       
#job information
job_info = load_job_info(job_web_bs)
# company information
com_info = load_com_info(com_web_bs)
用hash sort的方式儲存我們要的資料
job_info = {}   #先建一個空間
title1 = '工作內容'
title2 = '工作說明'
explanation = 'xxxxxxx'
if title1 not in job_info.keys():           #當不存在'工作內容'這個索引值,建立'工作內容'這個索引值
    job_info[title1] = {}
if title2 not in job_info[title1].keys():   #當不存在'工作說明'這個索引值,建立'工作說明'這個索引值
    job_info[title1][title2] = {}
job_info[title1][title2] = explanation      #將'xxxxxxx'放在 job_info[工作內容][工作說明]裡

舉例:職缺網頁
# job info
def load_job_info(job_web_bs):
    job_info = {}
    main_info = job_web_bs.select("section")[0]
    title1 = main_info.select('h2')[0].text      #工作內容
    title2 = '工作說明'
    explanation = main_info.select('p')[0].text.replace('\r', ' ')
    
    if title1 not in job_info.keys():
        job_info[title1] = {}
    if title2 not in job_info[title1].keys():
        job_info[title1][title2] = {}
    job_info[title1][title2] = explanation
            
    for i in range(len(main_info.select('dt'))):
        title2 = main_info.select('dt')[i].text.replace('\r','').replace('\t','').replace(':','')
        explanation = main_info.select('dd')[i].text.replace(' ','').replace('\n','')                       
        if title2 not in job_info[title1].keys():
            job_info[title1][title2] = {}
        job_info[title1][title2] = explanation
               
    # job condition
    condition = job_web_bs.select("section")[1]
    title1 = condition.select('h2')[0].text      #工作條件
    if title1 not in job_info.keys():
        job_info[title1] = {}
        
    for i in range(len(condition.select('dt'))):
        title2 = condition.select('dt')[i].text.replace(':','')
        explanation = condition.select('dd')[i].text.replace(' ','')  
        if title2 not in job_info[title1].keys():
            job_info[title1][title2] = {}
        job_info[title1][title2] = explanation
        
    # work welfare
    welfare = job_web_bs.select("section")[2]
    title1 = welfare.select('h2')[0].text        #公司福利
    explanation = welfare.select('p')[0].text.replace('\r\u3000','\t').replace('\r','')
    if title1 not in job_info.keys():
        job_info[title1] = {}
    job_info[title1] = explanation
    return job_info




寫檔案的格式問題:

上網查excel不是用utf-8編碼,python寫檔時也有錯誤訊息,寫說'cp950' codec can't encode character '\u30fb' in position 0: illegal multibyte sequence,所以就全部經由cp950編碼一次測試,如果有字元不能被編碼那就以空白字元代替
要先將string轉為list,在對特定字元做設定,之後再轉回string格式

wl = '"工作內容","工作說明","xxxx"'
for j in range(len(wl)):
    try:
        wl[j].encode('cp950').decode('cp950')
    except:
        tmp = list(wl)
        tmp[j] = " "
        wl = ''.join(tmp)


完整程式碼:

import requests
from bs4 import BeautifulSoup as bs
import time
import pandas as pd
import chardet
import random

# get title company there's link
def load_web(url):
    web_res = requests.get(url)
    web_res.encoding = 'utf8'
    #print(job_web_res.text)
    time.sleep(random.uniform(0,2))
    web_bs = bs(web_res.text)
    return web_bs

def writeline(array):
    wl = '"' + array[0] + '"'
    for i in range(1, len(array)):
        wl = wl + ',"' + array[i] + '"'
    return wl

def get_all_page(beautifulsoup_text):
    page = beautifulsoup_text.select('.next_page')[0]
    tt = page.text.replace('\t','').split('\n')
    num = tt[1].find('共')
    allpage = int(tt[1][num+2])
    return allpage

class job_class(object):
    
    def __init__(self, joblist):
        self.job_name = joblist.select('a')[0]['title']    #job name
        self.com_name = joblist.select('a')[1]['title']    #company name
        self.edu = joblist.select(".edu")[0].text.replace('\t','').replace('\r\n','')
        self.area = joblist.select(".area")[0].text.replace('\t','').replace('\r\n','')
        self.job_url = "https://www.104.com.tw" + joblist.select('a')[0]['href']
        self.com_url = "https://www.104.com.tw" + joblist.select('a')[1]['href']
        
    # job info
    def load_job_info(job_web_bs):
        job_info = {}
        main_info = job_web_bs.select("section")[0]
        title1 = main_info.select('h2')[0].text      #工作內容
        title2 = '工作說明'
        explanation = main_info.select('p')[0].text.replace('\r', ' ')
        
        if title1 not in job_info.keys():
            job_info[title1] = {}
        if title2 not in job_info[title1].keys():
            job_info[title1][title2] = {}
        job_info[title1][title2] = explanation
                
        for i in range(len(main_info.select('dt'))):
            title2 = main_info.select('dt')[i].text.replace('\r','').replace('\t','').replace(':','')
            explanation = main_info.select('dd')[i].text.replace(' ','').replace('\n','')                       
            if title2 not in job_info[title1].keys():
                job_info[title1][title2] = {}
            job_info[title1][title2] = explanation
                   
        # job condition
        condition = job_web_bs.select("section")[1]
        title1 = condition.select('h2')[0].text      #工作條件
        if title1 not in job_info.keys():
            job_info[title1] = {}
            
        for i in range(len(condition.select('dt'))):
            title2 = condition.select('dt')[i].text.replace(':','')
            explanation = condition.select('dd')[i].text.replace(' ','')  
            if title2 not in job_info[title1].keys():
                job_info[title1][title2] = {}
            job_info[title1][title2] = explanation
            
        # work welfare
        welfare = job_web_bs.select("section")[2]
        title1 = welfare.select('h2')[0].text        #公司福利
        explanation = welfare.select('p')[0].text.replace('\r\u3000','\t').replace('\r','')
        if title1 not in job_info.keys():
            job_info[title1] = {}
        job_info[title1] = explanation
        return job_info
    
    # company info
    def load_com_info(com_web_bs):
        com_info = {}
        com_info_bs = com_web_bs.select(".intro")[0]
        title1 = com_info_bs.select('h2')[0].text       #公司介紹
        
        if title1 not in com_info.keys():
            com_info[title1] = {}
        for i in range(len(com_info_bs.select('dt'))):
            title2 = com_info_bs.select('dt')[i].text.replace('\u3000','').replace(':','')
            explanation = com_info_bs.select('dd')[i].text.replace('\r','').replace('\n','')
            if title2 not in com_info[title1].keys():
                com_info[title1][title2] = {}
            com_info[title1][title2] = explanation
            
        com_info_bs = com_web_bs.select(".intro")[1]   
        title1 = com_info_bs.select('h2')[0].text        #公司簡介
        explanation = com_info_bs.select('p')[0].text.replace('\r','\n')
        if title1 not in com_info.keys():
            com_info[title1] = {}
        com_info[title1] = explanation
        
        title1 = com_info_bs.select('h2')[1].text #主要商品/服務項目
        explanation = com_info_bs.select('p')[1].text.replace('\r','')
        if title1 not in com_info.keys():
            com_info[title1] = {}
        com_info[title1] = explanation
        return com_info
    
    # number of people apply for a job
    def num_of_people(job_web_bs):
        number_of_people_bs = job_web_bs.select('.sub')[0]
        number_of_people = number_of_people_bs.select('a')[0].text
        return number_of_people
        

def load_104_newpeople_main(search_url, filename = './test.csv'):
    print('write to file ', filename)

    
    job_list = [[   ''    , '工作內容', '條件要求', '公司福利'],
                ['工作說明', '職務類別', '工作待遇', '工作性質', '上班地點', '管理責任', '出差外派', '上班時段', '休假制度', '可上班日', '需求人數'], 
                ['接受身份', '工作經歷', '學歷要求', '科系要求', '語文條件', '擅長工具', '工作技能', '具備駕照', '其他條件']]
    com_list = [[   ''    , '公司介紹', '公司簡介', '主要商品/服務項目'],
                ['產業類別', '產業描述', '員工', '資\xa0本\xa0額', '聯\xa0絡\xa0人', '公司地址', '電話', '傳真', '公司網址']]
    all_headlist = ['職務名稱','公司名稱','學歷', '地區', '應徵人數',
                '工作說明', '職務類別', '工作待遇', '工作性質', '上班地點', '管理責任', '出差外派', '上班時段', '休假制度', '可上班日', '需求人數', '接受身份', '工作經歷', '學歷要求', '科系要求', '語文條件', '擅長工具', '工作技能', '具備駕照', '其他條件', '公司福利',
                '產業類別', '產業描述', '員工', '資本額', '聯絡人', '公司地址', '電話', '傳真', '公司網址', '公司簡介', '主要商品/服務項目']
    
    head = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
            'Accept-Language':'zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4'}
    
    res = requests.get(search_url, headers = head)
    #print(res.encoding)
    res.encoding = 'utf8'
    soup = bs(res.text)
    
    wl = writeline(all_headlist)  
                          
    fw = open(filename, 'w')
    fw.writelines(wl + '\n')
    
    allpage = get_all_page(soup)+1
    
    for page in range(1, allpage):
        
        if page == 1:
            print('page:',page)
            print(search_url)
            
        else:
            print('page:', page)
            #url = 'https://www.104.com.tw/area/freshman/search?area=6001001000%2C6001002000&jobcategory=2007000000&industry=&keyword=%E6%A9%9F%E5%99%A8%E4%BA%BA&page=2&sortField=APPEAR_DATE&sortMode=DESC'
            findpage = 'page='
            local = search_url.find(findpage)
            tmp = list(search_url)
            tmp[local + len(findpage)] = str(page)
            search_url = ''.join(tmp)
    
            res = requests.get(search_url, headers = head)
            #print(res.encoding)
            res.encoding = 'utf8'
            soup = bs(res.text)
            print(search_url)
            
        job_box = soup.select(".job_box")[0]
        
        #job_url = ''
        #com_url = ''
        for joblis in job_box.select('.joblist_cont'):
            
            job = job_class(joblis)
            print(job.job_name)            
        
        #        print("job_url: ", job_url)
        #        print("com url: ", com_url)
            global job_info
            global com_info
            job_web_bs = load_web(job.job_url)
            com_web_bs = load_web(job.com_url)
            
            # number of people apply for a job
            num = job_class.num_of_people(job_web_bs)       
            #job info
            job_info = job_class.load_job_info(job_web_bs)
            # company
            com_info = job_class.load_com_info(com_web_bs)
             
            once_data = []
            once_data.append(job.job_name)
            once_data.append(job.com_name)
            once_data.append(job.edu)
            once_data.append(job.area)
            once_data.append(num)
            # job
            for j in range(len(job_list[1])):
                try:
                    once_data.append(job_info[job_list[0][1]][job_list[1][j]])
                except:
                    once_data.append('')       
            for j in range(len(job_list[2])):
                try:
                    once_data.append(job_info[job_list[0][2]][job_list[2][j]])
                except:
                    once_data.append('')   
            once_data.append(job_info[job_list[0][3]])    
            
            # company
            for j in range(len(com_list[1])):
                try:
                    once_data.append(com_info[com_list[0][1]][com_list[1][j]])
                except:
                    once_data.append('')       
            once_data.append(com_info[com_list[0][2]])
            once_data.append(com_info[com_list[0][3]])
        
            # do wl
            wl = writeline(once_data)
            for j in range(len(wl)):
                try:
                    wl[j].encode('cp950').decode('cp950')
                except:
                    tmp = list(wl)
                    tmp[j] = " "
                    wl = ''.join(tmp)
            #write to file
            fw.writelines(wl + '\n')
            break
            
    fw.close()
    print('output file succesful')
    return 'succesful'

if __name__ == '__main__':
    filename = './test.csv'
    search_url = "https://www.104.com.tw/area/freshman/search?keyword=%E6%A9%9F%E5%99%A8%E4%BA%BA&area=6001001000,6001002000&jobcategory=2007000000&industry=&page=1&sortField=APPEAR_DATE&sortMode=DESC"

    load_104_newpeople_main(search_url, filename)    


留言

  1. 太厲害了!最近才想用python寫一個可以固定擷取特定公司每日是否有釋放新職缺,並就工作性質分類。看到你的BLOG發現都寫好了XD

    回覆刪除
    回覆
    1. 台北班 Python + Excel VBA 金融資訊爬蟲課程 https://docs.google.com/forms/d/e/1FAIpQLSdL6NzcFFoitBT6zfkLWpOPghHjwOB9pXBWp5SJgduFM0EPDg/viewform

      刪除
  2. 很有用的教學!剛好要爬104,值得參考

    回覆刪除
  3. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 不好意思想請教板大是用什麼環境去跑的
      我用 jupyter 跑上面的完整程式碼好像跑不出來🥴

      刪除
    2. 可能對方網頁格式有改版吧,這種事常有

      刪除

張貼留言

這個網誌中的熱門文章

android 定時通知(永久長期的) 本篇只講AlarmManager使用

C# 模擬鍵盤滑鼠控制電腦

python nn 聲音辨識 -1 傅立葉轉換

python pyautogui 簡介

raspberrypi 開機自動執行程式 與 在terminal開啟第二個terminal執行python