标签归档:Python 咖啡

Python 计算 瑞幸和星巴克 谁的门店最多

如果不借助他人的数据,你能自己算出瑞幸咖啡和星巴克咖啡其各自的门店数量吗?

让你自己算出一个精确的值,你会使用什么方法进行计算一线城市门店数量?

难度高一点点,你怎么样才能知道二线城市的门店总数,甚至是全国的门店数量?

用我们今天的方法,你可以知道,瑞幸咖啡在一线城市的数量是:1634间,而星巴克则为:1587间

往下看答案之前,你可以想想有几种方法可以实现我们的目的。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。

当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南

输入以下命令安装我们所需要的依赖模块:

pip install requests

看到 Successfully installed xxx 则说明安装成功。你可以在Python实用宝典公众号后台回复:咖啡门店数 获得本文完整数据和代码。

2.获取门店数

怎么样,文章开头提出的问题你想到答案了吗?

其实很简单,那就是调用地图的接口进行门店搜索。 通过这个方法,我们不仅可以算出门店的数量,还能得到每个门店的对应位置,并且可以用来做后续的数据分析:

所以现在问题就转化为找到有提供搜索接口的地图供应商,而且这个接口得是免费的,因此我找了腾讯地图的接口:

https://lbs.qq.com/

你只需要上去注册账号,申请Key即可调用相关的接口,申请完了记得开webserviceAPI,选择签名校验的形式调用接口:

2.1 初始化

为了使用API,我们得先初始化请求链接及其所需要的参数:

class LocationSearch(object):

    def __init__(self, keyword: str):
        self.keyword = keyword
        self.key = '你的Key'
        self.sk = '你的校验sk'
        self.url = (
            'https://apis.map.qq.com/ws/place/v1/search?'
            'boundary=region({},0)&key={}&keyword={}'
            '&page_index={}&page_size=20'
        )

Key是在你申请API权限的时候就会分配给你的,而sk是在你选择 签名校验 的形式调用接口时分配给你的。

那么我们如何用这两个数据请求接口呢?请看下面这个函数:

def request_data(self, location: str, page: int):
    """
    请求接口数据
    Arguments:
        location {str} -- 地点
        page {int} -- 第几页
    
    Returns:
        {list} -- 该页该地点的数据
        {int} -- 该地点结果总数
    """
    # 拼接链接
    url = self.url.format(location, self.key, self.keyword, page)
    # 获得数字签名,并将签名加到链接后面进行请求
    wait_sig = url.split('qq.com')[1] + str(self.sk)
    sig = hashlib.md5(wait_sig.encode('utf-8')).hexdigest()
    res = requests.get(url + '&sig=' + sig)
    # 获得数据返回
    pois = res.json()['data']
    # 避免请求上限
    time.sleep(0.2)
    return pois, res.json()['count'] 

首先是将初始化的请求链接拼接起来,然后由于需要签名校验,因此我们得如下进行操作:

GET请求分为:域名,请求路径和参数三个部分,用于签名计算的有:
请求路径: /ws/place/v1/search?
请求参数: boundary=region({},0)&key={}&keyword={} &page_index={}&page_size=20
注意{}是待填充的

1. 首先对参数进行排序:按参数名升序(本例结果为 boundary 在前,key在后,如果第一个字母相同,要依据第二个字母升序):
boundary=region({},0)&key={}(….后面略)

2. 签名计算(sig):
请求路径+”?”+请求参数+SK进行拼接,并计算拼接后字符串md5值,即为签名(sig):
要求:请求参数必须是未进行任何编码(如urlencode)的原始数据
md5(” /ws/place/v1/search?boundary=region({},0)&key={}&keyword={} &page_index={}&page_size=20你的SK“)

计算得到结果类似为:22dxxxxxxxxxxxxxx2b0bcc0e50

3. 生成最终请求:将计算得到的签名sig,放到请求中(参数名即为:sig):
https://apis.map.qq.com/ws/place/v1/search? boundary=region({},0)&key={}&keyword={} &page_index={}&page_size=20&sig= 22dxxxxxxxxxxxxxx2b0bcc0e50

注意:计算 sig 要使用原始参数值,不要进行任何编码,但最终发送时的参数,是需要时行url编码的

最后拿到返回的值,里面带有所有结果的地理位置及结果的数量。

2.2 按地点返回结果

这一部分其实很简单,就是调用2.1的函数,然后实现分页保存变量,最后输出门店数量,返回数据。

def get_single_location(self, location: str):
    """
    获得单个地点的数据
    
    Arguments:
        location {str} -- 地点
    
    Returns:
        {list} -- 该地点某关键词的所有数据
        {int} -- 该地点某关键词的所有数量
    """
    page = 1
    location_data = []
    pois, total = self.request_data(location, page)
    for poisition in pois:
        location_data.append(poisition)
    # 如果有多页
    while (total / 20) > page:
        pois, _ = self.request_data(location, page)
        for poisition in pois:
            location_data.append(poisition)
        page += 1
    print(f'{self.keyword} {location} 门店总数为:{total}')
    return location_data, total 

计算一线城市的结果如下:

F:\push\20200315>python scrapy.py
瑞幸咖啡 北京 门店总数为:492
瑞幸咖啡 上海 门店总数为:581
瑞幸咖啡 广州 门店总数为:301
瑞幸咖啡 深圳 门店总数为:260

2.3 汇总结果并保存

接下来我们需要汇总2.2计算到的每个城市的数据,保存到json文件,并计算总数。

def get_cities_data(self, cities: str):
    """
    获得所有城市某关键词的数据
    
    Arguments:
        cities {list} -- 城市列表
    """
    result = []
    keyword_count = 0
    for city in cities:
        # 获得该城市的所有门店和总数
        data, count = self.get_single_location(city)
        keyword_count += count
        result.extend(data)
    print(f'{self.keyword} 一线城市门店总数为:{keyword_count}')
    # 导出数据
    with open(f'{self.keyword}.json', 'w') as my_file:
        json.dump(result, my_file, ensure_ascii=False) 

最终可以获得一个 瑞幸咖啡.json 的文件,里面存有每个城市的咖啡店精确位置,并输出一个总数,这样调用即可:

if __name__ == '__main__':
    cities = ['北京', '上海', '广州', '深圳']

    loc = LocationSearch('瑞幸咖啡')
    loc.get_cities_data(cities)

    loc = LocationSearch('星巴克咖啡')
    loc.get_cities_data(cities)

F:\push\20200315>python scrapy.py
瑞幸咖啡 北京 门店总数为:492
瑞幸咖啡 上海 门店总数为:581
瑞幸咖啡 广州 门店总数为:301
瑞幸咖啡 深圳 门店总数为:260
瑞幸咖啡 一线城市门店总数为:1634

星巴克 北京 门店总数为:380
星巴克 上海 门店总数为:797
星巴克 广州 门店总数为:209
星巴克 深圳 门店总数为:201
星巴克 一线城市门店总数为:1587

看来瑞幸咖啡一线城市里的门店数量已经超过星巴克了,不愧是割资本主义国家韭菜,造福中国老百姓的企业啊!

3.扩展

就像文章开头所提到的,如果你需要算出每个城市的咖啡店数量其实也很简单,咱可以调用下面这个接口请求腾讯地图的所有行政区数据,获得所有城市的名称:

https://apis.map.qq.com/ws/district/v1/list

不过我已经dump了一个,大家在Python实用宝典公众号后台回复:咖啡门店数 即可获得。

使用这一个,你只需要读取该csv文件提取所有城市名,然后放入cities变量中进行计算,如下代码所示:

if __name__ == '__main__':
    with open('cities.csv', 'r', encoding='utf-8') as csvfile:
        reader = csv.reader(csvfile)
        cities = [row[0] for row in reader]

    loc = LocationSearch('瑞幸咖啡')
    loc.get_cities_data(cities)

    loc = LocationSearch('星巴克咖啡')
    loc.get_cities_data(cities)

不过,请注意一些特殊情况,比如说那个城市没有数据的时候,接口可能不会返回date数据,这时候要用字典的get方法进行处理:

# pois = res.json()['data']
pois = res.json().get('data', []) 

不过如果你要计算全国的数据的话,这个方法并不可靠,因为无法避免山寨店的存在,山寨店一样也会被记入到腾讯地图中,而一线城市的监管严格,比较少出现山寨店的情况,因此可以用这个方法计算。

我们的文章到此就结束啦,如果你喜欢我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典