python模拟登录饿了么并爬取指定位置附近的商家信息

这篇博客介绍了如何使用Python模拟登录饿了么网站,并爬取指定位置附近的商家信息。涉及的模块包括openpyxl进行Excel读写、pypinyin实现汉字转拼音、geohash获取经纬度编码、requests的cookies处理以及json数据转换。通过学习,可以爬取全国多地的商家数据,具有潜在的商业价值。内容中还提到了登录过程中的问题及解决办法,如geohash导入错误和图片验证码的应对策略。
部署运行你感兴趣的模型镜像

 此篇内容使用到了以下模块:

1.使用openpyxl实现excel的读写

2.使用pypinyin实现汉子转拼音

3.使用geohash获取经纬度编码

4.使用requests中的utils方法实现cookies转字典和字典转cookies

5.使用json对列表和字典以及字符串之间进行转换

通过此次的学习可以爬取到全国2000多个城市指定位置附近的商家信息,如果再进一步挖掘的就具备了很大的商业价值

import requests
from pypinyin import lazy_pinyin
import geohash
import json
import openpyxl
session = requests.session()


# 将处理后的信息写入excel文件中
def write_info():
    rest_list = get_nearby_restaurants_by_menu()
    if rest_list != False:
        # 新建excel
        wb = openpyxl.Workbook()
        # 获取工作表
        sheet = wb.active
        # 工作表名称
        sheet.title = "饿了么商家信息"
        # 设置单元格的列宽
        sheet.column_dimensions['A'].width = 40
        sheet.column_dimensions['B'].width = 20
        sheet.column_dimensions['C'].width = 60
        sheet.column_dimensions['D'].width = 20
        sheet.column_dimensions['E'].width = 30
        # 表头信息
        headers =['商家名称','月销量','商家活动','配送费','营业时间']
        sheet.append(headers)
        # 写入数据
        for rest in rest_list:
            sheet.append([rest['name'],rest['order_num'],rest['activities'],rest['ship_fee'],rest['open_hours']])
        # 保存数据
        wb.save("饿了么商家信息.xlsx")


# 信息处理,获取所需要的信息
def get_result(res):
    restaurants_infos = []
    for info in res:
        infos = {}
        name = info['name']  # 商家名称
        recent_order_num_display = info['business_info']
        order_num = json.loads(recent_order_num_display)['recent_order_num_display']  # 月销量
        open_hours = info['opening_hours'][0]  # 营业时间
        latitude = info['latitude']  # 商家地理位置经纬度
        longitude = info['longitude']
        piecewise_agent_fee = info['piecewise_agent_fee']['tips']  # 配送费
        activities = info['activities'][0]['tips']  # 活动
        infos['name'] = name
        infos['order_num'] = order_num
        infos['open_hours'] = open_hours
        infos['latitude'] = latitude
        infos['longitude'] = longitude
        infos['ship_fee'] = piecewise_agent_fee
        infos['activities'] = activities
        restaurants_infos.append(infos)
        # print(name + " " + str(order_num) + " " + str(open_hours) + " " + str(latitude) + " " + str(
        #     longitude) + " " + str(piecewise_agent_fee) + " " + activities)
    return restaurants_infos


# 爬去附近商家信息
def get_nearby_restaurants_by_menu():
    # 是否登录成功
    login_flag = login_with_cookies()
    if login_flag == 200:
        print("=====================登陆成功=====================")
    else:
        print("=====================登陆失败=====================")
        return False
    city = input("请输入所在城市:(eg:杭州市输入【杭州】)")
    menu_info = print_menu(city)
    print("您选择获取【{}】类别商家的信息".format(menu_info['name']))
    restaurants_infos = []
    for i in range(0,48,24):
        every_info = []
        print("***************************正在爬取第{}页***************************".format(i // 24 + 1))
        rest_url = 'https://www.ele.me/restapi/shopping/restaurants'
        headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36',
            'referer':'https://www.ele.me/place/wtmkhzfkmgh1?latitude={}&longitude={}'.format(menu_info['nearby_latitude'],menu_info['nearby_longitude'])
        }
        ids = []
        if menu_info['name'][:1] == "0":
            ids = menu_info['ids']
        else:
            ids.append(menu_info['id'])
        rest_data = {
            'extras[]':'activities',
            'geohash':menu_info['nearby_geohash'],
            'latitude':str(menu_info['nearby_latitude']),
            'longitude':str(menu_info['nearby_longitude']),
            'limit':'24',
            'offset':str(i),
            'restaurant_category_ids[]':ids,
            'terminal':'web'
        }
        res = session.get(rest_url,headers=headers,params=rest_data).json()
        # 数据处理
        every_info = get_result(res)
        restaurants_infos.extend(every_info)
    return restaurants_infos


# 获取所选择菜单的信息
def print_menu(city):
    menu_list = get_menu_info(city)
    print("*"*90)
    for menu in menu_list:
        if menu['name'][:2] in ('4 ','9 ','14'):
            print(menu['name'])
        else:
            print(menu['name'],end='\t\t\t')
    print("*" * 90)
    num = int(input("请选择对应菜单的编号:(eg:0)"))
    return menu_list[num]


# 获取附近商家的菜单分类选项
def get_menu_info(city):
    nearby_geohash,nearby_latitude,nearby_longitude = get_currAddr_info(city)
    menu_url = 'https://www.ele.me/restapi/shopping/v2/restaurant/category'
    menu_data = {
        'latitude':nearby_latitude,
        'longitude':nearby_longitude
    }
    res = session.get(menu_url,params=menu_data).json()
    i = 0
    menu_list = []
    for menu in res:
        if i == 0:
            menu_info = {'nearby_geohash':nearby_geohash,'nearby_latitude':nearby_latitude,'nearby_longitude':nearby_longitude,'name':'{} {}'.format(i,menu['name']),'ids':menu['ids']}
        else:
            menu_info = {'nearby_geohash':nearby_geohash,'nearby_latitude':nearby_latitude,'nearby_longitude':nearby_longitude,'name':'{} {}'.format(i,menu['name']),'id':menu['id'],'ids':menu['ids'],'sub_categories':menu['sub_categories']}
        menu_list.append(menu_info)
        i += 1
    return menu_list


# 获取当前收货地址的经纬度信息
def get_currAddr_info(city):
    # 获取经纬度以及geohash编码
    latitude, longitude,geohash_code = get_geohash(city)
    addr = input("请输入收货地址:")
    nearby_url = 'https://www.ele.me/restapi/bgs/poi/search_poi_nearby'
    nearby_data = {
        'geohash': geohash_code,
        'keyword': addr,
        'latitude': latitude,
        'limit': ' 20',
        'longitude': longitude,
        'type': ' nearby'
    }
    # 获取收货地址的经纬度信息
    res = session.get(nearby_url,params=nearby_data).json()
    return res[0]['geohash'],res[0]['latitude'],res[0]['longitude']


# 使用cookies登录
def login_with_cookies():
    # 先从cookies中获取信息
    cookies = get_cookies()
    if cookies != False:
        session.cookies = cookies
        return 200
    else:
        return login_with_phone()


# 模拟使用手机号登录
def login_with_phone():
    url = 'https://h5.ele.me/restapi/eus/login/mobile_send_code'
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36'
    }
    tel = input("请输入手机号:")
    data = {
        'captcha_hash': '',
        'captcha_value': '',
        'mobile': tel,
        'scf': 'ms'
    }
    res = session.post(url, headers=headers, data=data).json()
    validate_token = ''
    try:
        validate_token = res['validate_token']
    except:
        print("无法获取token\t{}".format(res))
        return
    # 输入验证码并登录
    login_url = 'https://h5.ele.me/restapi/eus/login/login_by_mobile'
    code = input("请输入验证码:")
    login_data = {
        'mobile': tel,
        'scf': ' ms',
        'validate_code': code,
        'validate_token': validate_token
    }
    # 模仿登陆
    res = session.post(login_url, headers=headers, data=login_data)
    # 获得cookies,将其写入文件中
    write_cookies(res.cookies)
    return res.status_code


# 读取cookies信息
def get_cookies():
    try:
        file = open("login_cookie.txt", 'r', encoding='utf-8')
        cookie_json = file.read()
        # 将json转换为字典
        cookie_dict = json.loads(cookie_json)
        # 字典转换为cookie
        cookies = requests.utils.cookiejar_from_dict(cookie_dict)
        return cookies
    except FileNotFoundError:
        return False


# 写入cookies
def write_cookies(cookies):
    # # 将cookies转换为字典
    cookies_dict = requests.utils.dict_from_cookiejar(cookies)
    # 将字典转换为字符串
    cookies_json = json.dumps(cookies_dict)
    file = open("login_cookie.txt", 'w', encoding='utf-8')
    file.write(cookies_json)
    file.close()


# 根据经纬度获取geohash编码
def get_geohash(city):
    latitude,longitude = get_lati_and_longi(city)
    code= geohash.encode(latitude,longitude,12)
    return latitude,longitude,code


# 获取经纬度信息
def get_lati_and_longi(city):
    # 获取首汉子的首字母,以及所有汉子的首字母
    first_word,all_first_word= get_first_word(city)
    cities_url = "https://www.ele.me/restapi/shopping/v1/cities"
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36'
    }
    cities= requests.get(cities_url,headers=headers).json()
    for key,value in cities.items():
        # 根据首字母定位数据
        if key == first_word:
            # 根据所有的首字母定位数据
            for city_info in value:
                if city_info['abbr'] == all_first_word and city_info['name'] == city:
                    return city_info['latitude'],city_info['longitude']
    return


# 根据输入的城市将其转换为对应的拼音
def get_first_word(city):
    # 汉子转拼音
    to_pinyin = lazy_pinyin(city)
    # 遍历获取每一个汉子的首字母,并转换为大写
    first_word_list = []
    for letter in to_pinyin:
        first_word_list.append(letter[:1].upper())
    # 第一个汉子的首字母
    first_leter_first_word = first_word_list[0]
    # 所有拼音的首字母
    all_letter_first_word = ''
    for letter in first_word_list:
        all_letter_first_word += letter
    return first_leter_first_word,all_letter_first_word


# 调用方法
write_info()

测试:

第一种:首次登录需要手机号以及验证码登录

请输入手机号:***********
请输入验证码:******
=====================登陆成功=====================
请输入所在城市:(eg:杭州市输入【杭州】)杭州
请输入收货地址:古墩路243号
******************************************************************************************
0 全部商家			1 美食			2 快餐便当			3 特色菜系			4 全球美食
5 小吃夜宵			6 甜品饮品			7 商店超市			8 鲜花绿植			9 医药健康
10 早餐			11 午餐			12 下午茶			13 晚餐			14 夜宵
******************************************************************************************
请选择对应菜单的编号:(eg:0)1
您选择获取【1 美食】类别商家的信息
***************************正在爬取第1页***************************
***************************正在爬取第2页***************************

第二种:已经登陆过后,再次登录只需要读取cookies即可

=====================登陆成功=====================
请输入所在城市:(eg:杭州市输入【杭州】)郑州
请输入收货地址:二七广场
******************************************************************************************
0 全部商家			1 美食			2 快餐便当			3 特色菜系			4 全球美食
5 小吃夜宵			6 甜品饮品			7 商店超市			8 鲜花绿植			9 医药健康
10 早餐			11 午餐			12 下午茶			13 晚餐			14 夜宵
******************************************************************************************
请选择对应菜单的编号:(eg:0)10
您选择获取【10 早餐】类别商家的信息
***************************正在爬取第1页***************************
***************************正在爬取第2页***************************

爬取到的内容存入excel这里就不再提供截图

以上功能还有很多需要优化的地方,比如:对输入的手机号和验证码的判断,爬取数据的优化,cookiies是否过期等等

关于代码过程中遇到的问题:

1. geohash导入报错的问题

    1.1 将python安装目录中 Lib\site-packages\文件夹中的Geohash文件夹名称改为geohash  eg: Lib\site-packages\Geohash ->geohash

    1.2 将修改后的geohash文件夹中的__init__.py中的 from geohash import decode_exactly, decode, encode改为from .geohash import decode_exactly, decode, encode

    1.3 将修改后的Lib\site-packages\geohash文件夹复制到Lib目录下 Lib\geohash

2. 在测试过程中需要图片验证码导致无法模拟登陆

    2.1 这个是因为手机号登录次数过于频繁,一般三次以内是不需要图片验证码的

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值