001.01 一般網頁爬蟲處理

Jason990420發表於2019-08-06
建檔日期: 2019/07/28
更新日期: 2019/07/29
語言: Python 3.7.4
系統: Win10 Ver. 10.0.17763 繁體中文版(TW)

在這裡將不使用BeatifulSoup來作網頁爬蟲, 並儘可能不使用其他的庫來完成件事, 使用別人的庫來寫程式, 好處是簡單, 快捷, 程式碼短, 而且還不一定要懂內部的細節; 但是就因為不懂細節, 就很容出錯, 而且不會真正瞭解真正的作法等等缺點.

1. 網頁爬蟲預備動作

  • 開啟網頁, 比如https://www.51job.com/, 選上方的職位搜尋

    001.01 一般網頁爬蟲處理

  • 會出現超過十萬條的工作資訊, 我們並不需要這麼多資料來供自己處理, 因此多加一點條件先行篩選, 按下面點選, 可以得到較少的資料, 比如撰文時的資料有491條職位, 共分10頁.

    001.01 一般網頁爬蟲處理

  • 檢查一下, 這十頁的網址差異, 並建立一份十個網址的utf-8格式文字檔案url.txt, 每條網址都有一些引數不同, 要自己確認. (如下圖)

    001.01 一般網頁爬蟲處理

  • 滑鼠指向表格中一開始的欄位, 右鍵單擊選看"檢查元素", 檢查HTML內容

    001.01 一般網頁爬蟲處理

  • 表格的起始位置及結束位置, 並找出表格資料所在的特別的標籤或其他可以確認的字串.

    001.01 一般網頁爬蟲處理

  • 找出表格資料所在的特別的標籤或其他可以確認的字串.

    001.01 一般網頁爬蟲處理

  • 在此, 我們以'<="el title">'為表格標題的起點, 再找到> .... <中的資料, 這就是表格的五項標題. 後面每一行的資料, 則以兩個a title, 三個span class為起點, 再找到> .... <中的資料, 這就是表格的一行中的五項資料.

  • 注意: 不同的系統, 瀏覽器所收到的HTML檔會有些差異, 比如我的前兩筆資料就不是以a title為起點, 而是以a target為起點 其他部份則相同.

  • 因此找出這網頁的資料步驟為

    (1) 取出 '<!--列表表格-->' 到 ' <!--列表表格 END-->' 的HTML
    (2) 找到 '<="el title">', 取出'>'和'<'之間表格的五項標題
    (3) 讀取每一筆資料
        找到 'a target', 取出'>'和'<'之間表格的資料, 重複兩次
        找到 'span class', 取出'>'和'<'之間表格的資料, 重複三次
    (4) 重複3中的動作, 讀取以下每一筆資料
    (5) 找不到起點的字串, 表示該網頁資料已讀取完畢, 再換下一個網頁, 直到結束.

2. 程式範例

import urllib.request
import datetime

# 取現在的時間當作檔名
date = datetime.datetime.now()
url_list_file = "url.txt"
data_file_name = "Web Data " + str(date.year) + ("%02d" % date.month) + \
                ("%02d" % date.day) +  ("%02d" % date.hour)  + \
                ("%02d" % date.minute) +  ("%02d" % date.second)  +".csv"

# 讀取網頁及存檔用的編碼
decoder = "GB18030"

# 網頁中表格的起點及終點
table_format = ['<!--列表表格-->','<!--列表表格 END-->']
area_start = table_format[0]
area_stop  = table_format[1]

# 尋找字串, 並跳過
def find_skip(in_string, string1):
    global pointer, string_found
    pointer = in_string.find(string1, pointer)

    #確認表格結束
    if pointer < 0:
        string_found = False
    pointer += len(string1)

# 按起始字串, 讀取兩個標記中的資料, 標記通常是'>'及'<', 並重復n次
def get_n_data(in_string, string1, string2, string3, n, data_follow):
    global pointer, string_found, table

    # 重複n次
    for i in range(n):

        # 跳過起始字串
        find_skip(in_string, string1)

        # 表格結束 ?
        if string_found:

            # 找到第一個標記
            find_skip(in_string, string2)
            data_start = pointer

            # 找到第二個標記
            find_skip(in_string, string3)
            data_stop  = pointer - len(string3)

            # 資料放到列表中
            data = in_string[data_start : data_stop]
            table.append(data.strip())

# 將列表的資料, 按CSV文字檔案格式, 寫入CSV檔中
def save_table(data_file_name, table):

    # 表格是的空的, 不存檔
    if len(table) != 0:

        table_data =""
        count = 0

        # 表格所有專案轉成字串
        for item in table:
            count += 1

            # 每一行五個資料, 以','間隔, '\n'為結尾
            if count != 5:
                table_data = table_data+item+','
            else:
                table_data = table_data+item+'\n'
                count = 0

        # 建立檔案, 寫入資料字串, 檔案開閉
        datafile = open(data_file_name, 'wt', encoding=decoder)
        datafile.write(table_data)
        datafile.close()

table =[]

# 讀入網址檔案中的每一個網址
with open(url_list_file, mode='rt', encoding='utf-8-sig') as url_list:

    for url in url_list:

        # 解碼讀入網頁HTML
        try:
            web_page = urllib.request.urlopen(url.strip())
        except urllib.error.URLError:
            print('Web page loading failed !!!')
            exit()
        html = web_page.read().decode(decoder)

        # 網頁只保留表格部份處理
        pointer = 0
        data_area_start = html.find(area_start)
        data_area_stop  = html.find(area_stop)
        data_html       = html[data_area_start : data_area_stop]
        del html

        pointer = 0

        '''
        省略全部表格每一欄的標題, 避免多個頁標題和資料混在一起
        find_skip(data_html, '<div class="el title">')
        get_n_data(data_html, 'span class', '>', '<',  4, ',')
        get_n_data(data_html, 'span class', '>', '<', 1, 'n')
        '''

        # 找不到間隔字串, 表示表格結束
        string_found = True

        # 重複取得每一筆的表格資料, 找到起始字中, 再取 >......< 中的資料
        while string_found:

            # 兩次的'a target', '< ......>'的資料
            get_n_data(data_html, 'a target', '>', '<', 2, ',')

            # 表格結束 ?
            if string_found:

                # 三次的'span class', '< ......>'的資料
                get_n_data(data_html, 'span class', '>', '<', 3, '\n')

# 存表格
save_table(data_file_name, table)

# 結束
exit()
注意: 由於本地變數及全域性變數的混淆, 容易出錯, 所以我對常用的變數, 使用了Global定義. 確認了一下, 程式讀入十個網頁, 取得491筆資料, 約34K檔案大小, 結果使用了整整30秒, 速度真的很慢, 我並沒有轉成可執行檔案來執行, 我想應該是直譯程式的關係, 而且直譯器都停止響應了, 等到跑完才醒來, My God !

把得到的資料, 轉存為以","來分隔每一筆資料的.csv格式檔, 用Excel外部輸入的方式, 插到Excel中, 就可以得到以下近五百筆的資料, 至於你要再作什麼處理, 就看你自己了, 比如用EXCEL找出職位名有'經理'的部份.

---- The End ---

Jason Yang

相關文章