puppeteer搭建代理转发请求
使用方法
tnpm install -g @ali/tuzki tuzki set -a 你的账号 tuzki set -p 你的密码 tuzki start
tuzki set
-a --account ,设置域账号
-p --passowrd ,设置域账号密码
tuzki config
输出当前配置的域账号和密码
tuzki start
运行代理,默认是 5000 端口,如果你在 start 后指定了一个端口则会在指定端口运行
tuzki start -p 9000 // 在9000端口运行
开发过程
koa
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
当然,这段话是我抄的。https://koa.bootcss.com/
puppeteer
Puppeteer是一个Node库,它提供了高级API来通过DevTools协议控制Chrome或Chromium 。
当然,这句话也是我抄的。https://github.com/puppeteer/puppeteer。简单的说就是Puppeteer提供了一个可供node环境使用的一个无界面、代码控制的谷歌浏览器。可以通过调用api来实现一个用户可能有的所有操作。说实话我用完以后觉得这个拿来当爬虫用真的杠杠的。。
方案
基本是围绕着怎么生成cookie,怎么请求接口进行。
让puppeteer代替用户登陆平台,请求接口,拿到响应以后再用koa响应本地的前端请求。emm,大概就是下面这条线。
代码实现
代码实现思路大致分两部分,第一部分是让koa去识别和响应前端请求,第二部分是puppeteer收到koa拆解出协议和参数后,收发请求。
koa部分
用koa生成一个http server,开始监听某个端口的请求。这里使用了koa-log4帮助记录日志。
const Koa = require("koa"); const log4js = require("koa-log4"); const logger = log4js.getLogger("app"); const { PORT } = require("./config"); require("../log"); const bodyParser = require("koa-bodyparser"); const { instance: lubanProxy } = require("./lubanProxy"); async function server() { // init app & proxy const app = new Koa(); app.use(bodyParser()); app.use(log4js.koaLogger(log4js.getLogger("http"), { level: "auto" })); await lubanProxy.loginLuban(); // add logger to a request app.use(async (ctx, next) => { const start = new Date(); await next(); const ms = new Date() - start; logger.info(`${start} ${ctx.method} ${ctx.url} - ${ms}ms`); }); // send request to lb-test app.use(async ctx => { if (ctx.method === "GET") { let res = await lubanProxy.get(ctx.url); ctx.body = res; } else if (ctx.method === "POST") { let res = await lubanProxy.post(ctx.url, ctx.request.body); ctx.body = res; } }); app.on("error", (err, ctx) => { logger.error("server error", err, ctx); }); app.listen(PORT); } server();
puppeteer部分
生成一个浏览器,登陆平台,向koa提供get和post方法。
登陆部分
模拟真实用户的登陆操作即可。
loginLuban = async () => { this.browser = await puppeteer.launch(); const page = await this.browser.newPage(); await page .goto( "https://login.abc.com/login.html“ ) .catch(async e => { await page.waitFor(2000); logger.error(`load login page timeout,retrying...`); }); await page .evaluate(() => { document.querySelector("input[name='account']").focus(); }) .catch(async _ => { logger.error(`Cannot get login form`); }); await page.keyboard.type(ACCOUNT); await page .evaluate(() => { document.querySelector("input[name='password']").focus(); }) .catch(async _ => { logger.error(`Cannot get login form`); }); await page.keyboard.type(PASSWORD); await Promise.all([page.waitForNavigation(), page.click(".submit")]) .then(async () => { await page.goto("http://luban.abc.net/manage.htm"); console.log("proxy ready..."); }) .catch(async _ => { logger.error( `Navigation timeout of 30000 ms exceeded,restarting browser...` ); await this.restartBrowser(); }); };
get请求
puppeteer只能用来模拟用户操作,无法自如的发送ajax,所以get请求的思路是用page.goto来模拟。
get = async url => { const page = await this.browser.newPage(); let res = ""; await page .goto("http://luban.abc.net" + url) .then(async () => { res = await page.content(); res = res.slice(res.indexOf("{"), res.lastIndexOf("}") + 1); }) .catch(_ => { logger.error(`get request got net::ERR_CONNECTION_RESET error`); res = { code: -1, message: "Got a net error, try again?" }; }); await page.close(); return res; };
post请求
post请求比get复杂一些,因为get可以通过页面跳转,而post无法模拟。
/** * 用注入一段js的方式发送post请求 * 打开lb-test jarvis的index页面,同时注入一段js代码,将参数填充进js代码段中, * 将接口返回的内容写入div#root的innerHTML中,wait()接口请求时间后,获取div#root的innerHTML即为接口返回内容。 * 为每个请求单独开辟一个标签页,防止上一个请求没结束时即被新的请求覆盖, * 当请求结束时主动关闭标签页,释放占用的内存 * 若此过程成功,则返回json字符串,若失败,返回失败原因 * @param {string} url 请求的地址 * @param {object} param 请求的参数 * @return {string} json字符串 */ post = async (url, param) => { const page = await this.browser.newPage(); let res = ""; await page.goto( "http://lb-test.alibaba.net/project/jarvis/page/index.html" ); await page.addScriptTag({ content: ` $.post('http://lb-test.alibaba.net'+'${url}', {data: ${JSON.stringify( param.data )}}, function (result) { document.querySelector('#root').innerHTML = JSON.stringify(result) });` }); await page.waitFor(500); res = await page.evaluate(() => { return document.querySelector("#root").innerHTML; }); await page.close(); return res; };
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Service 的两种启动方式和「Service 与 Activity 数据交互」
1. Service 的两种启动方式 Activity 中可以有两种方式启动 Service,不同方式启动时 Service 的生命周期也不一样,现在在 Activity 中定义四个 Button,分别是 startService、stopService、bindService、unbindService,Service 中各生命周期中分别打印 Log 日志,通过日志查看生命周期执行情况: // MainActivity.kt class MainActivity : AppCompatActivity(){ var mService: MyService? = null var isBind = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val intent = Intent(this, MyService::class.java) val conn = obje...
- 下一篇
作业帮基于 Apache Doris(Incubating) 的数仓实践
【本文经作者授权转载,原则作者 糜利敏,联系方式见文章末尾】 关于 Apache Doris(Incubating) Apache Doris(Incubating) 一款基于大规模并行处理技术的交互式SQL分析数据库,由百度于2018年贡献给 Apache 基金会,目前在 Apache 基金会孵化器中。 Github:https://github.com/apache/incubator-doris,欢迎大家 Star、提 Issue、Pull Request。 官方网站:http://doris.incubator.apache.org/ 可以查看更多安装、部署、使用文档,也欢迎对文档内容进行校对或建议。 开发者邮件列表:dev@doris.apache.org,(如何订阅请戳这里) 背景 作业帮大数据团队主要负责建设公司级数仓,向各个产品线提供面向业务的数据信息,如到课时长、答题情况等,服务于拉新、教学、BI等多个重要业务线。在过去数月内,我们通过对Doris的应用实践,构建了数仓实时查询系统。本文总结并分享下期间的工作内容,也欢迎大家一起讨论。 典型的数仓从逻辑上划分为: 大数...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS关闭SELinux安全模块
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- 设置Eclipse缩进为4个空格,增强代码规范
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7安装Docker,走上虚拟化容器引擎之路