close

一、實作目標:

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

 

二、爬蟲構思:

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

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

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

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

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

再爬取該json的商品資料

 

三、爬蟲實作:

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

1.分析網頁

首先找出該網站商品目錄

pchome購物中心 最新商品

2.JPG

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

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

5.JPG

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

6.JPG

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

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

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

10.JPG

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

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

7.JPG

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

3.JPG

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

4.JPG

最後會有一串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)

運行結果:

8.JPG

直接從網址打開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)

9.JPG

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

使用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)

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

11.JPG

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)

12.JPG

這裡要做兩個處理:獲得分類名稱、取出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'])

14.JPG

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

三個參數分別為:商品分類代號、跳轉的頁數(每頁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

最後成果如下

15.JPG

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

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

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

 

 

arrow
arrow
    全站熱搜

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