今天为你们重写一个美团美食板块小爬虫,说不定哪天作旅游攻略的时候也能够用下呢。废话很少说,让咱们愉快地开始吧~json
requests模块;api
argparse模块;app
pyquery模块;echarts
jieba模块;dom
pyecharts模块;ide
wordcloud模块;函数
以及一些Python自带的模块。工具
安装Python并添加到环境变量,pip安装须要的相关模块便可。学习
前期准备:开发工具
由于我想让这个小爬虫能够爬取美团上任意城市美食板块的数据,可是每一个城市的URL是不同的,其格式为:
https://{城市拼音缩写}.meituan.com/
不一样的城市须要不一样的URL来构造请求从而爬取咱们所须要的数据,因而如今的问题就变成了:如何获取全部城市对应的城市拼音缩写呢?
其实很简单,点击网页上的切换城市按钮:
而后查看网页源代码:
因而咱们很easy地就能够爬取全部城市对应的城市拼音缩写了,代码实现以下:
'''城市名-拼音码爬取''' def downCitynamesfile(citynamesfilepath): url = 'https://www.meituan.com/changecity/' doc = PyQuery(requests.get(url).text) cities_dict = dict() [cities_dict.update({city.text(): city.attr('href').replace('.', '/').split('/')[2]}) for city in doc('.cities a').items()] with open(citynamesfilepath, 'w', encoding='utf-8') as f: f.write(json.dumps(cities_dict, indent=2, ensure_ascii=False))
爬虫主程序:
如今随便切换到一个城市,以杭州为例。简单抓个包,能够发现美食商家的数据能够经过请求下图这个URL得到:
其构造方式为上图红框框出的baseURL加上下图所示的一堆参数:
其中变量为:
cityName:城市名 page:页码 uuid:uuid _token:_token
其余均为不变量,直接copy过来就好了。前面两个变量很明显是什么,就很少说了。变量uuid在网页源代码里就能找到:
至于_token,稍微麻烦一点。考虑到_token结尾出现了=,因此猜想是base64编码,可是解码后发现是一堆16进制ASCII码,因此考虑原数据是先进行二进制压缩而后base64编码的。反向操做一波,发现果真是这样的:
全局搜索找生成相关参数的源代码:
一顿分析以后就能够开始写_token生成的代码了,具体以下:
'''获取SIGN''' def getSIGN(cityname, page, uuid, city_code): url = 'https://{}.meituan.com/meishi/'.format(city_code) sign = 'areaId=0&cateId=0&cityName={}&dinnerCountAttrId=&optimusCode=1&originUrl={}&page={}&partner=126&platform=1&riskLevel=1&sort=&userId=&uuid={}' sign = sign.format(cityname, url, page, uuid) return sign '''获取_token参数''' def getToken(brfilepath, city_code, uuid, page, cityname): ts = int(time.time() * 1000) with open(brfilepath, 'r') as f: brs_dict = json.load(f) key = random.choice(list(brs_dict.keys())) info = brs_dict[key] _token = { 'rId': 100900, 'ver': '1.0.6', 'ts': ts, 'cts': ts + random.randint(100, 120), 'brVD': info.get('barVD'), 'brR': [info.get('brR_one'), info.get('brR_two'), 24, 24], 'bI': ['https://{}.meituan.com/meishi/'.format(city_code),''], 'mT': [], 'kT': [], 'aT': [], 'tT': [], 'aM': '', 'sign': getSIGN(cityname, page, uuid, city_code) } return base64.b64encode(zlib.compress(str(_token).encode())).decode()
OK,知道了baseURL,得到了全部参数,咱们就能够愉快地写主程序了:
'''主函数''' def MTSpider(cityname, maxpages=50): data_pages = {} citynamesfilepath, uafilepath, uuidfilepath, brfilepath, savedatapath = initialProgram(cityname) base_url = 'https://{}.meituan.com/meishi/api/poi/getPoiList?'.format(cityname2CODE(cityname, citynamesfilepath)) try: for page in range(1, maxpages+1): print('[INFO]: Getting the data of page<%s>...' % page) data_page = None while data_page is None: params = getGETPARAMS(cityname, page, citynamesfilepath, uuidfilepath, brfilepath) url = base_url + urlencode(params) headers = { 'Accept': 'application/json', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'User-Agent': getRandomUA(uafilepath), 'Connection': 'keep-alive', 'Host': 'bj.meituan.com', 'Referer': 'https://{}.meituan.com/'.format(cityname2CODE(cityname, citynamesfilepath)) } res = requests.get(url, headers=headers) data_page = parsePage(json.loads(res.text)) if data_page is None: time.sleep(random.random()+random.randint(3, 6)) initialProgram(cityname) data_pages.update(data_page) if page != maxpages: time.sleep(random.random()+random.randint(3, 6)) except: print('[Warning]: Something wrong...') with open(savedatapath, 'wb') as f: pickle.dump(data_pages, f)
其中解析返回的json数据的函数以下:
'''解析一页数据''' def parsePage(data_page): data_parse = dict() infos = data_page.get('data') if infos is None: return None else: infos = infos.get('poiInfos') for info in infos: # 店名: 地址, 评论数量, 平均得分, 平均价格 data_parse[info.get('title')] = [info.get('address'), info.get('allCommentNum'), info.get('avgScore'), info.get('avgPrice')] return data_parse
一些细节和tricks就不细说了。
All Done!完整源代码详见主页我的介绍获取相关文件。
按惯例随手可视化一波,以抓取的杭州美食数据为例吧(这里只爬取了前50页),省的从新爬了。
先来搞个词云玩玩吧,用爬到的全部商家名/商家地址来搞个词云:
而后咱们假设美食性价比的定义为(这个假设极可能是不合理,这里只是为了方便本身作下简单的数据分析随便假设了一下。):
性价比 = 评论数量 x 平均得分 / 平均价
因而咱们能够获得"杭州性价比最高的十家店"为(只是个小例子,不供参考,若有雷同,不胜荣幸。):
OK。完整源代码详见主页中我的介绍获取相关文件。**
为了帮助学习Python进步慢的伙伴们,在这里为你们准备了丰富的学习大礼包