Python爬蟲系列之爬取美團美食板塊商家數據(二)

今天爲你們重寫一個美團美食板塊小爬蟲,說不定哪天作旅遊攻略的時候也能夠用下呢。廢話很少說,讓咱們愉快地開始吧~json

開發工具

Python版本:3.6.4
相關模塊:

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進步慢的夥伴們,在這裏爲你們準備了豐富的學習大禮包

image

相關文章
相關標籤/搜索