1.引言
六只脚平台是一个专门用于旅游数据收集和分享的在线平台,游客可以在平台上记录自己在岳麓山的游览轨迹,并分享给其他用户。六只脚是国内著名的户外网站,拥有大量的户外GPS轨迹路线,网址为:http://www.foooooot.com/。
六只脚平台可以通过关键词搜索各个游客户外GPS轨迹路线,数据包含了一些列坐标点信息,速度,海拔位置等丰富信息,提供了丰富数据可视化结果。
因此本文章将以岳麓山为例子,对六只脚平台的页面构成等进行分析,然后使用网络爬虫技术从六只脚平台上爬取岳麓山相关的游客轨迹数据。通过编写爬虫程序,可以自动化地访问平台上的数据,并将其保存到本地供后续处理和分析。
2.数据分析
2.1获取所有轨迹
在登录的情况下当我们在六只脚平台搜索关键词“岳麓山”,将可以看到各个用户所发布的关于岳麓山的轨迹数据。
可以发现,每页具有三十个轨迹记录,观察第二页的网址我们可以发现网址为:http://www.foooooot.com/search/trip/all/1/all/time/descent/?page=2&keyword=%E5%B2%B3%E9%BA%93%E5%B1%B1。
通过对六只脚平台的网址进行分析,不难发现其规律:page为页数、keyword为岳麓山的转义。当我们测试page为600的情况,在浏览器输入:http://www.foooooot.com/search/trip/all/1/all/time/descent/?page=600&keyword=%E5%B2%B3%E9%BA%93%E5%B1%B1,显示没有相关行程。
综上所述,在六只脚平台,如果我们想要获取岳麓山游客轨迹数据,我们可以通过不断增加page的数字,直至某一页不满足三十个轨迹记录,获取该关键词所有的轨迹记录。
2.2获取轨迹ID
点击获取的岳麓山某个具体的轨迹详情,可以看到每一页具体的轨迹页面的网址是由轨迹ID构造的,诸如:http://www.foooooot.com/trip/315172/。
从刚才的岳麓山轨迹数据列表界面我们就可以找到每个轨迹ID。
2.3获取轨迹数据
打开谷歌浏览器控制台(按F12),点击到网络记录界面,刷新网址。
从网络请求记录中我们发现有XHR异步请求其名字很像轨迹数据,点开查看。可以看到,这个trackjson就是轨迹的JSON数据。
2.4字段解释
对于trackjson,前三列个数据项可以快速判断为时间戳和经纬度,对于后面三个数据项,结合网页数据,可以知道后三列个数据项分别为高程,速度和里程。
3.数据爬取
3.1数据爬取程序
经过上面的数据分析,爬取轨迹数据主要就是通过page和keyword构造网址获取轨迹ID,通过轨迹ID构造地址获取trackjson。因此基于Python,使用requests库发送http请求,使用Xpath解析界面提取数据。由于网站设置了简易反爬措施,设置'user-agent'进行访问。发起请求设置休眠时间,避免瞬间的大量访问请求会导致网站崩溃。通过组合和调用这些部分,实现了从六只脚平台爬取岳麓山游客轨迹数据的功能。
引入库
import re
import os
import json
import time
import requests
import pandas as pd
from lxml import etree
from fake_useragent import UserAgent
首先,主程序入口:通过if __name__ == "__main__":判断代码是否作为主程序运行。在主程序中,用户输入搜索关键字和开始爬取的页面,创建保存轨迹数据的文件夹,并设置登录的URL和请求头信息。然后调用get_csrfmiddlewaretoken()函数开始执行数据爬取的流程。
if __name__ == "__main__":
keyword = input("请输入一个搜索关键字:")
page_start = int(input("请输入开始爬取的页面:"))
if not os.path.exists(f"{keyword}轨迹"):
os.mkdir(f"{keyword}轨迹")
url = "http://www.foooooot.com/accounts/login/"
headers = {
"User-Agent": UserAgent().random,
}
get_csrfmiddlewaretoken()
其次,get_csrfmiddlewaretoken():获取CSRF中间件令牌。通过发送请求获取网页内容,使用正则表达式提取CSRF令牌,并调用get_sessionid()函数。
def get_csrfmiddlewaretoken():
res = requests.get(url, headers=headers)
reg = re.compile(
r"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='(.*?)' /></div>"
)
csrf = reg.findall(res.text)[0]
get_sessionid(csrf)
再者,get_sessionid(csrf):获取会话ID。设置请求头中的Cookie信息,使用提取的CSRF令牌和用户凭据进行登录请求,并调用get_data(session)函数。
def get_sessionid(csrf):
headers["Cookie"] = f"csrftoken={csrf}"
data = {
#email和password替换成登录六只脚网站的账号密码
"csrfmiddlewaretoken": csrf,
"email": "16670",
"password": "324",
"next": "/accounts/login_complete/",
}
res = requests.post(url, headers=headers, data=data)
session = res.request.headers["Cookie"]
get_data(session)
然后,get_data(session):获取数据。根据给定的页面范围和关键字,循环发送请求获取数据,并处理可能的错误和异常情况。
def get_data(session):
for page in range(int(page_start), 10000):
while True:
try:
url = "http://www.foooooot.com/search/trip/all/1/all/occurtime/descent/"
headers["Cookie"] = session
params = {
"page": page,
"keyword": keyword,
}
res = requests.get(url, headers=headers, params=params)
if res.status_code == 200:
get_id(res.text)
break
else:
reply = int(input("返回数据错误,去官网判断账号是否存在异常(续爬按1/退出按2):"))
if reply == 1:
pass
else:
return
except:
reply = int(input("请求数据错误,去官网判断ip是否存在异常(续爬按1/退出按2):"))
if reply == 1:
pass
else:
return
print(f"page{page} is over!!!")
time.sleep(5)
之后,get_id(res):获取轨迹ID。从网页响应中提取轨迹ID列表,通过访问特定的URL获取轨迹数据,并调用save_data(track_num_arr, trackjsons)函数。
def get_id(res):
tree = etree.HTML(res)
trip_list = tree.xpath('//p[@class="trip-title"]/a/@href')
track_num_arr = [trip.split("/")[2] for trip in trip_list]
trackjsons = []
for track_num in track_num_arr:
trackjson_url = f"http://www.foooooot.com/trip/{str(track_num)}/trackjson/"
track_list = requests.get(trackjson_url, headers=headers).text
trackjson = json.loads(track_list)
trackjsons.append(trackjson)
save_data(track_num_arr, trackjsons)
最后,save_data(track_num_arr, trackjsons):保存数据。将轨迹数据保存到CSV文件中,并打印处理完成的消息。
def save_data(track_num_arr, trackjsons):
for track_num, trackjson in zip(track_num_arr, trackjsons):
df1 = pd.DataFrame(trackjson)
df1.to_csv(
f"./{keyword}轨迹/{str(track_num)}轨迹.csv",
mode="a",
header=False,
index=False,
encoding="utf-8-sig",
)
print('\r' + f"{track_num} is over!!!", end="")
综上,通过以上代码即可从六只脚平台上爬取岳麓山相关的游客轨迹数据,但是要注意的是所爬取的代码必须进行清洗,再进行分析操作,完整代码如下。
import re
import os
import json
import time
import requests
import pandas as pd
from lxml import etree
from fake_useragent import UserAgent
def get_csrfmiddlewaretoken():
res = requests.get(url, headers=headers)
reg = re.compile(
r"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='(.*?)' /></div>"
)
csrf = reg.findall(res.text)[0]
get_sessionid(csrf)
def get_sessionid(csrf):
headers["Cookie"] = f"csrftoken={csrf}"
data = {
"csrfmiddlewaretoken": csrf,
"email": "16670",
"password": "324",
"next": "/accounts/login_complete/",
}
res = requests.post(url, headers=headers, data=data)
session = res.request.headers["Cookie"]
get_data(session)
def get_data(session):
for page in range(int(page_start), 10000):
while True:
try:
url = "http://www.foooooot.com/search/trip/all/1/all/occurtime/descent/"
headers["Cookie"] = session
params = {
"page": page,
"keyword": keyword,
}
res = requests.get(url, headers=headers, params=params)
if res.status_code == 200:
get_id(res.text)
break
else:
reply = int(input("返回数据错误,去官网判断账号是否存在异常(续爬按1/退出按2):"))
if reply == 1:
pass
else:
return
except:
reply = int(input("请求数据错误,去官网判断ip是否存在异常(续爬按1/退出按2):"))
if reply == 1:
pass
else:
return
print(f"page{page} is over!!!")
time.sleep(5)
def get_id(res):
tree = etree.HTML(res)
trip_list = tree.xpath('//p[@class="trip-title"]/a/@href')
track_num_arr = [trip.split("/")[2] for trip in trip_list]
trackjsons = []
for track_num in track_num_arr:
trackjson_url = f"http://www.foooooot.com/trip/{str(track_num)}/trackjson/"
track_list = requests.get(trackjson_url, headers=headers).text
trackjson = json.loads(track_list)
trackjsons.append(trackjson)
save_data(track_num_arr, trackjsons)
def save_data(track_num_arr, trackjsons):
for track_num, trackjson in zip(track_num_arr, trackjsons):
df1 = pd.DataFrame(trackjson)
df1.to_csv(
f"./{keyword}轨迹/{str(track_num)}轨迹.csv",
mode="a",
header=False,
index=False,
encoding="utf-8-sig",
)
print('\r' + f"{track_num} is over!!!", end="")
if __name__ == "__main__":
keyword = input("请输入一个搜索关键字:")
page_start = int(input("请输入开始爬取的页面:"))
if not os.path.exists(f"{keyword}轨迹"):
os.mkdir(f"{keyword}轨迹")
url = "http://www.foooooot.com/accounts/login/"
headers = {
"User-Agent": UserAgent().random,
}
get_csrfmiddlewaretoken()
3.2数据爬取验证
数据爬取验证通过设置关键为杭州西湖、从第2页开始爬取,爬取30页,可以得到870条关于杭州西湖的轨迹数据。