請啟用JavaScript來查看內容

[Python爬蟲實例] 如何爬取"蝦皮購物"的商品、評價資料?

前言

大家都喜歡買東西,而現代人購物很大一部份會從線上的購物平台來尋找,像是台灣前幾大的 PChome24h購物、momo購物網、蝦皮購物、露天拍賣等等。

這次的Python網路爬蟲實例系列要來挖掘"蝦皮購物",試著說明如何抓取各項商品資訊、評價資料,跟著文章體驗爬蟲的樂趣吧~

備註:此文僅教育學習,切勿用作商業用途,個人實作皆屬個人行為,本作者不負任何法律責任

線上購物 (來源:Unsplash)
線上購物 (來源:Unsplash)

套件

此次 Python 爬蟲主要使用到的套件:

安裝

1
pip install requests

尋找抓取資料的方法

我們想要爬取"搜尋商品結果"頁面的商品資訊,以下以搜尋"手機保護殼"來示範。

前往蝦皮購物網站,一開始當然是打開我們熟悉的 開發人員工具 (F12 或 Ctrl + Shift + i),切換到"Network" > "XHR",並在網頁上方搜尋欄打入關鍵字、點擊搜尋。

搜尋商品結果
搜尋商品結果

接著在開發人員工具頁面中,它會記錄著每個請求,從中尋找蝦皮購物是否採用動態載入的方式載入資料,可以一個一個查看,或使用搜尋(Ctrl+f)來找商品名稱。

在其中一條 ?by=relevancy&keyword=... 開頭的請求中發現了搜尋結果商品資訊。

"Network" > "XHR" 動態載入
"Network" > "XHR" 動態載入

開發人員工具切換到"Headers"頁面查看請求的網址與方法。

"Headers"頁面
"Headers"頁面

請求網址:https://shopee.tw/api/v2/search_items/?by=relevancy&keyword=手機保護殼&limit=50&newest=0&order=desc&page_type=search&version=2
請求方法:GET


但如果你直接拿此網址去用 Python requests.get(url),會發現回傳資料會不完整、裡面的多項數據也是錯誤的,只在 Headers 加上 user-agent 顯然是不夠的。
經過我的測試,將 referer 加入 Headers 後資料就完整了。
(放到 referer 前記得要將 url 編碼才不會噴錯)

但又遇到一個問題,仔細看會發現,怎麼取得的資料和網頁上看到的順序不一樣?!
首先猜測 Headers 是否還缺少哪個參數導致,經過多次嘗試後,發覺是 x-api-source 與 cookie。

x-api-source 帶入 pc 即可,很簡單。
但 cookie 則需要當中的 SPC_SI 值,這個值該從何而來。

我將 SPC_SI 的值貼到搜尋(Ctrl+f),來找找這個值是從哪邊來的。
發現其中一個是在請求 https://shopee.tw/api/v4/search/product_labels 後,返回的結果 set-cookie 中有設定 SPC_SI。

因此這部分我就先嘗試使用 requests 的 Session,他會把每次請求都算在同一個 session 裡,像是 cookie 之類的都會保持,這樣我就不用還要將 cookie 讀出來,下一次請求再設定了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import urllib


keyword = '手機保護殼'
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.68',
    'x-api-source': 'pc',
    'referer': f'https://shopee.tw/search?keyword={urllib.parse.quote(keyword)}'
}

s = requests.Session()
url = 'https://shopee.tw/api/v4/search/product_labels'
r = s.get(url, headers=headers)

base_url = 'https://shopee.tw/api/v2/search_items/'
query = f"by=relevancy&keyword={keyword}&limit=50&newest=0&order=desc&page_type=search&version=2"
url = base_url + '?' + query
r = s.get(url, headers=headers)
if r.status_code == requests.codes.ok:
    data = r.json()
    # with open('shopee.json', 'w', encoding='utf-8') as f:
    #     f.write(r.text)

回傳資料是 JSON 格式,可以使用 requests 提供的 r.json() 轉換。


其他兩個"商品資訊"與"評價資訊"都是採用同樣的方法來尋找請求網址,這邊就不再重複說明。

下面我將"搜尋商品"、"商品資訊"、"評分資訊"所找出來的請求路徑、參數內容及回傳資料整理出來說明。

搜尋商品

搜尋商品的頁面大致上能分為三個部分,藍色方框為商品結果、綠色方框為篩選功能(雖然應該算排序)、紫色方框為條件篩選。

搜尋商品結果 板塊
搜尋商品結果 板塊

請求路徑

GET https://shopee.tw/api/v2/search_items/?by=relevancy&keyword={關鍵字}&limit=50&newest=0&order=desc&page_type=search&version=2

範例:https://shopee.tw/api/v2/search_items/?by=relevancy&keyword=手機保護殼&limit=50&newest=0&order=desc&page_type=search&version=2

refererhttps://shopee.tw/search?keyword={關鍵字}
(也就是搜尋頁面網址,記得將 url 編碼)

請求參數

來看一下請求網址帶的參數有哪些:

  • byorder 排序用,也就是網頁上面這欄(雖然它這邊是寫篩選,但其功能是排序):
排序用欄位
排序用欄位
篩選byorder
綜合排名relevancydesc
最新ctimedesc
熱銷salesdesc
價格(高到低)pricedesc
價格(低到高)priceasc
  • keyword:很明顯就是想要搜尋的關鍵字。

  • limit:一次請求最大回傳的結果數量,預設20個、最多100個,但如果給超過100個,會回傳10個。

  • newest:偏移量的概念,如果limit設50、newest設100,就如同取得第三頁(第101~150個)。

  • page_type:設定search代表我們使用搜尋。還有像是shop,則用在查看某一賣場內商品。

  • version:可能是指 API 的版本。

  • 條件篩選
    在網頁左邊的位置,有許多類別可以選擇,底下會一一介紹,至於參數的值,某些數量較多就不全部列出了(像是"用送方式"、"分類"、"品牌")。

條件篩選
條件篩選

如果有使用"條件篩選",請求會再多加上 skip_autocorrect=1 (跳過自動修正),這個參數不確定會產生怎樣的影響。

某些參數可以給多個值,其中就使用,隔開,例如運送方式要"全家、7-11",參數就設定為shippings=1,2

條件參數
運送方式
全家shippings=1
7-11shippings=2
賣家宅配shippings=8
出貨地點
台灣locations=-1
海外locations=-2
分類
ASUS保護殼categoryids=7964
卡通殼categoryids=17360
品牌
NOKIAbrandids=137374
ASUSbrandids=4747
價格
最小 100 元price_min=100
最大 500 元price_max=500
評價
5星rating_filter=5
4星或以上rating_filter=4
賣場類型
蝦皮商城official_mall=1
蝦皮優選shopee_verified=1
商品保存狀況
新商品conditions=new
二手商品conditions=used
付款方式
信用卡pay_credit_card=1
服務與促銷
目前有折扣with_discount=true
運費補助shipping_fee_included=true
商城最低價filter_lowest_price_guarantee=1
多件優惠wholesale=1
影音介紹filter_video=1
快速出貨label_ids=1000028
蝦幣回饋label_ids=1000040

* "商品保存狀況"裡如果你兩個都勾,會等於兩個都不勾。

* 舉例來說:
我查詢"手機保護殼",並以"最熱銷"排序,
條件篩選有"運送方式 7-11"、"價格最高 600 元"、"評價四星或以上"、"新商品",
一次最多 100 筆商品
https://shopee.tw/api/v2/search_items/?by=sales&conditions=new&keyword=手機保護殼&limit=100&newest=0&order=desc&page_type=search&price_max=600&rating_filter=4&shippings=2&skip_autocorrect=1&version=2

回傳資料

它以 JSON 格式回傳,商品會在 items 欄位內,每一個商品有許多欄位,這邊挑幾個來說明。

欄位代表意思
itemid商品ID
shopid賣場ID
name商品名稱
brand品牌
shop_location賣場位置(出貨地)
ctime上架時間(時間戳)
can_use_wholesale是否有多件優惠
show_free_shipping是否顯示免運
shopee_verified是否為蝦皮優選
show_shopee_verified_label是否顯示蝦皮優選標籤
is_official_shop是否為官方商店
show_official_shop_label是否顯示官方商店標籤
show_official_shop_label_in_title是否在標題中顯示官方商店標籤
is_adult是否為成人商品
historical_sold已售出數量
stock庫存
liked_count喜歡數
view_count瀏覽數
currency貨幣
has_lowest_price_guarantee最低價格保證
discount折扣
price價格(要再除100000)
price_before_discount折扣前價格
price_min價格範圍(最低)
price_max價格範圍(最高)
price_min_before_discount折扣前價格範圍(最低)
price_max_before_discount折扣前價格範圍(最高)
cmt_count評價數量
item_rating評價
image商品圖片(封面)
images商品圖片
video_info_list影片
tier_variations規格
voucher_info優惠券資訊

* 回傳資料裡面並無"商品網址",要自己串,如下所示:
https://shopee.tw/{商品名稱}-i.{賣場ID}.{商品ID}
https://shopee.tw/犀牛盾-iPhone12-11-Pro-Mini-Se-XR-7-8-Xs-Max-手機-防摔殼-防摔邊框-台灣公司貨-i.9060351.1558498138

商品名稱中間加如有 " "(空白) 或 "/" 要轉換成 "-",而且如有連續多個也要合併成一個。
但…我發現商品網址其實不一定需要"商品網址",就算這部分隨便給也都可以(但不能不給),像是上方範例改成這樣也可😆。
https://shopee.tw/一代一代-i.9060351.1558498138

[更新 2021/04/04] "商品網址"還有另一種樣式,就不需要包含商品名稱了,如下:
https://shopee.tw/product/{賣場ID}/{商品ID}
https://shopee.tw/product/6453718/103810019


* 商品圖片是一串碼,要取得圖片網址需再如下自行補上,如要取得其縮圖(解析度較低、檔案較小)則再加上 _tn 即可:
圖片網址:https://cf.shopee.tw/file/a8b82098b055a69c3b63b73fe1978caf
縮圖網址:https://cf.shopee.tw/file/a8b82098b055a69c3b63b73fe1978caf_tn

* 影片網址則是先從 video_info_list 讀到 video_id,再如下組成網址:
影片網址:https://cv.shopee.tw/video/JmkCD3CvRP50uxb-

* 可以從 adsid 是否有值來判斷這間商品是否為廣告。

商品資訊

在商品頁面取得,相比上方"搜尋商品"得到的資訊來說,這邊會有更多的資訊,例如商品詳情、各規格較詳細資訊等等。

商品頁面
商品頁面

請求路徑

GET https://shopee.tw/api/v2/item/get?itemid={商品ID}&shopid={賣場ID}

範例:https://shopee.tw/api/v2/item/get?itemid=1558498138&shopid=9060351

refererhttps://shopee.tw/{商品名稱}-i.{賣場ID}.{商品ID}
(也就是此商品網址,還有記得將 url 編碼)
[更新 2021/04/04] 或是 https://shopee.tw/product/{賣場ID}/{商品ID}

請求參數

參數很簡單,就只有 itemidshopid 兩項:

  • itemid:商品ID。
  • shopid:賣場ID。

回傳資料

同樣是以 JSON 格式回傳,資料內欄位很多,但大部分欄位都跟上面"搜尋商品"一樣,這邊列出幾個上面沒有但較重要的欄位:

欄位代表意思
description商品詳情
estimated_days估計出貨天數
models各規格較詳細資訊
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
{
    "item": {
      "itemid": 6155409258,
      "price_max_before_discount": -1,
      "item_status": "normal",
      "can_use_wholesale": false,
      "show_free_shipping": false,
      "estimated_days": 20,
      "is_hot_sales": null,
      "is_slash_price_item": false,
      "upcoming_flash_sale": null,
      "slash_lowest_price": null,
      "is_partial_fulfilled": false,
      "condition": 1,
      "show_original_guarantee": false,
      "add_on_deal_info": null,
      "is_non_cc_installment_payment_eligible": false,
      "categories": [
        {
          "display_name": "手機平板與周邊",
          "catid": 70,
          "image": null,
          "no_sub": false,
          "is_default_subcat": false,
          "block_buyer_platform": null
        },
        {
          "display_name": "手機周邊配件",
          "catid": 1579,
          "image": null,
          "no_sub": false,
          "is_default_subcat": false,
          "block_buyer_platform": null
        },
        {
          "display_name": "轉接電視棒、線",
          "catid": 16762,
          "image": null,
          "no_sub": true,
          "is_default_subcat": false,
          "block_buyer_platform": null
        }
      ],
      "ctime": 1601691912,
      "name": "【WoW世界代購】2020 Google Chromecast with Google TV 4k HDMI媒體串流播放",
      "show_shopee_verified_label": false,
      "size_chart": null,
      "is_pre_order": true,
      "service_by_shopee_flag": null,
      "historical_sold": 1428,
      "reference_item_id": "",
      "recommendation_info": null,
      "bundle_deal_info": null,
      "price_max": 210000000,
      "has_lowest_price_guarantee": false,
      "shipping_icon_type": 0,
      "images": [
        "82ddad81bd0cc6b03d797acbdf51c7cd",
        "bb192fb5989cef4358a5377100965fd8",
        "1ee7c425fedb0b5f0cfef7dddf475ec1",
        "f0df75429124a11407597908abf76f7f",
        "46d282f0742b2ea5fe9a21fafacf6c03",
        "044f715ffd1e982989aa50fd35370bcc",
        "6a8bf7deb89df6cc49edd27612cc8604"
      ],
      "price_before_discount": 0,
      "cod_flag": 0,
      "catid": 70,
      "is_official_shop": false,
      "coin_earn_label": null,
      "hashtag_list": null,
      "sold": 210,
      "makeup": null,
      "item_rating": {
        "rating_star": 4.96895,
        "rating_count": [
          1096,
          1,
          2,
          3,
          21,
          1069
        ],
        "rcount_with_image": 150,
        "rcount_with_context": 305
      },
      "show_official_shop_label_in_title": false,
      "discount": null,
      "reason": null,
      "label_ids": [
        1001276,
        1000575,
        1000630,
        1002199,
        1002826
      ],
      "has_group_buy_stock": false,
      "other_stock": 35,
      "deep_discount": null,
      "attributes": [
        {
          "is_pending_qc": false,
          "idx": 0,
          "value": "Google",
          "id": 7958,
          "is_timestamp": false,
          "name": "品牌"
        }
      ],
      "badge_icon_type": 0,
      "liked": false,
      "cmt_count": 1096,
      "image": "82ddad81bd0cc6b03d797acbdf51c7cd",
      "recommendation_algorithm": null,
      "is_cc_installment_payment_eligible": true,
      "shopid": 3149132,
      "normal_stock": 35,
      "video_info_list": [],
      "installment_plans": null,
      "view_count": 15059,
      "voucher_info": null,
      "current_promotion_has_reserve_stock": false,
      "liked_count": 934,
      "show_official_shop_label": false,
      "price_min_before_discount": -1,
      "show_discount": 0,
      "preview_info": null,
      "flag": 720896,
      "exclusive_price_info": null,
      "current_promotion_reserved_stock": 0,
      "wholesale_tier_list": [],
      "group_buy_info": null,
      "shopee_verified": false,
      "item_has_post": false,
      "hidden_price_display": null,
      "transparent_background_image": "",
      "welcome_package_info": null,
      "discount_stock": 0,
      "coin_info": {
        "spend_cash_unit": 100000,
        "coin_earn_items": []
      },
      "is_adult": false,
      "currency": "TWD",
      "raw_discount": 0,
      "is_preferred_plus_seller": false,
      "is_category_failed": false,
      "price_min": 210000000,
      "can_use_bundle_deal": false,
      "cb_option": 0,
      "brand": "",
      "stock": 35,
      "status": 1,
      "bundle_deal_id": 0,
      "is_group_buy_item": null,
      "description": "原廠保固一年,我們免費代送保固哦!\n\n下單後才代購,預計3/9左右抵台灣出貨,出貨時會通知,......",
      "flash_sale": null,
      "models": [
        {
          "itemid": 6155409258,
          "status": 1,
          "current_promotion_reserved_stock": 0,
          "name": "粉紅(缺貨)",
          "promotionid": 0,
          "price": 220000000,
          "price_stocks": [
            {
              "model_id": 23792748542,
              "stockout_time": 1609939591,
              "region": "TW",
              "rebate": null,
              "price": 220000000,
              "promotion_type": 0,
              "allocated_stock": null,
              "shop_id": 3149132,
              "end_time": null,
              "stock_breakdown_by_location": [],
              "item_id": 6155409258,
              "promotion_id": 0,
              "purchase_limit": null,
              "start_time": null,
              "stock": 0
            }
          ],
          "current_promotion_has_reserve_stock": false,
          "currency": "TWD",
          "normal_stock": 0,
          "extinfo": {
            "seller_promotion_limit": null,
            "has_shopee_promo": null,
            "group_buy_info": null,
            "holiday_mode_old_stock": null,
            "tier_index": [
              2
            ],
            "seller_promotion_refresh_time": 0
          },
          "price_before_discount": 0,
          "modelid": 23792748542,
          "sold": 1,
          "stock": 0
        },
        {
          "itemid": 6155409258,
          "status": 1,
          "current_promotion_reserved_stock": 0,
          "name": "白色snow(3/9出貨)",
          "promotionid": 0,
          "price": 210000000,
          "price_stocks": [
            {
              "model_id": 51236728441,
              "stockout_time": 0,
              "region": "TW",
              "rebate": null,
              "price": 210000000,
              "promotion_type": 0,
              "allocated_stock": null,
              "shop_id": 3149132,
              "end_time": null,
              "stock_breakdown_by_location": [],
              "item_id": 6155409258,
              "promotion_id": 0,
              "purchase_limit": null,
              "start_time": null,
              "stock": 35
            }
          ],
          "current_promotion_has_reserve_stock": false,
          "currency": "TWD",
          "normal_stock": 35,
          "extinfo": {
            "seller_promotion_limit": null,
            "has_shopee_promo": null,
            "group_buy_info": null,
            "holiday_mode_old_stock": null,
            "tier_index": [
              0
            ],
            "seller_promotion_refresh_time": 0
          },
          "price_before_discount": 0,
          "modelid": 51236728441,
          "sold": 1329,
          "stock": 35
        }
      ],
      "has_low_fulfillment_rate": false,
      "price": 210000000,
      "shop_location": "高雄市苓雅區",
      "tier_variations": [
        {
          "images": [],
          "properties": [],
          "type": 0,
          "name": "顏色",
          "options": [
            "白色snow(3/9出貨)",
            "粉紅(缺貨)"
          ]
        }
      ],
      "can_use_cod": null,
      "makeups": null,
      "welcome_package_type": 0,
      "show_official_shop_label_in_normal_position": null,
      "item_type": 0
    },
    "version": "3ce549a98de8723d3e24613cc448f7f8",
    "data": null,
    "error_msg": null,
    "error": null
  }

評價資訊

評價資訊指的是商品頁面底下,買家對於此商品的評分、評論、照片、影片等資料。

評價資訊
評價資訊

請求路徑

GET https://shopee.tw/api/v2/item/get_ratings?filter=0&flag=1&itemid={商品ID}&limit=6&offset=0&shopid={賣場ID}&type=0

範例:https://shopee.tw/api/v2/item/get_ratings?filter=0&flag=1&itemid=1558498138&limit=6&offset=0&shopid=9060351&type=0

refererhttps://shopee.tw/{商品名稱}-i.{賣場ID}.{商品ID}
(同樣是此商品網址,一樣記得將 url 編碼)

請求參數

來看一下評價資訊的請求網址帶哪些參數:

  • shopid:賣場ID。
  • itemid:商品ID。
  • flagtype:篩選條件。
篩選filtertype
全部留言00
五星留言05
四星留言04
三星留言03
二星留言02
一星留言01
附上評論10
附上照片/影片30

* 經過嘗試,發現兩者貌似無法混用,例如無法篩選"附上評論的三星留言"這樣。

  • limit:一次請求最大回傳的結果數量,預設 15 個、最多 59 個,但如果給超過 59 個,反而會回傳 0 個。
  • offset:偏移值。
  • flag:未知。

回傳資料

它也是以 JSON 格式回傳,data 底下有 item_rating_summaryratings 兩個欄位。
item_rating_summary 是總評價的統計,像是總評分為幾分、有幾個五星評價、有幾個加上照片/影片的評價;ratings 裡面就是各評價的詳細資料。

以下我列出一些各評價中比較重要的欄位,如與上方有重複的就不再寫出來:

欄位代表意思
rating_star評分(星等)
author_portrait作者大頭照
cmtid評價ID
author_username評分者帳號
author_shopid評分者賣場ID
comment評論
anonymous是否匿名
ctime創建評分時間(時間戳)
mtime修改評分時間(時間戳)
product_items > model_name評分者購買商品的規格
ItemRatingReply賣家回應

* 作者大頭照與之前圖片一樣,網址 https://cf.shopee.tw/file/ 再加上圖片ID即可。

* 這邊影片網址不需要再像上面那樣串,它直接給影片網址了。


延伸練習

  1. 試著將"搜尋商品"、"商品資訊"、"評分資訊"的抓取資料挑選、整理之後,儲存到檔案(txt、csv、json)中。

  2. 以上文章說明抓取"搜尋商品"、"商品資訊"、"評分資訊",請試著自己寫寫看"如何取得指定賣場內商品"?
    例如:Logitech羅技官方旗艦館 裡頭所販售的所有商品。

注意事項

後來發現如果短時間太頻繁的送出請求,會被伺服器阻擋,貌似正常獲得資料,但其實仔細看會發現資料有誤,例如價格很明顯不正確等等。

結語

在尋找、爬取"蝦皮購物"的相關資料時會覺得很有趣,因為它是使用 API 的方式去請求資料,也不用自己寫 BeautifulSoup 來解析網頁,一步步尋找個參數代表什麼意思,我想這也是在寫爬蟲程式過程的樂趣吧。

之後會繼續陸續寫一些網站的Python網路爬蟲實例,如果你正好是剛開始想學爬蟲的新手、想知道某網站如何爬取資料,又或遇到其他問題,歡迎參考與在底下留言喔~🔖




逆風的方向,更適合飛翔,我不怕千萬人阻擋,只怕自己投降。

—— 五月天《倔強》


🔻 如果覺得喜歡,歡迎在下方獎勵我 5 個讚~
分享

Jia
作者
Jia
軟體工程師