之前学习了一些python的爬虫技术... 已经可以通过python来水blog的阅读量了 你知道的太多了, 然而你看我这个blog惨不忍睹的访问量, 有人吗? 有人吗?

今天突然又双叒叕心血来潮想写一个poj的自动提交脚本(其实已经觊觎各大oj好久了)..

本来是想选bzoj的, 但是不知道用了什么黑科技 requests会404.. 加header好像也没用... 不知道浏览器怎么做到的..

所以还是选poj吧... 不过poj的结构做得也是很清晰(就鬼咯)

因为几乎是第一次开发这种东西, 所以不免有些不成熟的地方.. 但是管它呢, 能用就行呗

所以也想分享一下自己做这东西的全过程, 来记录自己的成长, 或许可能的话还能帮一些跟我一样的萌新解决一点问题.. 那我一定会很欣慰的... 而且自己并没有系统学过python或者前端的知识, 全靠在网上吃百家饭, 如果有什么高明的见解, 欢迎提出哟~

这里采用的是requests和BeautifulSoup4, 都是直接pip安装的, 大约都是最新版吧(萌新不懂...

这两个东西的教程网上也有不少, 官方文档(的翻译)大约也是可以搜得到的...

这里因为自己也没完全学完, 所以就不详细讲了(说了能用就行嘛)

然后我们想提交肯定要登录啊对吧, 那我们就观察一下登录要做些什么...

想要查看登录需要一些什么东西, 我们需要抓包, 而最简单的方式就是采用自己的浏览器辣~

我们可以打开自己的Chorme(Firefox什么的应该也可以, 这里用的是装虚拟机里面自带的Chormium..)

我们就利用一个新注册的测试账户"fk_poj"来进行实验.

我们先输入账号和密码, 别急着点login!!, 然后按F12, 打开开发人员工具界面. 大约像这样:



然后我们看到顶上的选项卡, 选NetWork..



现在这里应该就一句话, 没显示什么东西..

这样我们再点击login, 看到这里多了一堆东西, 有一个叫login的, 点它左边的小白块就可以查看详细信息了~



然后我们就可以看到, 登录就是一个POST请求, 而请求的内容也在下面的Form Data中显示出来了,(好像顺便暴露了密码...), 我们只需要处理成一个字典:

logindata={
'user_id1':'fk_poj',
'password1':'123456',
'B1':'login',
'url':'/'
}

然后用requests.post往Request URL(http://poj.org/login)里把这个字典发过去就ok了..

r=requests.post('http://poj.org/login',data=logindata)

但是我们又不能同时登录和交题, 所以我们要保持登录...

这样我们就不能每次调用requests.post/get了, 但是requests替我们想了办法...

我们可以用requests.session..

session=requests.session()
r=session.post('http://poj.org/login',data=logindata)

这样很显然session这个变量还能继续使用, 然后这个连接就会保持...

好的我们已经实现了发送登录数据, 但是我们并不知道是否登录成功了啊, 我们模拟一下登录错误的情况, 输密码多输一个7:

发现并没有什么提示啊之类的, 界面也没有切换, 抓包里面的也没有特殊的返回...

这就比较难办咯.. 但是有些功能是只有登录后才能用的!

(比如我们要做的交题就是... 留个小练习:自己动手做一下如果不登录就用POST交题(方法下面会讲), 会产生什么现象?)

这里我选择了邮件... 如果登录不成功的话会是这样的:

而登录成功的话是这样的:

那我们就找出两个界面中html代码的不同(不同还挺大), 然后正则处理一波就行了...

(这里我用的是"Error", 判断存不存在的时候可以用开发人员工具中的Elements选项卡里Ctrl+F搜索...)

这样就登录完了, 该交题了.

由于我们只自动提交, 所以不需要读题啊获取题目信息啊之类的..

那我们就直接打开交题界面就行了...

这里选择了伟大的1000 A+B Problem

然后贴一段代码(越简单越好啊, 不要用什么"高标预流推进"之类的啦)

然后点submit, 会发现代码变成了这个熊样

而且看抓包信息的话, 这鬼东西作为了表单中的Source被POST了进去

而由常识, 我们可以一眼看出这是base64的编码...

而非常巧的是, python支持base64的编码和解码, 所以我们只需要:

#import base64 # 只要import就好啦
submitdata={
'problem_id':1000,
# 语言的话当然是填列表项的索引, 注意从0开始
'language':4,
# base64的解码解出来是unicode, 所以再强转一步str
'source':str(base64.b64encode(code.encode('utf-8'))),
'submit':'Submit',
'encoded':1
}

然后我们把表单POST给http://poj.org/submit就行啦~

r=session.post('http://poj.org/submit',data=submitdata)

这样就做完了, 我们来看一下是否成功地提交了呢?

嗯 成功了, status中出现了我们的提交记录.

但是平时交题的时候基本都不是交上就算了的, 肯定还要知道自己的结果怎样, 而如果每次都手动查看的话, 显然还不如直接在oj上主动交呢.. 所以我们还要获取一下提交的结果.

为了防止其他人交题过多造成的干扰(比如交了好几页), 我们不妨利用搜索功能, 用对应的用户名、题目名和语言来找到最近的提交记录(就是刚刚提交的..) 我们还是来搜索然后抓一下包看看做了什么.

嗯, 这次是一个GET指令, 而且其实有很显然的GET的标志——选项都写在了url里..

所以我们如果是按照题目编号 用户名和语言三个关键字来搜索的话, 就应该写

status=fxxk_poj_.mainurl+'/status?problem_id=%d&user_id=%s&result=&language=%d'\
%(prob_id,self.user_id,lang)
r=session.get(status)

然后我们就可以获取到网页的内容了...

然后为了找到提交的结果, 我们用BeautifulSoup来分析网页的html代码

from bs4 import BeautifulSoup # 引入BeautifulSoup
soup=BeautifulSoup(r.text)

这样这碗美丽的汤就会自动帮我们做分析了...

但是我们要找到我们要的东西还是要找到这东西的特征...

那我们就找到Elements选项卡, 然后点左上角的箭头, 再单击我们要找的"Accepted"

然后分析它和它父亲 祖父... 标签的关系,

我们发现这东西在一个table里面, 而这个table和其它的table不同的一点是, 这个table有一个class="a"的与众不同的属性.

我们找到了特征, 就可以从汤里把这个table捞出来~

# 自定义比较函数, 找到所有有class标签且class="a"的table
def stat_tab(tag):
'''
ignore tables which is no the submission table
'''
return tag.has_attr('class') and tag['class']==['a'] # 然后用find_all, 不过返回值是个列表, 我们取第0号元素即可(因为是唯一哒)
tbs=soup.find_all(stat_tab)[0]

这样我们就提取出了我们要的状态所在的table... 我们来输出一下看看是不是我们找的...

print tbs.prettify() # 这样可以以正常的缩进来输出

然后我们再在里面精确的定位即可.

我们就可以利用类似于这篇blog讲的那样处理出我们要的信息, 这里我为了防止时间太长定位出偏差选择了根据Run ID来找, 不过翻页了并没有加以处理.. 如果有这种需要(大家还是别交太快对吧), 可以去修改代码... 具体实现这里就不细讲了, 可以去看代码.

然后我们定位出状态所在的这一条之后, 什么内存 时间 代码长度 就都是小意思啦~

这样我们已经可以实现我们需求的功能了, 不过交题的话把code写在一个字符串里其实并不多么好看, 而且也不好调试对吧... 所以我们可以采用文件读写的技术来交题..

上面的实现了的话其实代码就非常的容易啦

fcode=open(filename)
code=fcode.read()
# 然后把code交上去就行啦

最后的代码我进行了一下封装, 这样以后如果还写了其它oj的可以串在一起用..

最后的实现结果:

大约就是这样了. 详细的代码实现参见github..(大家要是觉得有那么一点意思, 希望大家能给个star, 大家的肯定是对一个萌新最大的鼓励_)

【颓废篇】Py:从零开始的poj自动提交的更多相关文章

  1. 自动提交Git branch代码评审到Review Board系统

    背景 敏捷软件开发中,越小的反馈环,意味着软件质量越容易得到保证. 作为组件团队,我们的开发任务中,往往存在一些特性涉及到几十个功能点,开发周期持续数周或数月的情况.如何在开发过程中保证软件质量,是个 ...

  2. 使用RBTool自动提交code review请求

    使用RBTool自动提交code review请求 前言 让我们回想一下手工提交review请求的过程: 首先得用 svn diff > filename.diff 生成diff文件. 然后输入 ...

  3. 57. Spring 自定义properties升级篇【从零开始学Spring Boot】

    之前在两篇文章中都有简单介绍或者提到过 自定义属性的用法: 25.Spring Boot使用自定义的properties[从零开始学Spring Boot] 51. spring boot属性文件之多 ...

  4. form表单取消按钮自动提交

    默认写在form表单里的按钮可以自动提交表单,现在要实现的效果是点击button按钮调用js函数,再有ajax提交 <button type="button" class=& ...

  5. form表单的enter自动提交

    当form中只有一个文本框时并且获得焦点 按enter时,就会自动提交表单.阻止自动提交 可以添加一个隐藏的input框 <input type="text" style=& ...

  6. 按Enter键后Form表单自动提交的问题

    怪事年年有,今年特别多. 话说,最近项目中遇到一件怪事,当我鼠标focus在文本框中,轻轻敲了下回车键,尼玛页面突然刷新了,当时把宝宝给吓得. 接下来就是一番苦逼的烧脑和蛋疼~ 一.被表象所迷惑 突然 ...

  7. 按下enter键后表单自动提交问题

    在HTML的form表单里,按下enter键之后,默认情况下表单会自动提交. 在公司一个项目里,按下enter键自动提交表单的查询结果与按下搜索框的搜索结果页面显示不一样,按下搜索按钮之后是通过Aja ...

  8. 关闭SSMS的事务自动提交,改为手动提交

    SQLServer 2005-2008-2012使用Oracle时,默认是手动提交.而SQLServer2005中,默认是自动提交,但是SQLServer支持配置. 方法: 用SSMS连接到SQL S ...

  9. postgresql 关闭自动提交

    1. 简介说明             我们知道oracle中sqlplus里面执行dml语句:是需要提交commit:若错了:也可以回滚rollback: 然而在postgresql里面默认是自动提 ...

随机推荐

  1. Pytest参数传递

    import pytest@pytest.fixture()def login_r(open_browser):#调用login时,发现需要先打开浏览器,所以改成先打开浏览器,在登陆 print('输 ...

  2. vue对象侦测

    http://blog.csdn.net/yihanzhi/article/details/74200618 数组:this.$set(this.arr,index,value)

  3. transform:translate(-50%,-50%)

    和父亲元素没关系,走自己盒子宽度一半

  4. TreeSet集合在哪种情况下会报错

    1.自然排序中的元素对象,都必须实现了Comparable接口,否则会抛出异常,案例如下: public class MySetTree { public static void main(Strin ...

  5. 【LeetCode 14】最长公共前缀

    题目链接 [题解] 二分最长前缀的长度. 然后暴力把第2..n个字符串和第1个字符串的前mid个字符匹配. 还有一种比较厉害的算法. 把这n个字符串加入到字典树当中去. 然后根节点到第一个有分支的节点 ...

  6. 【Flutter学习】基本组件之图片组件Image

    一,概述 Image(图片组件)是显示图像的组件,一个显示图片的widget,支持图像格式:JPEG,PNG,GIF,动画GIF,WebP,动画WebP,BMP和WBMP. Image组件有多种构造函 ...

  7. 思维+multiset优化——cf1249E

    正着想很难,但是反着想就容易有思路 /* 将问题转化为 挑选最多的线段,每个点的覆盖次数不超过k次 multiset里存k个右端点,表示第i层当前的最远右端点,每次来一根新线段,能填就填进并更新,不能 ...

  8. Elasticsearch之index_closed_exception

    索引的打开与关闭 关闭索引 POST /index_name/_close 尝试插入数据 PUT /shakespeare/_doc/ { "title":"kibana ...

  9. Alibaba Cloud Toolkit,你确定不来尝鲜一下?

    阿里云出了新的工具,Alibaba Cloud Toolkit,看看“toolkit”这个名字就知道它是一个工具集. 没错!它就是一个工具集,一个集打包部署发布以及探索分析程序的工具集.而我,目前还停 ...

  10. move_base 分层代价地图的作用(翻译)

    A. 标准层 ​ Static Map Layer:为了做全局规划,机器人需要一个超越其传感器的地图,以了解墙壁和其他静态障碍物的位置. 静态地图可以先用SLAM算法生成,也可以从架构图中创建. 当层 ...