金秋九月,丹桂飘香,在这秋高气爽,阳光灿烂的收获季节里,我们送走了一个个暑假余额耗尽哭着走向校园的孩子们,又即将迎来一年一度伟大祖国母亲的生日趴体(无心上班,迫不及待想为祖国母亲庆生)。
那么问题来了,去哪儿玩呢?百度输了个“国庆”,出来的第一条居然是“去哪里旅游人少”……emmmmmmm,因缺思厅。
于是我萌生了通过旅游网站的景点销量来判断近期各景点流量情况的想法(这个想法很危险啊)。
所以这次的目标呢,是爬去哪儿网景点页面,并得到景点的信息,大家可以先思考下大概需要几步。
本文建议有一定 Python 基础和前端(html,js)基础的朋友阅读。
因为前几次爬虫都是爬一些文本信息,做一下词云之类的,我觉得:没!意!思!了!这次正好爬的是数据,我决定用数据的好基友——图表来输出我爬取的数据,也就是说我要用爬取的景点销量以及景点的具体位置来生成一些可视化数据。
安利一下百度的地图 API 和 echarts,前者是专门提供地图 API 的工具,听说好多 APP 都在用它,后者是数据处理居家旅行的好伙伴,用了之后,它好,我也好(隐约觉得哪里不对)。
API 是什么,API 是应用程序的编程接口,就好像插头与插座一样,我们的程序需要电(这是什么程序?),插座中提供了电,我们只需要在程序中写一个与插座匹配的插头接口,就可以使用电来做我们想做的事情,而不需要知道电是如何产生的。
引入数据后的百度热力图
通过 API 对接的开发者与服务商
有人可能说,我已经懂了 API 是啥意思了,可是咋个用呢。关于这一点,我很负责任的告诉你:我也不会。
但是!
百度地图提供了很多 API 使用示例,有 html 基础,大致可以看懂,有 js 基础就可以尝试改函数了(不会 js 的,我默默地复制源代码),仔细观察源代码,可以知道热力图生成的主要数据都存放在 points 这个变量中。
这种[{x:x,x:x},{x:x,x:x}]格式的数据,是一种 json 格式的数据,由于具有自我描述性,所以比较通俗易懂,大概可以知道这里的三个值,前两个是经纬度,最后一个应该是权重(我猜的)。
也就是说,如果我希望将景点的热门程度生成为热力图,我需要得到景点的经纬度,以及它的权重,景点的销量可以作为权重,并且这个数据应该是 json 格式的呈现方式。
echarts 也是一样滴(*^__^*)。
这次的爬虫部分是比较简单的。
分析网址(去哪儿景点)→爬取分页中信息(景点经纬度、销量)→转为 json 文件。
分析去哪儿景点页的网址,可得出结构:
http://piao.qunar.com/ticket/list.htm?keyword=搜索地点®ion=&from=mpl_search_suggest&page=页数
这次没有用正则来匹配内容,而使用了 xpath 匹配,非常好用。
def getList():
place = raw_input('请输入想搜索的区域、类型(如北京、热门景点等):')
url = 'http://piao.qunar.com/ticket/list.htm?keyword='+ str(place) +'®ion=&from=mpl_search_suggest&page={}'
i = 1
sightlist = []
while i:
page = getPage(url.format(i))
selector = etree.HTML(page)
print '正在爬取第' + str(i) + '页景点信息'
i+=1
informations = selector.xpath('//div[@class="result_list"]/div')
for inf in informations: #获取必要信息
sight_name = inf.xpath('./div/div/h3/a/text()')[0]
sight_level = inf.xpath('.//span[@class="level"]/text()')
if len(sight_level):
sight_level = sight_level[0].replace('景区','')
else:
sight_level = 0
sight_area = inf.xpath('.//span[@class="area"]/a/text()')[0]
sight_hot = inf.xpath('.//span[@class="product_star_level"]//span/text()')[0].replace('热度 ','')
sight_add = inf.xpath('.//p[@class="address color999"]/span/text()')[0]
sight_add = re.sub('地址:|(.*?)|\(.*?\)|,.*?$|\/.*?$','',str(sight_add))
sight_slogen = inf.xpath('.//div[@class="intro color999"]/text()')[0]
sight_price = inf.xpath('.//span[@class="sight_item_price"]/em/text()')
if len(sight_price):
sight_price = sight_price[0]
else:
i = 0
break
sight_soldnum = inf.xpath('.//span[@class="hot_num"]/text()')[0]
sight_url = inf.xpath('.//h3/a[@class="name"]/@href')[0]
sightlist.append([sight_name,sight_level,sight_area,float(sight_price),int(sight_soldnum),float(sight_hot),sight_add.replace('地址:',''),sight_slogen,sight_url])
time.sleep(3)
return sightlist,place
-
这里把每个景点的所有信息都爬下来了(其实是为了练习使用 xpath……)。
-
使用了 while 循环,for 循环的 break 的方式是发现无销量时给 i 值赋零,这样 while 循环也会同时结束。
-
地址的匹配使用 re.sub() 函数去除了 n 多复杂信息,这点后面解释。
为了防止代码运行错误,维护代码运行的和平,将输出的信息列表存入到 excel 文件中了,方便日后查阅,很简单的代码,需要了解 pandas 的用法。
def listToExcel(list,name):
df = pd.DataFrame(list,columns=['景点名称','级别','所在区域','起步价','销售量','热度','地址','标语','详情网址'])
df.to_excel(name + '景点信息.xlsx')
非常悲伤的,(ಥ﹏ಥ)我没找到去哪儿景点的经纬度,以为这次学(zhuang)习(bi)计划要就此流产了。(如果有人知道景点经纬度在哪里请告诉我)
但是,enhahhahahaha,我怎么会放弃呢,我又找到了百度经纬度 API。
网址:
http://api.map.baidu.com/geocoder/v2/?address=地址&output=json&ak=百度密钥
,
修改网址里的“地址”和“百度密钥”,在浏览器打开,就可以看到经纬度的 json 信息。
#上海市东方明珠的经纬度信息
{"status":0,"result":{"location":{"lng":121.5064701060957,"lat":31.245341811634675},"precise":1,"confidence":70,"level":"UNKNOWN"}}
百度密钥申请方法:
http://jingyan.baidu.com/article/363872eccda8286e4aa16f4e.html
这样我就可以根据爬到的景点地址,查到对应的经纬度辣!Python 获取经纬度 json 数据的代码如下:
def getBaiduGeo(sightlist,name):
ak = '密钥'
headers = {
'User-Agent' :'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
}
address = 地址
url = 'http://api.map.baidu.com/geocoder/v2/?address=' + address + '&output=json&ak=' + ak
json_data = requests.get(url = url).json()
json_geo = json_data['result']['location']
观察获取的 json 文件,location 中的数据和百度 API 所需要的 json 格式基本是一样,还需要将景点销量加入到 json 文件中,这里可以了解一下 json 的浅拷贝和深拷贝知识,最后将整理好的 json 文件输出到本地文件中。
def getBaiduGeo(sightlist,name):
ak = '密钥'
headers = {
'User-Agent' :'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
}
list = sightlist
bjsonlist = []
ejsonlist1 = []
ejsonlist2 = []
num = 1
for l in list:
try:
try