• 【颓废篇】Py:从零开始的poj自动提交


    之前学习了一些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, 大家的肯定是对一个萌新最大的鼓励_)

  • 相关阅读:
    SqlConnectionStringBuilder
    什么时候使用“静态方法”(转)
    Dos命令下多文件中查找某字符串
    StructLayout(LayoutKind.Sequential)(转)
    DOS批处理高级教程:第四章 批处理中的变量(转)
    DOS批处理高级教程:第一章 批处理基础(转)
    C#各种进制之间数据转换二进制、八进制、十进制、十六进制
    DOS批处理高级教程:第六章 if命令讲解(转)
    ModbusRTU模式和结束符(转)
    DOS批处理高级教程:第七章 DOS批处理编程高级技巧(转)
  • 原文地址:https://www.cnblogs.com/enzymii/p/8466201.html
Copyright © 2020-2023  润新知