资讯专栏INFORMATION COLUMN

如何爬取外卖平台商家订单

wenhai.he / 3281人阅读

摘要:标签餐饮外卖,美团,饿了么,百度,爬虫,数据挖掘爬虫定时抓取外卖平台订单的解决方案想必很多人都在美团,饿了么,百度上点过外卖吧,每家平台都不定期的发力进行各种疯狂打折活动,好多人都是三个都安装的一起比价的策略。

标签:餐饮外卖,美团,饿了么,百度,爬虫,数据挖掘

爬虫定时抓取外卖平台订单的解决方案

想必很多人都在美团,饿了么,百度上点过外卖吧,每家平台都不定期的发力进行各种疯狂打折活动,好多人都是 三个app都安装的一起比价的策略。而作为大的餐饮企业为了扩大自己的订单量,也是三家都会上自己的商户,但是这 三家平台因为竞争的原因都不支持订单批量导出功能。这个爬虫程序就是这个原因而开发出来的。

想了解客户就要收集销售数据

定位客户,了解客户有很多种渠道,其中收集订单信息是比较客观的数据,我们能从中知道客户的年龄分布,地理位 置分布,喜欢的口味,消费的层次,购买套餐后还喜欢哪些单点等等问题都能逐渐积累的订单数据中挖掘出来, 刚开 始这项艰巨的工作是由运营的童鞋们开始的, 她们每天兢兢业业的Ctrl+C , Ctrl+V的拷贝下来百度,美团,饿了么 后台数据,然后Excel大神生成各种报表,供我们做分析。 但平淡的日子总是渐渐枯燥起来,随着订单越来越来,公 司配送点也越来越多, (三个外卖平台 +自有微信商城) X 配送点 X 每个配送点的订单的数据就是运营童鞋们的 噩梦

重复劳动就应该让机器去做

当运维童鞋正在苦逼复制各种订单数据时, 我已经想到用爬虫技术爬取外卖平台上的订单了, 这件事并不能,之前 学习Nodejs时候,还写过一个爬虫在@煎蛋爬取无聊图和美女图呢:>于是开始调研这三家外 卖平台的后台系统。

三家后台采用的页面技术

平台 后台展现 页面使用的数据接口 可能的抓取方案
美团外卖 网页 and 桌面程序 restful api 请求获取json 或者抓取网页
百度外卖 桌面程序内嵌webkit 动态页面 抓取网页
饿了么 桌面程序内嵌webkit restful api 请求获取json 或者抓取网页

其中百度外卖后台页面非常{{BANNED}},采用动态页面生成页面还能接受, 订单部分数据特意生成 一大段js代码,

由页面执行渲染后才显示出来,这也是后来在抓取时一个坑。

如何抓取数据

爬虫技术简单说就是用程序模拟人在上网,浏览需要的网页,然后把网页上需要的内容下载提取出来, 转换成结构 化的数据保存起来。这些外卖后台也是一样,基本上都如下面的流程。

人工操作流程

抽象出软件执行流程

三家外卖平台抓取的细节都不一样,但总体上可以用下面的方式表示

更细化一下的表示

核心代码为
/*  爬虫任务的父类
*   定义抓取流程,各步骤的内容
*   抽取出统一的json to csv生成代码
*/
class FetchTask {
    /*  account:{username:String,password:String}
        option:{beginTime:moment,endTime:moment}
    */
    constructor(account,option) {
        this.account = account;
        let end = moment().subtract(1,"days").endOf("day");
        let begin = moment().subtract(option.beforeDays, "days").startOf("day");
        logger.info(`Start fetch ${account.name} from ${begin.format("YYYY-MM-DD")} to ${end.format("YYYY-MM-DD")} orders`);
        this.option = {
            beginTime: begin,
            endTime: end
        };
        this.columns = {};
    }
    //  任务执行主方法 
    run() {
        return this.preFetch().then(this.fetch.bind(this)).then(this.postFetch.bind(this));
    }
    // 抓取前的准备工作
    preFetch() {
        logger.info(`preFetch ${this.account.name}`);
        return this.login();
    }
    // 保存登录凭证
    setToken(token){
        this.token = token;
        logger.info(`${this.account.name} gets token :${JSON.stringify(token)}`);
    }
    //  执行抓取
    fetch() {
        logger.info(`fetch ${this.account.name}`);
        return this.fetchPageAmount().then(this.fetchPages.bind(this));
    }
    //  登录步骤需要子类实现
    login() {
        return;
    }
    //  抓取分页总数
    fetchPageAmount(){
        return 0;
    }
    //  抓取所有分页上的数据
    fetchPages(pageAmount) {
        let tasks = [];
        for (let pageNum = 1; pageNum <= pageAmount; pageNum++) {
            tasks.push(this.fetchPage(pageNum));
        }
        return promise.all(tasks).then((result)=> {
            return _.flatten(result);
        });
    }
    //  抓取之后的操作,主要是对原始数据转换,格式转换,数据输出
    postFetch(orders){
        logger.info(`postFetch ${this.account.name}`);
        return this.convertToReport(orders).then(this.convertToCSV.bind(this));
    }
    //  原始数据格式转换
    convertToReport(orders){
        return orders;
    }
    //  在postFetch中将数据转换成csv格式并生成文件
    convertToCSV(orders) {
        logger.info(`convertToCSV ${this.account.name}`);
        let option = {
            header: true,
            columns: this.columns,
            quotedString: true
        };
        var begin = this.option.beginTime.format("YYYY-MM-DD");
        var end = this.option.endTime.format("YYYY-MM-DD");
        let reportFile = this.account.name + begin + "_" + end + "_" + uuid.v4().substr(-4, 4) + ".csv";
        let reportPath = path.resolve(__dirname, "../temp", reportFile);
        return new promise(function (resolve, reject) {
            stringify(orders, option, function (err, output) {
                if (err) {
                    reject(err);
                }
                fs.appendFile(reportPath, output, {
                    encoding: "utf8",
                    flag: "w+"
                }, function (err) {
                    if (err) return reject(err);
                    logger.info("Generate a report names " + reportPath);
                    resolve(reportPath);
                });
            });
        });
    }
}
module.exports = FetchTask;

每天凌晨6点钟自动执行抓取任务,定时执行是由later定时库实现的

const ElemeTask = require("./lib/eleme_task");
const BaiduTask = require("./lib/baidu_task");
const MeituanTask = require("./lib/meituan_task");
const mail = require("./lib/mail");
const logger = require("./lib/logger");
const promise = require("bluebird");
const moment = require("moment");
const config = require("config");
const accounts = config.get("account");
const later = require("later");

function startFetch() {
    let option = {beforeDays: 1};
    let tasks = [];
    accounts.forEach((account)=> {
        switch (account.type) {
            case "meituan":
                tasks.push(new MeituanTask(account, option).run());
                break;
            case "eleme":
                tasks.push(new ElemeTask(account,option).run());
                break;
            case "baidu":
                tasks.push(new BaiduTask(account,option).run());
                break;
        }
    });
    promise.all(tasks).then((files)=> {
        logger.info("Will send files :" + files);
        mail.sendMail(option, files);
    }).catch((err)=> {
        logger.error(err);
    });
}
later.date.localTime();
let schedule = later.parse.recur().on(6).hour();
later.setInterval(startFetch,schedule);
logger.info("Waimai Crawler is running");

按这个结构就是可以实现各个平台上的抓取任务了,因为不想把文章写成代码review,细节可以直接
访问waimai-crawler

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79815.html

相关文章

  • 机器学习在美团配送系统的实践:用技术还原真实世界

    摘要:可以说,美团要建设的就是配送系统的超级大脑。美团超脑配送系统目前互联网技术,很大部分还是针对线上产品和系统研发,整个流程可以在线上全部完成,而这也正是配送技术最大的不同和挑战。 在2018 AI开发者大会(AI NEXTCon)上,美团配送AI方向负责人何仁清,分享了美团在即时配送领域中机器学习技术的最新进展,以及如何通过大数据和机器学习手段,建立对线下真实世界各种场景的感知能力,还原...

    hearaway 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<