python爬蟲基礎Ⅴ——帶cookies請求、session:餓了麼--附近餐館信息



基礎爬蟲部分Ⅴ

這裏打算用一個實例來簡單說下cookie和會話。

也許看起來我下面寫的做這個任務很順利,其實都是經過摸索得出來的,像一些參數在哪裏獲得,哪些參數是代表什麼意思,都是要經過不斷嘗試、對比才得出答案,所以要有耐心哦!

爬取餓了麼上的附近餐館

要求就是要爬取附近餐館的店名和評分(其實可以獲取到更多)。

運行過程大概這樣:輸入手機號碼 -> 輸入驗證碼 -> 輸入所在地址 -> 選擇具體地址 -> 獲取數據

在這裏插入圖片描述
在這裏插入圖片描述


cookies

其實,你對cookies並不陌生,我敢肯定你見過它。比如一般當你登錄一個網站,你都會在登錄頁面看到一個可勾選的選項“記住我”,如果你勾選了,以後你再打開這個網站就會自動登錄,這就是cookie在起作用。

當然,cookies也是有時效性的,過期後就會失效。你應該有過這樣的體驗:哪怕勾選了“記住我”,但一段時間過去了,網站還是會提示你要重新登錄,就是之前的cookies已經失效。


session

所謂的會話,你可以理解成我們用瀏覽器上網,到關閉瀏覽器的這一過程。session是會話過程中,服務器用來記錄特定用戶會話的信息。比如你打開瀏覽器逛購物網頁的整個過程中,瀏覽了哪些商品,在購物車裏放了多少件物品,這些記錄都會被服務器保存在session中。

如果沒有session,你加購了很多商品在購物車,打算結算時,發現購物車空無一物Σ(っ°Д°;)っ,因爲服務器根本沒有幫你記錄你想買的商品。

sessioncookies的關係還非常密切——cookies中存儲着session的編碼信息,session中又存儲了cookies的信息。當瀏覽器第一次訪問購物網頁時,服務器會返回set-cookies的字段給瀏覽器,而瀏覽器會把cookies保存到本地。

等瀏覽器第二次訪問這個購物網頁時,就會帶着cookies去請求,而因爲cookies裏帶有會話的編碼信息,服務器立馬就能辨認出這個用戶,同時返回和這個用戶相關的特定編碼的session

這也是爲什麼你每次重新登錄購物網站後,你之前在購物車放入的商品並不會消失的原因。因爲你在登錄時,服務器可以通過瀏覽器攜帶的cookies,找到保存了你購物車信息的session

我們通常通過創建一個session來處理cookies,它會幫我們自動保持cookies


POST請求

之前學的get可以帶着參數請求,且get請求的參數會在url上顯示出來。其實post也可以帶參請求,只是參數不會顯示在url上,而是隱藏起來
像賬號密碼這種私密的信息,就應該用post的請求。如果用get請求的話,賬號密碼全部會顯示在網址上,這顯然不科學!你可以這麼理解,get是明文顯示,post是非明文顯示。
通常,get請求會應用於獲取網頁數據,比如我們之前學的requests.get()post請求則應用於向網頁提交數據,比如提交表單類型數據(像賬號密碼就是網頁表單的數據)。
get 和 post是兩種最常用的請求方式,除此之外,還有其他類型的請求方式,如head、options等,但一般很少用到。


獲取登錄的cookie

你在能登錄的網站上,打開第一個請求的【headers】面板裏,【requests headers】存儲的是瀏覽器的請求信息,【response headers】存儲的是服務器的響應信息。我們要找的cookies就在其中。
你會看到在【response headers】裏有set-cookies的參數,就是服務器往瀏覽器寫入了cookies。

http://httpbin.org/post 這個鏈接可以用來測試POST請求,它可以輸出請求的一些信息,其中包含我們傳遞的data參數(get請求中用的參數是params,post請求用的是data)。現在做個示範。

import requests

url = 'http://httpbin.org/post' #這個鏈接可以用來測試POST請求,它可以輸出請求的一些信息,其中包含我們傳遞的data參數
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36'
} #請求頭
data = {
    'user' : "username",
    'password' : '123456'
} #傳遞的參數
res = requests.post(url,data=data,headers=headers)

print(res.status_code) #打印出請求的狀態碼,若狀態碼等於200,則證明請求成功。

print(res.text) #輸出請求的信息,我們傳遞的參數出現在form字段中,表明是模擬了表單提交的方式,即POST方式傳輸數據。

cookies = res.cookies #提取cookies的方法:調用requests對象(login_in)的cookies屬性獲得登錄的cookies,並賦值給變量cookies。

但是我前面說了,由於會話可以自動保持cookies,所以通常創建一個會話來處理cookie,後面再要用到時就不用傳入cookies。

import requests

session = requests.Session()
#用requests.session()創建session對象,賦值給session,相當於創建了一個特定的會話,幫我們【自動保持了cookies】。後面要用的時候就不用傳入cookies

url = 'http://httpbin.org/post' #這個鏈接可以用來測試POST請求,它可以輸出請求的一些信息,其中包含我們傳遞的data參數
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36'
}
data = {
    'user' : "username",
    'password' : '123456' #其實傳什麼參數都可以,畢竟這只是個測試POST請求的網站
}
res = session.post(url,data=data,headers=headers)

print(res.status_code) #打印出請求的狀態碼,若狀態碼等於200,則證明請求成功。
#無需再提取cookies了,因爲session幫我們自動保持了cookie(我不是復讀機)。

分析過程

下面開始分析吧!

藉助cookies的相關知識,使用Python登錄餓了麼網站,爬取自己家附近的餐廳列表。

網站地址:https://www.ele.me/home/

因爲餓了麼要登錄才能獲取餐館列表,所以我們需要用使用cookies模擬登陸.

(1) 模擬發送驗證碼

體驗登錄地址:https://h5.ele.me/login/

是一個需要手機和驗證碼登錄的界面。我們需要先模擬發送驗證碼的請求,再模擬登錄的請求。

在頁面按下 F12 -> Network -> XHR -> 網頁刷新 -> 輸入手機號碼後點擊【獲取驗證碼】(如果發送驗證碼不成功,說明你發太多次了,建議換個手機號碼或等幾個小時)

此時會加載出一個新的請求爲mobile_send_code,很明顯就是“發送手機驗證碼”的意思,點開它,

在這裏插入圖片描述

然後拉到下面【Request Payload】,這裏是模擬發送驗證碼請求所需要的一些參數mobile參數的值是你的手機號碼。

在這裏插入圖片描述

注意:請求驗證碼時,會返回一個json,json裏會有validate_token的值(Preview裏可以看到),先把它記下,在我們輸入賬號驗證碼模擬登錄的時候會用到它。

然後我們可以寫發送驗證碼的代碼啦。

import requests

session = requests.session() #用requests.session()創建session對象
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}

#模擬發送驗證碼
tel = input("請輸入手機號碼:")
url1 = 'https://h5.ele.me/restapi/eus/login/mobile_send_code'
data1 = { 
    'captcha_hash': '',
    'captcha_value': '',
    'mobile': tel,
    'scf': ''}
#注意:請求驗證碼時,會返回一個json,json裏會有【validate_token】的值,它在登錄時作爲參數用到,這裏我們把它記住,賦值給token
token = session.post(url1, headers=headers, data=data1).json()['validate_token']

(2) 使用session模擬登錄

然後我們勾選一下Preserve log保存日誌,再輸入驗證碼,點擊【登錄】,這時會加載出一個login_by_mobile的請求,明顯是手機號碼登錄的意思,點開它:

在這裏插入圖片描述
參數裏面的倒數第二個是剛纔收到的手機驗證碼,最後一個就是我們剛纔模擬發送驗證碼請求時響應返回的值token,這裏就用到了。於是可以接着往下寫模擬登錄的代碼:

#*******接着上一個代碼後面*******
#模擬登陸[login_by_mobile]
code = input('請輸入驗證碼:')
url2 = 'https://h5.ele.me/restapi/eus/login/login_by_mobile'
data2 = {
    'mobile': tel,
    'scf': 'ms',
    'validate_code': code,
    'validate_token': token
}
session.post(url2, headers=headers, data=data2) #模擬用手機號碼登陸

這下我們已經登錄好了,就可以去獲取餐館列表啦✿✿ヽ(°▽°)ノ✿


(3) 模擬輸入地址,獲取必要參數

爲了請求餐館列表,我們需要幾個所在地址的關鍵參數(至於爲什麼,往後看就知道遼):

  1. geohash:通過搜索得之,這是一個能夠代表地理位置的字符串。
  2. latitude:緯度
  3. longitude:經度

這三個參數,都需要模擬輸入地址來獲得。
體驗輸入地址:https://www.ele.me/home/

提示:1.本步驟不需要模擬登錄。2.在模擬該請求時需要用到城市的geohash,可在 XHR 裏查看自己城市的geohash值。

例如下面是我獲得【五一廣場】的地理位置信息,看看這個請求的參數,這些是長沙的地理位置信息:

在這裏插入圖片描述

再看一下【Preview】裏面,有加載出來的詳細地址,地名是鍵爲name的值,具體地址是鍵爲short_address的值,然後地址的地理位置信息(前邊那個圖裏的是城市的地理位置)也在裏面,要把這個列表打印出來供用戶選擇。那麼首先我們先要模擬請求地址,繼續完善代碼:

#*******又接着上一段代碼*******
place = input('請輸入你的收貨地址:')
address_url = 'https://www.ele.me/restapi/bgs/poi/search_poi_nearby?'
# 在模擬該請求時需要用到城市的geohash值,可在XHR裏查看自己城市的geohash值(例如深圳:ws105rz9smwm)
params = {'geohash':'wt029fxwdpkh',
          'keyword':place,
          'latitude':'28.227779',
          'limit':'20',
          'longitude':'112.938858',
          'type':'nearby'}
# 將要傳遞的參數封裝成字典,鍵與值都要用字符串,其中keyword對於的值是place。
address_res = requests.get(address_url, params=params)
# 發起請求,將響應的結果,賦值給address_res
address_json = address_res.json()
# 將響應的結果轉爲列表/字典,此處是列表
print('以下,是與'+place+'相關的位置信息:\n')
n = 0
for address in address_json: #每個address是一個字典
    print(str(n)+'. '+address['name']+':'+address['short_address']+'\n')
    n = n+1
address_num = int(input('請輸入您選擇位置的序號:')) # 讓用戶選擇序號。

由於在 restaurants 請求需要用到 地址的一些參數值,然後根據用戶選擇,獲取收穫地址的參數:

final_address = address_json[address_num]
geohash = final_address['geohash']
latitude = final_address['latitude']
longitude = final_address['longitude']

(4) 帶cookies和參數請求餐館列表

然後我再點選【五一廣場】,下一個頁面就有餐館列表了,在restaurants請求裏:

在這裏插入圖片描述

然後去【Preview】裏選出我們要的數據就可以啦,繼續完善代碼:

url3 = 'https://www.ele.me/restapi/shopping/restaurants?'# 使用帶有餐館列表的那個XHR地址。
params = {
    'extras[]': 'activities',
    'geohash': geohash,
    'latitude': latitude,
    'limit': '24',
    'longitude': longitude,
    'offset': '0',
    'terminal': 'web'
}

# 將參數封裝,其中geohash和經緯度,來自前面獲取到的數據。
restaurants_res = session.get(url3, params=params)# 帶參數發起請求(GET請求參數用params表示,POST請求參數用data表示)
restaurants_json = restaurants_res.json()# 把response對象,轉爲json。
#print(restaurants_json)
print( '\n\n' +'在' + address_json[address_num]['name'] + '附近的餐館有:') #這是用戶在上一步選擇的地址
for restaurant in restaurants_json:
    # restsurants最外層是一個列表,它可被遍歷。restaurant則是字典,裏面包含了單個餐廳的所有信息。
    print('店名:'+restaurant['name'],'評分:'+str(restaurant['rating']))
    print()

(5) 代碼整合

其實要一路順利做下來真的不簡單,多嘗試幾次吧!ヾ(◍°∇°◍)ノ゙理清楚各步驟間的關係,下面是我的完整代碼:

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import requests

session = requests.session() #創建會話
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'
}


#模擬發送驗證碼
tel = input("請輸入手機號碼:")
url1 = 'https://h5.ele.me/restapi/eus/login/mobile_send_code'
data1 = {
    'captcha_hash': '',
    'captcha_value': '',
    'mobile': tel,
    'scf': ''}
#注意:請求驗證碼時,會返回一個json,json裏會有【validate_token】的值,它在登錄時作爲參數用到
token = session.post(url1, headers=headers, data=data1).json()['validate_token']


#模擬登陸[login_by_mobile]
code = input('請輸入驗證碼:')
url2 = 'https://h5.ele.me/restapi/eus/login/login_by_mobile'
data2 = {
    'mobile': tel,
    'scf': 'ms',
    'validate_code': code,
    'validate_token': token
}
session.post(url2, headers=headers, data=data2) #登陸


#由於在 restaurants 請求需要用到 地址的一些參數值,先獲取地址的參數
place = input('請輸入你的收貨地址:')
address_url = 'https://www.ele.me/restapi/bgs/poi/search_poi_nearby?'
# 因爲我們的geohash使用了深圳的值,所以推薦你測試的時候使用“騰訊大廈”。
# 在模擬該請求時需要用到城市的geohash值,可在XHR裏查看自己城市的geohash值(例如深圳:ws105rz9smwm,下邊的是長沙的)
params = {'geohash':'wt029fxwdpkh',
          'keyword':place,
          'latitude':'28.227779',
          'limit':'20',
          'longitude':'112.938858',
          'type':'nearby'}
# 將要傳遞的參數封裝成字典,鍵與值都要用字符串,其中keyword對於的值是place。
address_res = requests.get(address_url, params=params)
# 發起請求,將響應的結果,賦值給address_res
address_json = address_res.json()
# 將響應的結果轉爲列表/字典,此處是列表
print('以下,是與'+place+'相關的位置信息:\n')
n = 0
for address in address_json: #每個address是一個字典
    print(str(n)+'. '+address['name']+':'+address['short_address']+'\n')
    n = n+1
address_num = int(input('請輸入您選擇位置的序號:'))
# 讓用戶選擇地址的序號,再記下地址的相關參數
final_address = address_json[address_num]
geohash = final_address['geohash']
latitude = final_address['latitude']
longitude = final_address['longitude']


#帶cookies請求餐館列表
url3 = 'https://www.ele.me/restapi/shopping/restaurants?'# 使用帶有餐館列表的那個XHR地址。
params = {
    'extras[]': 'activities',
    'geohash': geohash,
    'latitude': latitude,
    'limit': '24',
    'longitude': longitude,
    'offset': '0',
    'terminal': 'web'
}

# 將參數封裝,其中geohash和經緯度,來自前面獲取到的數據。
restaurants_res = session.get(url3, params=params)# 發起請求,將響應的結果,賦值給restaurants_res
restaurants_json = restaurants_res.json()# 把response對象,轉爲json。
#print(restaurants_json)
print( '\n\n' +'在' + final_address['name'] + '附近的餐館有:' + '\n') #這是用戶在上一步選擇的地址
for restaurant in restaurants_json:
    # restsurants最外層是一個列表,它可被遍歷。restaurant則是字典,裏面包含了單個餐廳的所有信息。
    print('店名:'+restaurant['name'],'評分:'+str(restaurant['rating']))
    print()



————————每個人都在抱怨生活不易,可是都在默默爲生活打拼————————

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章