requests请求cookies本地持久化

作者:matrix 发布时间:2020年2月25日星期二 分类:Python

Python中单个地址进行请求我都是使用header的cookie中添加会话信息,简单干脆。但是多个域名跳转请求的时候就出现了问题,多域名的话需要按照域名host作为key来缓存,这岂不是很麻烦?

requests.Session()也很少使用,这次正好试试。requests.Session()可以作为全局变量来保存请求的cookies会话信息。这样在脚本的单次执行中可以很好的关联请求会话信息,避免要求多次登录的情况出现。

环境:Python 3.7

Session 单次会话

这里所谓单次会话其实就是单次运行脚本的一种效果
如果想要下次重新运行脚本依旧使用之前的cookies就必须要持久化处理。


import requests session = requests.Session() response1 = session.post('https://passport.baidu.com/v2/?login&tag=hhtjim.com') response2 = session.get('http://www.baidu.com/?tag=pang)

response1请求会返回Set-Cookie的响应头,Session会记录Set-Cookie的值然后在response2中携带Cookie的请求头。这些都是会话处理的效果,也就是requests自动完成。这样如果response1登录成功,则后续请求就可以直接进行,避免手动携带Cookie

Session本地持久化

现在需求是本地保存cookies信息,避免重新执行脚本的时候还要求登录。
本来没找到现成的方法只能自己序列化存储cookies数据,然后载入的时候反序列化就好了。但是后面看到http.cookiejar.MozillaCookieJa1这些函数可以处理目前的问题。

import requests,os
http.cookiejar import MozillaCookieJar


session = requests.Session() #作为全局变量使用

#载入cookies
path = 'cookies.txt' #设置cookies文件保存路径
s = MozillaCookieJar(path)
os.path.isfile(path) and s.load(path, ignore_discard=True, ignore_expires=True)#存在文件则载入
session.cookies = s #使用MozillaCookieJar进行会话管理   

response1 = session.post('https://passport.baidu.com/v2/?login&tag=hhtjim.com')

#触发保存会话到本地文件
session.cookies.save(ignore_discard=True, ignore_expires=True) 

response2 = session.get('http://www.baidu.com/?tag=pang)

上面操作就可以实现本地持久化存储,如果过期则会自动使用过期的Session请求续签。相对于单次会话其实就多了load和save操作,知道这基本原理也能够自己实现。
⚠️注意:
ignore_discard=True参数确保有开启,否则使用save方法不会保存到本地,load()处也是一致,避免无法读取。

如果想要清空会话使用clear()方法即可,再save()方法执行文件保存。

Note that the save() method won’t save session cookies anyway, unless you ask otherwise by passing a true ignore_discard argument.

参考:

https://stackoverflow.com/questions/13030095/how-to-save-requests-python-cookies-to-a-file

https://zhuanlan.zhihu.com/p/42950252


  1. CookieJar,LWPCookieJar都有实现save方法进行会话保存 

auto.js自动化处理插件 - 葡萄浏览器多账号签到

作者:matrix 发布时间:2020年2月11日星期二 分类:零零星星

这些天在家研究autojs操作,完全可以用它来模拟手动点击滑动app的各种操作,这个自动化是真的香~
ios端的Jsbox没这么友好,毕竟系统完全不同,也有很多限制。

https://hyb1996.github.io/AutoJs-Docs

目前要使用多个账号登录,然后进行点击签到,看广告的需求。

环境:mi8 Android9 AutoJs[4.1.1] 葡萄浏览器com.qwh.grapebrowser

AutoJs在应用市场都被下架了,留个下载的渠道:

https://github.com/SuperMonster002/Hello_Sockpuppet/raw/master/%5Bauto.js%5D%5B4.1.1_alpha2%5D%5Barm-v7%5D(b69a4e23).apk

https://www.hhtjim.com/wp-content/uploads/2020/02/auto.js4_.1.1_alpha2arm-v7b69a4e23.apk_.zip

鼓捣了几天,差不多完工。测试的app是葡萄浏览器com.qwh.grapebrowser,最终效果是运行后自动切换账号来签到。
第一次编写的确是各种懵逼,为啥子不动了,日志在哪里怎么看,每次都手动复制代码到手机好麻烦。。。

开发环境vscode

vscode插件来开发很方便。如果没有开发调试环境,我每次都要微信发送代码的消息,toast,再上app清空粘贴运行,累死。给hyb1996好评,相当不容易。

  1. command + shift + p运行Start Server命令来开启远程环境。

  2. auto.js APP设置里打开连接电脑,填入电脑端内网ip。当然手机和电脑是要一个内网环境

图片5130-auto.js插件 - 葡萄浏览器多账号签到

  1. 运行Run命令就可以在app端运行插件脚本。注意默认模式需要打开在Auso.js界面中启动。

开发

文档:

https://hyb1996.github.io/AutoJs-Docs/

要操作的控件基本信息都需要用 autojs app的悬浮窗设置里面找到分析

"auto"
console.show();//开启log窗口

//等待打开app
launchApp('葡萄浏览器');
waitForPackage("com.qwh.grapebrowser");

const LOGIN_ACTIVITY = 'com.qwh.grapebrowser.mvp.base.login.LoginActivity';
const MAIN_ACTIVITY = 'com.qwh.grapebrowser.mvp.navigation.NavigationActivity';
const SETTING_ACTIVITY = 'com.qwh.grapebrowser.ui.main.activity.mineview.SystemSettingActivity';
const BASEDIALOG = 'com.qwh.base.BaseDialog';

var  iv_cancel = id("iv_cancel"); //粘贴板搜索窗口的取消按钮
var ad = id('skip_view');//跳过广告按钮
var my = id("ll_mine");//我的按钮
var sign = id("iv_sign");//签到图标
var sign_in = id("iv_sign_in");//签到按钮
var profile_photo = id("iv_pic").className("android.widget.ImageView").clickable(true);//头像
var exit_btn = id('ll_exit');//退出登录的按钮. 不可点击 只能获取坐标来操作
var exit_ac = id('tv_dialog_message_confirm'); //同意执行退出
var login_btn = id('tv_login');//登录按钮 

var accounts = {
    "131***1110": "123123"
};

cance_list = [ad , iv_cancel];

//监听弹窗事件(异常界面处理)
threads.start(function(){
    while(true){
        sleep(100);
        for (i in cance_list){
            //toast(cance_list[i]);
            if(cance_list[i].exists()){
                cance_list[i].click();
            }
        }

    };
});

//监听音量下键
threads.start(function(){
  isKeyDown();
})

//id("iv_pic").className("android.widget.ImageView").clickable(true)  .findOne().click();


//根据控件布局点击
function click_btn_func (widget_list,time ){
  if(widget_list instanceof Array){
    for(var i in widget_list){
      var widget = widget_list[i];
      if(widget.exists()){
        if(time){
          while(!widget.findOne(time).click());
        }else{
          while(!widget.findOne().click());
        }

        // if(widget_list[i].clickable()){//可点击判断  测试没有用。
        //   toast('aaaa');
        // }
      }
    }
  }else{
    if(time){
      while(!widget_list.findOne(time).click());
    }else{
      while(!widget_list.findOne().click());
    }
  }
}

//根据坐标点击控件
function click_coord_func(widget_list){
  if(widget_list instanceof Array){
    for(var i in widget_list){
      let widget = widget_list[i];
      if(widget.exists()){
        widget = widget.findOne();
          //获取其中心位置并点击
        while( !click(widget.bounds().centerX(), widget.bounds().centerY()) );
      }
    }
  }else{
    let widget = widget_list.findOne();
          //获取其中心位置并点击
    while( !click(widget.bounds().centerX(), widget.bounds().centerY()) );
  }
}

// 我的界面中判断是否登录
function is_login_of_my_activity(){
    return !!profile_photo.exists();
}

function logout(){
  console.log('function: logout')

  //已经进入我的界面
    // waitForActivity(MAIN_ACTIVITY);//入口主界面 activity
  if(is_login_of_my_activity()){//如果已登录,则点击退出
    console.log('发现有登录账号')
    //切记等待界面响应
    click_btn_func([profile_photo]);
    waitForActivity(SETTING_ACTIVITY);//等待界面响应
    click_coord_func([exit_btn]);
    waitForActivity(BASEDIALOG);
    console.log('返回到登录界面');
    click_btn_func([exit_ac]);
  }else{//已经是退出了 进入登录界面
    console.log('发现已经退出');
      //我的界面点击登录按钮操作
  // if(id('tv_btn').exists()){
  //   .findOne().click();
  // }
  console.log('查找「登录/注册」按钮 进入登录界面')
  click_btn_func( id('tv_btn'),4000);
  }
}

function login(phone,pwd){
  //已进入登录界面
    waitForActivity(LOGIN_ACTIVITY);
    setText(0,phone);
    setText(1,pwd);
  click_btn_func([login_btn]);
  console.log('登录完成')
}

//检测是否点击了音量下键的方法
function isKeyDown(){
  //按键时不会弹出音量框
  events.setKeyInterceptionEnabled("volume_down",true);
  //监听按键
  events.observeKey();
  events.on("key_down",function(volume_down,event){
      toast("正在关闭此脚本");
      //退出脚本
      sleep(500);
      engines.stopAll();
      //直接返回桌面
      home();
  });
}

//每秒执行检测操作 超时退出
function await_callback(callback,timeout_second){
  let _n = 0;
  while(true){
    if(_n > timeout_second){// 最长等待n秒检测
      console.log('await_callback最长等待超时 break')
      break;
    }
    if(callback()){//执行回调 返回true
      return true;//返回true标记给外部
      // break;
    }
    sleep(1000);//步长 1秒
    _n++;
  }
  return false
}

console.log('查找关闭按钮');
await_callback(function(){
    if(className("android.widget.ImageView").depth(5).bounds(55,130,137,212).exists()){
        className("android.widget.ImageView").findOne().click();
        console.log('完成 关闭广告按钮');
        return true
    }
    return false
},3);

  //确定图片 领取报销额度
  console.log('等待界面 准备领取');
  // waitForActivity('com.qwh.grapebrowser.dialog.s');//等待领取报销额度的页面
  var pull_down = await_callback(function(){
    if(className("android.widget.FrameLayout").exists()){
      console.log('点击确定FrameLayout 领取');
      click_coord_func(className("android.widget.FrameLayout"));
      return true;
        }
        return false;
  },30);

console.log('done');

说明:
上面成品修改下账户密码可以直接运行。
需要注意的是代码中很多时候卡住不动都需要判断控件或者按钮是否存在,否者findOne()会一致做轮询。
用log输出,查看运行状态
有些控件无法触发click()方法,需要查看其clickable属性是否为true,意即为是否可点击
不能正常按照控件布局点击的话只能用坐标进行点击操作

安卓是真的香,以后想拥有一台一加😂

参考:

https://hyb1996.github.io/AutoJs-Docs

https://github.com/SuperMonster003/auto.js_Projects/tree/Ant_Forest

https://www.jianshu.com/p/d2ad3ce9cf87

https://thbelief.coding.me/2019/04/02/Auto-js-Script%E5%BC%80%E5%8F%91-%E4%BA%8C/

https://www.autojs.org/

https://www.autojs.org/topic/1651/%E5%BD%93%E6%8E%A7%E4%BB%B6-clickable-false-%E6%97%B6-%E6%80%8E%E4%B9%88%E8%BF%9B%E8%A1%8C%E7%82%B9%E5%87%BB%E6%88%96%E8%80%85%E8%A7%A6%E6%91%B8

PEACE~

ifttt推送gmail新邮件提醒

作者:matrix 发布时间:2020年2月8日星期六 分类:零零星星

今天临时想用ifttt来发送新邮件提醒服务的推送,查了下才知道19年初google就取消了对ifttt的api调用。不过看到可以使用其他三方应用来解决这个问题。

目的:新gmail邮件->tg消息通知

需要Gmail,IFTTT,automate.io,Trello,telegram相关账户

方法

类似于IFTTT的automate.io可以设置处理条件,自由度也更高,可以接收处理gmail邮件,但是没找到telegram私有频道的接收app。但是可以用trello来中转达到转发效果。

调用链形式:gmail->automate->Trello->ifttt->telegram

绑定授权的automate.io应用

设置automate.io,进入app界面 授权gmail和Trello应用。注意授权gmail时要选着权限更高的gmail app要不然没有权限读取邮件。

图片5117-ifttt推送gmail新邮件提醒

添加Trello相关List用于接收邮件

  • 在Trello中添加私有看板boards命名为gmail

    图片5118-ifttt推送gmail新邮件提醒

  • 进入gmail看板再添加一个列表List命名为新邮件提醒用来接收邮件

设置automate.io BOT

创建bot

  • 添加gmail触发器

    触发应用Trigger app选择绑定了的Gmail,操作应用Action app选择Trello。
    触发事件Event选择新New EmailLable为Gmail邮件的分类,我这里选择INBOX,也就是所有收件箱。还可以设置Search Filter达到关键字过滤的效果。

  • 添加Trello操作

    选择Add or Update a Card事件,Board和List选择Trello中创建好的名称,gmail和新邮件提醒。title中设置显示的标题[From Email] - [From Name] - [Subject],
    Description描述我这里写入邮件的url https://mail.google.com/mail/u/0/#inbox/[Message ID],方便tg中点击查看。
    其他还有时间,标签的配置都可以自定义。
    如果想要预览邮件内容的话再添加个Trello的Add a Comment操作。

    个人版本不能分享bot,可以参照下面图片配置:

    图片5120-ifttt推送gmail新邮件提醒

automate.io的配置自由度很高,甚至都有存在or不存在的逻辑判断,真的好方便。免费版应该有很多限制,不过足够个人使用了。
这里添加完成之后进行automate.io的BOT测试,发送个邮件,如果Trello的gmail看板中有邮件就算成功一半。

每次有新邮件,让系统自动添加Card到List就好了。邮件标题和email等基本信息作Card名,方便可读。

图片5119-ifttt推送gmail新邮件提醒

上面就是接收新邮件到Trello的List的效果

设置IFTTT

到这里完成最后一步的IFTTT设置就可以把Trello的新Card发送到TG私有频道。如果是第一次使用IFTTT的tg频道的话还需要去关联

  • 设置This为Trello,之后选择Card added to list事件触发

  • 选择看板board,默认个人私有看板是My Boards

  • List name填入新邮件提醒

  • 选择IFTTT的thatTarget chat 选择绑定的tg ifttt频道。

  • 之后再设置tg频道的message消息内容格式

    GMAIL新邮件提醒: <a href="{{Description}}">{{Title}}</a><br>
    <a href="{{CardURL}}">View on Trello</a>
    

实现接收gmail提醒的方式还有很多路径,类似于automate的还可以用Zapier

参考:

Gmail is being removed from IFTTT from ifttt

https://if404.com/archives/2019/04/09/use-ifttt-gmail-dropbox-auto-push-ebook-to-kindle