一、實作目標:

爬取pchome購物的所有商品資料,並進行分類

 

二、爬蟲構思:

pchome購物的商品資料並沒有辦法根據商品網址來進行遍歷

所以分析商品分類頁面來取得商品資料

由於該網站內容皆是由js產生

所以沒辦法藉由beautifulSoup4來對網頁程式碼進行分析

要從該網頁的回應的內容找出商品資料存放的js

再爬取該json的商品資料

 

三、爬蟲實作:

經過許多嘗試,最後選定先爬取商品分類、再至每個分類爬取商品資料

1.分析網頁

首先找出該網站商品目錄

pchome購物中心 最新商品

按下F12開發人員模式,切換到network並重新整理,選Preview查看是否有json內容

很快的,就可以找到包含商品資料的檔案

將Preview切換至Headers來查看回應的網址

再來判斷不同頁數的js網址會有什麼差異

可以觀察出,第二頁的商品offset從51開始,limit維持50

代表從第51項商品往後顯示50筆資料,經過測試後最多只能顯示50筆,將limit調高也不會增加

由於商品資料中並沒有"分類"的項目,所以得從左列的分類分別爬取資料

查看選擇左側分類後的js網址,會發現多了關於分類的信息,不同分類則會有不同代號

從network查找是否有關於左側分類的資料,由於整個網頁都是由js產生,所以必定會有

將Preview切換至Headers來查看回應的網址

最後會有一串13位數字,隨著每次重整頁面會有所變動

根據我的經驗判斷他應為時間戳記,經由測試後也確認此為當前的時間戳記

將最後一個不確定因素排除後

接著就可以進行爬取資料的部分了

2.初步爬取資料

使用Python requests來獲取資料,

由於每訪問幾次就會被阻擋,所以添加headers

headers為一些從pchome網站Headers的取得的信息(取得json網址的地方),以防被網站阻擋獲取資料

首先先從全部最新資料的json網址來獲取看看

import requests
import re
import json
url = 'https://ecapi.pchome.com.tw/mall/prodapi/v1/newarrival/prod&offset=1&limit=50&_callback=jsonpcb_newarrival'
user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
headers = {'User-Agent':user_agent,
          'server': 'PChome/1.0.4',
          'Referer': 'https://mall.pchome.com.tw/newarrival/'}
res = requests.get(url=url,headers=headers)#分析得出的網址
res_text = res.text
jd = json.loads(res_text)
print(jd)

運行結果:

直接從網址打開json檔查看,才發現因為json多了try、catch判斷,導致解析失敗

因此我使用replace直接把前後多餘的字串替換為空,這時再次解析即可成功

url = 'https://ecapi.pchome.com.tw/mall/prodapi/v1/newarrival/prod&offset=1&limit=50&_callback=jsonpcb_newarrival'
user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
headers = {'User-Agent':user_agent,
          'server': 'PChome/1.0.4',
          'Referer': 'https://mall.pchome.com.tw/newarrival/'}
res = requests.get(url=url,headers=headers)#分析得出的網址
res_text = res.text
res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
jd = json.loads(res_text_format)
print(jd)

接著提取出我想要的商品訊息

使用for迴圈來遍歷每組json資料

並從其中獲取商品名稱、商品圖片(需添加圖片前綴網址)、商品價格、商品網址(取出id並添加前綴網址)

#pchome
url = 'https://ecapi.pchome.com.tw/mall/prodapi/v1/newarrival/prod&offset=1&limit=50&_callback=jsonpcb_newarrival'
user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
headers = {'User-Agent':user_agent,
          'server': 'PChome/1.0.4',
          'Referer': 'https://mall.pchome.com.tw/newarrival/'}
res = requests.get(url=url,headers=headers)#分析得出的網址
res_text = res.text
res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
jd = json.loads(res_text_format)
print(jd)
pcgoods = (jd['Rows'][:])
for pcgood in pcgoods:
    pcname = pcgood['Name']
    pcimg = 'https://b.ecimg.tw' + pcgood['Pic']['S']
    pcprice = pcgood['Price']['P']
    pclink = 'https://mall.pchome.com.tw/prod/'+ pcgood['Id']
    print(pcname)
    print(pcimg)
    print(pcprice)
    print(pclink)

將資料打印出來結果如下圖

3.結合分類爬取資料

首先從python獲取時間戳記

import time
millis = int(round(time.time() * 1000))
print(millis)

接著將時間戳記加入網址,並爬取資料,同樣要使用replace將多餘的字串替換為空

#pchome分類
#分析後,網址結尾需加上13位unix時間戳記
url = 'https://ecapi.pchome.com.tw/mall/cateapi/v1/sign&tag=newarrival&fields=Id,Name,Sort,Nodes&_callback=jsonpcb_newarrival&'+str(millis)
user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
headers = {'User-Agent':user_agent,
          'server': 'PChome/1.0.4',
          'Referer': 'https://mall.pchome.com.tw/newarrival/'}
res = requests.get(url=url,headers=headers)#分析得出的網址
res_text = res.text
res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
jd = json.loads(res_text_format)
pcclass = jd[:]
#print(pc)
for pc in pcclass:
    print(pc['Name'])
    for pccl in pc['Nodes']:
        print(pccl)

這裡要做兩個處理:獲得分類名稱、取出Id值

#pchome取出分類ID
#分析後,網址結尾需加上13位unix時間戳記
url = 'https://ecapi.pchome.com.tw/mall/cateapi/v1/sign&tag=newarrival&fields=Id,Name,Sort,Nodes&_callback=jsonpcb_newarrival&'+str(millis)
user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
headers = {'User-Agent':user_agent,
          'server': 'PChome/1.0.4',
          'Referer': 'https://mall.pchome.com.tw/newarrival/'}
res = requests.get(url=url,headers=headers)#分析得出的網址
res_text = res.text
res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
jd = json.loads(res_text_format)
pcclass = jd[:]
#print(pc)
for pc in pcclass:
    pcclname = pc['Name']
    print('分類名稱:'+pc['Name'])
    for pccl in pc['Nodes']:
        print(pccl['Id'])

最後則是將原先取得商品資料的代碼寫成一個函數,並引用三個參數進入

三個參數分別為:商品分類代號、跳轉的頁數(每頁50筆資料)、分類名稱

以下為商品資料爬蟲代碼:

def pcgood(pccid,page,pcclname):
    url = 'https://ecapi.pchome.com.tw/mall/prodapi/v1/newarrival/prod&region=' + str(pccid) + '&offset='+ str(page) +'&limit=50&_callback=jsonpcb_newarrival'
    user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
    headers = {'User-Agent':user_agent,
              'server': 'PChome/1.0.4',
              'Referer': 'https://mall.pchome.com.tw/newarrival/'}
    res = requests.get(url=url,headers=headers)#分析得出的網址
    res_text = res.text
    res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
    jd = json.loads(res_text_format)
    pcgoods = (jd['Rows'][:])
    for pcgood in pcgoods:
        pcname = pcgood['Name']
        pcimg = 'https://b.ecimg.tw' + pcgood['Pic']['S']
        pcprice = pcgood['Price']['P']
        pclink = 'https://mall.pchome.com.tw/prod/'+ pcgood['Id']
        print(pcname)
        print(pcimg)
        print(pcprice)
        print(pclink)
        print('分類:'+pcclname)

由於迴圈遞增商品項數50筆總會有到底的時候,也就是該分類已無商品。這時候就要回傳一個值用來判斷跳出迴圈:

def pcgood(pccid,page,pcclname):
    url = 'https://ecapi.pchome.com.tw/mall/prodapi/v1/newarrival/prod&region=' + str(pccid) + '&offset='+ str(page) +'&limit=50&_callback=jsonpcb_newarrival'
    user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
    headers = {'User-Agent':user_agent,
              'server': 'PChome/1.0.4',
              'Referer': 'https://mall.pchome.com.tw/newarrival/'}
    res = requests.get(url=url,headers=headers)#分析得出的網址
    res_text = res.text
    res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
    jd = json.loads(res_text_format)
    if jd['Rows'] != []:
        pcgoods = (jd['Rows'][:])
        for pcgood in pcgoods:
            pcname = pcgood['Name']
            pcimg = 'https://b.ecimg.tw' + pcgood['Pic']['S']
            pcprice = pcgood['Price']['P']
            pclink = 'https://mall.pchome.com.tw/prod/'+ pcgood['Id']
            print(pcname)
            print(pcimg)
            print(pcprice)
            print(pclink)
            print('分類:'+pcclname)
    else:
        status = 'no_data'
        return status
        

將整個爬蟲系統也包進一個函數,

在取得分類後在其中進行頁數遍歷,頁數遍歷完則進行下一個分類

而每次爬取一個頁面都暫停8秒,避免被伺服器阻擋(經過測試,設置5秒也有機率被阻擋)

def pchomecrawler():
    url = 'https://ecapi.pchome.com.tw/mall/cateapi/v1/sign&tag=newarrival&fields=Id,Name,Sort,Nodes&_callback=jsonpcb_newarrival&'+str(millis)
    user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #偽裝使用者
    headers = {'User-Agent':user_agent,
              'server': 'PChome/1.0.4',
              'Referer': 'https://mall.pchome.com.tw/newarrival/'}
    res = requests.get(url=url,headers=headers)#分析得出的網址
    res_text = res.text
    res_text_format = res_text.replace('try{jsonpcb_newarrival(','').replace(');}catch(e){if(window.console){console.log(e);}}','')
    jd = json.loads(res_text_format)
    pcclass = jd[1:2]
    #print(pc)
    for pc in pcclass:
        pcclname = pc['Name']
        print(pc['Name'])
        for pccl in pc['Nodes']:
            pccid = pccl['Id']
            pageid = 1
            for page in range(99):  
                print(page)
                if pcgood(pccid,pageid,pcclname) != 'no_data':
                    pcgood(pccid,pageid,pcclname)
                    pageid = pageid + 50
                    time.sleep(8)
                else:
                    break

最後成果如下

至此為止爬取的目標已經達成

可以再添加例外處理讓爬蟲程式更加健全

而處理好的資料也可以存入資料庫進行查詢與使用

 

 

文章標籤
全站熱搜
創作者介紹
創作者 ivankao 的頭像
ivankao

IvanKao的部落格

ivankao 發表在 痞客邦 留言(0) 人氣(0)