采集流程分析
经分析,采集流程可以简化为如下:
1. 国外城市列表
https://m.ctrip.com/webapp/hotel/j/hoteldetail/dianping/api/static/city?oversea=true&index=H
JSON格式返回,如下示例,可以通过index参数遍历A-Z获取所有国外城市。
2. 根据城市信息构造出城市下酒店列表的首页链接:http://hotels.ctrip.com/international/washington26363
http://hotels.ctrip.com/international/ + seo(去掉括号及内部内容) + cityID
下载所有列表分页。从酒店列表页获取酒店详情页链接和酒店最低价。
3. 进入酒店详情页提取字段:
例如http://hotels.ctrip.com/international/2081845.html(去掉多余参数)
详情页面源码内有如下有用信息(下简称JSON信息),下面提取字段的时候有些可以直接用上。
采集程序编写
上面分析了基本的采集流程,下来着手编写程序,如果遇到问题就边写边调试吧。
程序开头定义好数据库相关信息:
# 连接数据库
client = pymongo.MongoClient()
# 指定数据库
db = client['数据库名']
# 指定集合
collection = db['集合名']
# 给url和酒店id字段创建索引
collection.ensure_index([('hotel_id', pymongo.ASCENDING)])
collection.ensure_index([('url', pymongo.ASCENDING)])
在程序编写、调试运行过程中,就发现网站的反采集策略做的十分严格,多线程也不能很奏效的快速采集到数据,那在这就采用多进程结合多线程方法进行采集了。
要注意的是进程的数量要合理,过多就会大量消耗系统内存导致系统卡死。下面就直接给出写好的多进程代码块,主要注释都写在程序里了:
def start_multiple_procs():
"""启动多进程
"""
# 创建多进程
procs = []
# 启动采集进程
cmd_args = ['python', 'ctrip_aa.py']
# 遍历所有国外城市列表 - 把每4个字母对应的链接加入到一个进程里,然后把进程加入进程列表里
inti_tasks = []
for cha in string.ascii_uppercase:
link = 'https://m.ctrip.com/webapp/hotel/j/hoteldetail/dianping/api/static/city?oversea=true&index={}'.format(cha)
inti_tasks.append(link)
procs_num = int(math.ceil(float(len(inti_tasks))/4))
# 得到进程总数,遍历进程数, 每个进程中加入任务列表
for num in range(0, procs_num):
proc_num = inti_tasks[num*4:num*4+4]
procs.append(subprocess.Popen(cmd_args + proc_num))
# 生成结束所有客户端进程脚本
with open('kill-clients.sh', 'w') as f:
f.write('kill -9 %s' % ' '.join([str(proc.pid) for proc in procs]))
os.system('chmod +x kill-clients.sh')
# 等待各子进程结束
common.logger.info('Waitting for all subprocesses(%s) to finish.' % len(procs))
for proc in procs:
proc.wait()
common.logger.info('All subprocesses have stopped.')
调试过程中发现pc端的酒店列表分页有显示数量限制,而移动端的没有。但是移动端页面列表加载是通过Ajax方法请求得到的。使用Fiddler辅助分析下,我们能找到正确的请求url,headers, data。
通过上述代码能尽可能全面的采集到国外酒店详情页链接。下来就要在详情页提取字段信息了,提取字段方法单独写一个模块,方便调试。
在详情页存在的难点也就是几处需要通过Ajax方法加载的数据,好在我们都可以通过Fiddler一点点拨开迷雾,得到想要的数据。
写在最后
本项目采集程序写完了,要多进程运行程序的话,我们要正确添加参数,程序文件是ctrip_aa.py,启动如下:
if__name__=='__main__':
if'--test'insys.argv:
test()
elif'--start_multiple_procs'insys.argv:
start_multiple_procs()
启动:python ctrip_aa.py --start_multiple_procs
领取专属 10元无门槛券
私享最新 技术干货