如果不借助他人的数据,你能自己算出瑞幸咖啡和星巴克咖啡其各自的门店数量吗?
让你自己算出一个精确的值,你会使用什么方法进行计算一线城市门店数量?
难度高一点点,你怎么样才能知道二线城市的门店总数,甚至是全国的门店数量?
用我们今天的方法,你可以知道,瑞幸咖啡在一线城市的数量是:1634间,而星巴克则为:1587间。
往下看答案之前,你可以想想有几种方法可以实现我们的目的。
1.准备
开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。
Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),准备开始输入命令安装依赖。
当然,我更推荐大家用VSCode编辑器,把本文代码Copy下来,在编辑器下方的终端装依赖模块,多舒服的一件事啊:Python 编程的最好搭档—VSCode 详细指南。
输入以下命令安装我们所需要的依赖模块:
pip install requests
看到 Successfully installed xxx 则说明安装成功。你可以在Python实用宝典公众号后台回复:咖啡门店数 获得本文完整数据和代码。
2.获取门店数
怎么样,文章开头提出的问题你想到答案了吗?
其实很简单,那就是调用地图的接口进行门店搜索。 通过这个方法,我们不仅可以算出门店的数量,还能得到每个门店的对应位置,并且可以用来做后续的数据分析:
所以现在问题就转化为找到有提供搜索接口的地图供应商,而且这个接口得是免费的,因此我找了腾讯地图的接口:
你只需要上去注册账号,申请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实用宝典