文章目录
一、概述
在看这篇文章之前,建议大家先看这两篇文章(token解析与生成和getPoiList接口调用),因为前两篇文章的介绍是本文的基础。本文要讲解的内容主要包括两个方面:一是获取店铺的分页数据,二是将分页数据存入MySQL数据库。
二、获取店铺的分页数据
1. URL调整:
在getPoiList接口调用文章中已经详细讲解了如何模拟浏览器的请求调用美团接口,获取店铺数据,这里只需要对URL进行稍微的调整,即可获取到不同页的店铺数据。URL中需要调整的参数主要有cateId、page 和 originUrl 三个。
# get category id
def get_cateId(link_url):
splits = link_url.split('/')
return splits[-2].replace('c', '')
# call interface to get json data
def call_interface(page, originUrl):
cityName = '广州'
cate_id = get_cateId(originUrl)
originUrl = str_replace(originUrl)
token = str_replace(encode_token())
url = 'https://gz.meituan.com/meishi/api/poi/getPoiList?' \
'cityName=%s' \
'&cateId=%s' \
'&areaId=0' \
'&sort=' \
'&dinnerCountAttrId=' \
'&page=%s' \
'&userId=' \
'&uuid=05bf3db6-3c2f-41cd-a4ec-ed79ae0a9506' \
'&platform=1' \
'&partner=126' \
'&originUrl=%s' \
'&riskLevel=1' \
'&optimusCode=1' \
'&_token=%s' % (cityName, cate_id, page, originUrl, token)
response = requests.get(url, headers=simulateBrowserHeader)
if response.status_code == 200:
data = response.json()['data']
return data
if response.status_code == 403:
print('Access is denied by server!')
return {}
2. 根据菜系获取分页数据:
首先需要通过scrapy爬取所有菜系的url列表dish_url_list,然后遍历dish_url_list列表,获取每个菜系的所有分页数据。调用getPoiList接口会返回该菜系的总数totalCounts,因此可以计算出每个菜系的总页数,最后就可以调用call_interface() 方法获取分页数据了。
class FoodSpider(scrapy.Spider):
name = 'food'
allowed_domains = ['gz.meituan.com']
start_urls = ['https://gz.meituan.com/meishi/']
def parse(self, response):
# 所有菜系URL
dish_url_list = response.xpath('//*[@id="app"]//*[@data-reactid="20"]/li/a/@href').extract()
# print(dish_url_list)
# traverse each dish_url to get food data
for dish_url in dish_url_list:
yield Request(dish_url, callback=self.parse_food)
def parse_food(self, response):
origin_url = response.url
print('crawl food from ' + origin_url)
dish_type = response.xpath('//*[@id="app"]//*[@class="hasSelect"]/span[2]/text()').extract()[0]
re_data = call_interface(1, origin_url)
data_list = get_food_list(dish_type, re_data['poiInfos'])
# calculate how many pages
if re_data['totalCounts'] % 15 == 0:
page_num = re_data['totalCounts'] // 15
else:
page_num = re_data['totalCounts'] // 15 + 1
for page in range(2, page_num + 1):
re_data = call_interface(page, origin_url)
data_list.extend(get_food_list(dish_type, re_data['poiInfos']))
write_to_db(data_list)
三、将分页数据存入MySQL数据库
1. 创建数据库表:
这里根据自己对店铺数据的需要创建数据库即可,如果有想要偷懒或者闲麻烦的小伙伴,可以直接复制下面的SQL语句创建表。
create table tb_restaurants
(
pk_id char(36) not null comment '主键',
dish_type varchar(20) not null comment '所属菜系',
restaurant_name varchar(50) not null comment '餐厅名称',
location varchar(100) not null comment '餐厅地址',
price int not null comment '人均价格',
star float not null comment '评分',
img_url varchar(200) not null comment '餐厅图片链接',
comment_num int not null comment '评论数量',
primary key (pk_id)
);
2. 新建实体类:
根据上面创建的数据库表,创建相应的实体类,这里初始化所有数据成员都是None。至于 to_json() 方法,只是为了便于把获取的数据保存为json文件。
class MeituanItem:
def __init__(self):
self.pk_id = None
self.dish_type = None
self.restaurant_name = None
self.location = None
self.price = None
self.star = None
self.img_url = None
self.comment_num = None
# transfer to dict, convenient to save as json file
def to_json(self):
return {
'pk_id': self.pk_id,
'dish_type': self.dish_type,
'restaurant_name': self.restaurant_name,
'location': self.location,
'price': self.price,
'star': self.star,
'img_url': self.img_url,
'comment_num': self.comment_num
}
3. 转换为实体对象列表:
该方法中的category参数是菜系类别,poiInfos参数是dict类型,就是调用getPoiList接口返回的json数据,其具体返回的属性值详见下图所示。
# get food detail from poiInfos
def get_food_list(category, poiInfos):
item_list = []
for i in range(0, len(poiInfos)):
item = MeituanItem()
item.pk_id = str(uuid.uuid1())
item.dish_type = category
item.restaurant_name = poiInfos[i]['title']
item.location = poiInfos[i]['address']
item.price = 0 if poiInfos[i]['avgPrice'] is None else int(poiInfos[i]['avgPrice'])
item.star = float(poiInfos[i]['avgScore'])
item.img_url = poiInfos[i]['frontImg']
item.comment_num = int(poiInfos[i]['allCommentNum'])
item_list.append(item)
return item_list
4. 建立数据库连接并写入数据库:
使用pymysql建立数据库连接主要需要配置host、port、user、password、database这几个参数,写入数据库这里使用的是原生的SQL语句方式,就不多做介绍了,直接看下面的代码吧。
# write data into mysql database
def write_to_db(item_list):
# mysql connection information
conn = pymysql.Connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='meituan',
charset='utf8')
cursor = conn.cursor()
# insert into database one by one
for item in item_list:
sql = 'INSERT INTO TB_RESTAURANTS(pk_id, dish_type, restaurant_name, location, price, star, img_url,' \
' comment_num) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
params = (item.pk_id, item.dish_type, item.restaurant_name, item.location, item.price, item.star, item.img_url,
item.comment_num)
# excute sql
cursor.execute(sql, params)
# commit
conn.commit()
cursor.close()
# close connection
conn.close()
print('Write data into MySQL database successfully!')
写入数据成功后,最后可以在数据库中查询到有上万条店铺数据,如下图所示。
四、致谢
至此,爬取美团店铺数据整个流程都已经讲解完了,如果有需要整个项目源码的小伙伴,可前往此github链接去下载。如果你觉得笔者写得还不错,还请点个赞,感谢!