今天爲你們重寫一個美團美食板塊小爬蟲,說不定哪天作旅遊攻略的時候也能夠用下呢。廢話很少說,讓咱們愉快地開始吧~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進步慢的夥伴們,在這裏爲你們準備了豐富的學習大禮包