Posts match “ WebCrawler ” tag:

今天來分享一個可以從網路獲取資料的技術:叫「網路爬蟲」,英文稱做Web Crawler or Web Scapying,以下簡稱爬蟲,這篇文章將會分成「一、原理介紹」以及「二、程式實作」,如果有基本概念的朋友們,可以直接跳至「二、程式實作」開始,我將會以iPeen愛評網(以下簡稱iPeen)作為範例用Python來實作簡單的爬蟲,希望透過此篇入門介紹,能讓大家都會寫基本的爬蟲,抓到你想要的資料。

一、基本概念

爬蟲是一個將網路上的檔案下載下來然後做一些處理的程式,一般我們在網路上看到的網頁、圖片或影片等,都算是一個檔案,只是透過瀏覽器我們可以看到正確的結果,例如:網頁檔案本身是一個HTML檔案,瀏覽器幫我們轉成我們看得懂的網頁介面(如下圖所示,操作方式為在Chrome按右鍵的[檢視網頁原始碼])。

為何要講這個呢?
因為爬蟲所要處理的,是沒有透過瀏覽器處理的HTML原始碼 ,當我們看到網頁有我們要抓的資料之時(以iPeen為例,我們要抓店家的資訊,如下圖,操作方式為在網頁上游標移至店家名稱連結,然後按下右鍵按[檢查])

我們必須知道這些在網頁上我們所看到的店家名稱是在HTML原始碼所對應的位置(如下圖),必須看看這些這些程式碼有沒有什麼樣的規律或特徵,以剛剛所講述的店家網址名稱為例,我們可以看出店家連結是<a>標籤,擁有 data-label=”店名” 獨特可以找出店家連結的CSS屬性。

爬蟲兩大工作

  1. 下載檔案:在給定一個網址時,我們可以使用HTTP協定的方法去對於伺服器送出Request請求,並且取得Response回應,在我們爬蟲一般的情況就是對於伺服器送出GET Request來取得HTML檔案的Response,在取得HTML檔案後接著我們就可以進行第二步驟:分析內容。
  2. 分析內容:擁有HTML之後,我們就可以透過搜尋、正則、字串處理、切分、取代等的各種技巧來將HTML過濾抓到我們所要的資料,例如上述例子所提到的店家連結標籤,可以用正則找到<a>標籤,然後用搜尋過濾留下CSS包括data-label=”店名” 的標籤,然後再用正則去除標籤只留下連結和店家名稱。

二、程式實作

首先,我們先安裝必要的第三方程式套件,有了這些套件加上Python本身提供的套件可以加速我們的開發工作,這邊我們使用Python3來開發,上述爬蟲兩大工作「下載檔案」是使用Requests來實作,而「分析內容」是使用BeautifulSoup來實作,這兩個套件安裝方式可以參考官網,不再文章詳述,讓我們開始吧!

我們的目標是抓下iPeen的美食店家資訊,在我們開始寫程式之前,我們必須大致分析網頁結構,知道我們所要的資料是在哪一些頁面,爬蟲要從哪裡開始、有哪些網頁要爬、爬蟲要怎麼到那些網頁等,所以先在iPeen先找到可以抓店家列表的網頁作為爬蟲起始點,這邊我們找到「http://www.ipeen.com.tw/search/taiwan/000/1-0-0-0/」具有店家列表可以作為爬蟲起始點,從此頁面取得各店家的資訊頁面連結後,在個別進入每個店家頁面抓店家資訊。

開頭我們先寫一個方法從起始點抓到所有的店家資訊頁面連結,並且儲存到列表中,程式碼如下(完整版在 commit (6741fd)[https://github.com/enginebai/iPeenCrawler/commit/6741fde67ea829544c01712d9f8aa76625789135]):

def get_shop_link_list():
    list_req = requests.get(LIST_URL)
    if list_req.status_code == requests.codes.ok:
        soup = BeautifulSoup(list_req.content, HTML_PARSER)
        shop_links_a_tags = soup.find_all('a', attrs={'data-label': '店名'})

        shop_links = []
        for link in shop_links_a_tags:
            shop_link = ROOT_URL + link['href']
            print(shop_link)
            shop_links.append(shop_link)
            parse_shop_information(shop_link)

我們先使用 requests.get() 來下載HTML程式碼 list_req.content 並且傳入 BeautifulSoup() 作為分析來源,然後我們可以用BeautifulSoup.find_all(‘a’, attrs={‘data-label’: ‘店名’})
找到所有CSS為 ‘data-label’: ‘店名’<a> 標籤。

接著,我們針對每個店家頁面去分析出詳細資訊,程式碼如下(完整版在 commit fd6435a):

def parse_shop_information(shop_link):
    shop_id = re.sub(re.compile(r'^.*/' + SHOP_PATH), '', shop_link).split('-')[0]
    print(shop_id)

    req = requests.get(shop_link)
    if req.status_code == requests.codes.ok:
        soup = BeautifulSoup(req.content, HTML_PARSER)
        shop_header_tag = soup.find('div', id='shop-header')
        name_tag = shop_header_tag.find('span', attrs={'itemprop': 'name'})
        print(re.sub(SPACE_RE, '', name_tag.text))
        category_tag = shop_header_tag.find("p", class_={'cate i'})
        print(re.sub(SPACE_RE, '', category_tag.a.text))
        address_tag = shop_header_tag.find('a', attrs={'data-label': '上方地址'})
        print(re.sub(SPACE_RE, '', address_tag.text))

        gps_str = address_tag['href']
        # print(gps_str)

        gps_str = re.search('/c=(\d+.\d*),(\d+.\d*)/', gps_str).group().replace('/', '')
        # print(gps_str)

        lat = gps_str.split(',')[0]
        lng = gps_str.split(',')[1]
        print(lat.split('=')[1], lng)

這邊我們可以看到用 BeautifulSoup.find() 再搭配正則 re.search() / re.sub() 和字串操作 string.split() / string.replace() 就可以取得店家的名稱、分類、地址、GPS(分析取得字串方法不只一種,你可以寫的跟我不同沒關係,重點在於可以有效且正確分析出字串即可)。

大功告成,所有完整程式碼在我的GitHub上,歡迎下載使用,也多用星星來支持。

FAQ

  1. 寫爬蟲有一個問題很多人會來問我「奇怪,我的網頁程式碼有看到,為何爬蟲卻無法抓到?」,這個原因很簡單,因為你看到的是「假的」(無誤),是因為網頁內容是動態產生的(以用Javascript產生的內容為例),你所看到的是經過瀏覽器已經將Javascript執行後產生的結果,而一般爬蟲預設是沒有執行這些Javascript,所以當然抓不到,我們要如何判別呢?在Chrome當中如果是右鍵按下[檢視網頁原始碼],則你所看到的HTML是沒有動態產生內容的,也就是我們一般爬蟲預設抓到的資料;在網頁上游標移動到其中一個元素按右鍵點[檢查]所看到的HTML內容則是經過動態產生的結果,一般爬蟲沒有特別處理是無法得到這些內容的。 這問題有沒有解?答案是有的,需要用其他方式例如讓爬蟲先模擬瀏覽器取得動態產生內容後,再去取得分析HTML的方式解決。
  2. 爬蟲是否能抓到一個需要登入或驗證後的網頁資訊?答案是可以的,但前提是你要知道該網頁怎麼登入、怎麼驗證身份,接著像瀏覽器一樣用 Session / Cookie 將登入或驗證後的資料儲存一起帶入到須登入驗證的網頁中去爬。
  3. 有時候網頁會回傳 HTTP 429 Too Many Requests的錯誤,我該如何處理?會出現這錯誤表示你存取得太頻繁,每一個爬蟲都是一個Request,對於伺服器都是一個資源損耗,有些伺服器會擋甚至你存取太快會視為攻擊的一種(類DDOS),正確的解法是調整爬蟲抓網頁的速度,可以設定延遲機制來預防。

後記

附上我在Android讀書會所分享「Android x 網路爬蟲」的完整簡報內容:SlideShare

我是大白,Co-founder & Developer @DualCores Studio / Developer @17 Media],歡迎對於Bot開發、人工智慧、語意分析、機器學習有興趣的朋友多跟我一起交流學習,接下來會更專注分享相關的題目。