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()



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

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