这里再次对所有跟我一样学习ACM的同学和大佬以及hdu管理员陪个不是,毕竟这只是个取巧的方式......望见谅
(测试发现C++的通过率实在有点尴尬,并且出现了大部分转码有问题造成CE的情况....在学长的建议下转投python大法.....然后排名直冲第二...)
python跟C++不一样的地方是python对爬虫,网页操作等着极好的封包操作,在这里选用requests第三方库进行POST和GET的操作,特别是这个BeautifulSoup,能够非常方便的从网页中某个标签内提取自己需要的东西
import requests
import re
import time
from bs4 import BeautifulSoup
整体思路分成两个操作,一个是页面操作,一个是爬取代码
网页操作中包含三个函数
1.login() //登录操作
2.submit() //提交代码操作
3.getsolve() //从个人界面中提取已AC的所有题号,判断当前题目是否被AC
爬取代码中包含一个函数
1.getcsdn() //在百度搜索中查找csdn博客题解,并且提取第一页中所有博客中的代码
(1)模拟浏览器
python跟C++一样,需要浏览器环境支持,C++代码中我不知道怎么模拟浏览器环境,但是python模拟浏览器环境非常方便
def __init__(self):
self.session = requests.Session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
}
self.session.headers.update(headers)
(2)利用POST模拟账号登录
利用F12抓包,在network里可以看到,在登录的时候会需要用到login操作界面,而这个界面的data中需要三个内容,username,userpass和login,所以只要我们在data中填上这三个内容,就可以实现登录操作
def login(self, username, password):
url = 'http://acm.hdu.edu.cn/userloginex.php?action=login'
data = {
'username': username,
'userpass': password,
'login': 'Sign In'
}
r = self.session.post(url, data=data)
(3)在个人界面中查看自己已通过的题目,判断当前题目是否通过
打开个人界面,F12查看HTML源代码,一层层标签展开后我们可以发现,有一个'p'标签,格式为align='left'的标签内存放着“List of solved problems”
这里就要用到python的BeautifulSoup函数库了,Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据
所以在对HTML进行BeautifulSoup操作后,我们可以直接找到格式为align='left'的'p'标签并且提取出这个标签内的所有内容,并对这段字符串进行处理,提取出我们已经AC的题号集合
def getsolved(self, username):
url = 'http://acm.hdu.edu.cn/userstatus.php?user=%s' %username
solved = []
r = self.session.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
res = soup.find('p', align='left')
# print(res)
str = res.text.split(';')
for item in str:
if item:
item = re.search(r'\d{4}', item)
solved.append(item.group(0))
return solved
(4)在确定当前题目没有被通过后,我们需要考虑在网上搜索这道题的题解,并且提取代码
首先搜索代码想到的肯定是百度了,但是在我C++爬虫文章中写过,百度的搜索都进行了转义加密,但是python有一个好处,有个命令叫match,所以就不要紧了。
对于如何查看搜索界面中所有标签,跟上面一样,使用F12展开标签页,找到包含搜索结果链接的标签页,并且提取所有链接也就是find_all,在提取了搜索界面所有链接后,我们只要match所有链接中是否存在csdn链接,保存所有csdn链接即可
同样,在csdn中我们可以发现
代码框中的代码被分成每行一个class,这样的话就非常难以提取代码,但是我们在python代码中输出找到的网页源代码,可以发现是不一样的,所有代码被放在一个'pre'标签内,内容是name="code"
所以我们可以按照这个提取代码,提取格式为name='code'的'pre'标签内的内容,并且存下所有代码
def getcsdn(self, problemID):
solutions = []
solutionurls = []
url = r'http://www.baidu.com/s?wd=hdu%20' + str(problemID) +'%20csdn' #r防止转义
baidusession = requests.session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
}
baidusession.headers.update(headers)
r = baidusession.get(url)
soup = BeautifulSoup(r.text, 'html.parser')
res = soup.find_all('a', attrs={'target': '_blank', 'class': 'c-showurl', 'style': 'text-decoration:none;'})
for item in res:
if re.match("blog.csdn.net", item.text):
solutionurls.append(item['href'])
for item in solutionurls:
r = baidusession.get(item)
soup = BeautifulSoup(r.text, 'html.parser')
code = soup.find('pre',attrs={'name': 'code'})
if code:
solutions.append(code.text)
#print(code.text)
return solutions
(5)获得代码后,利用submit函数提交程序到指定题目
同样的利用F12的network抓包,手动提交一次之后可以发现,真正的提交操作是在url=http://acm.hdu.edu.cn/submit.php?action=submit
这个页面中的,另外我们可以发现,data中需要填入的有check,problemid,language和usercode
problemid和usercode很容易看清楚是什么,那么languag也很容易可以发现,就是我们选择的提交语言
我们可以发现,之前提交的G++在language中选项是0,那么依次对应的就是
G++: 0
GCC: 1
C++: 2
C: 3
Pascal:4
Java:5
C#: 6
//check选项我不知道是干什么的。。不管提交什么代码都是0,那么就默认它为0就可以了~
def submit(self, problemID, code, language=0):
url = 'http://acm.hdu.edu.cn/submit.php?action=submit'
code = code.encode('utf-8').decode() //使用python的命令转码代码
# print(code)
data = {
'check': 0,
'problemid': str(problemID),
'language': str(language),
'usercode': code
}
se = self.session
r = se.post(url, data=data)
遍历所有题号,按照上面的步骤运行,这里有两点要注意:
1.前面提取代码的时候忽略了'pre'标签中的一个内容是class='cpp',因为有的博客大佬是用java写的程序...所以为了提高AC率,我们提取所有java或者c++的代码,在提交时选择一下语言即可,判断是否是java代码也非常简单...判断一下是不是存在Scanner命令就好了..
2.我们写爬虫是为了自己娱乐...或者学习新东西,但是这个的前提是不影响其他同学正常提交以及正常练习,所以我们这里在每次提交后使用time.sleep()函数间隔30秒再继续提交
def acc(self, start=1001, end=6000, timelate=30):
language = 0
for problemID in range(start, end):
print("Problem",problemID,end=" ")
if str(problemID) not in ac.getsolved(user)://判断当前题目是否被AC
answer = ac.getcsdn(problemID) //若没有被AC则提取百度搜索第一页中所有csdn博客中的代码
if answer:
flag = 0
for ans in answer:
if ans.find('Scanner') != -1: //若搜索到java的关键字Scanner,则选择language为5
language = 5
else:
language = 0
if str(problemID) not in ac.getsolved(user):
ac.submit(problemID, ans, language=language)
time.sleep(timelate)
else:
flag = 1 //若已经AC,则跳出循环节省时间
break
if flag:
print('Accepted')
else:
print('')
else:
print("Accepted")
(7)至此,整个结构体操作已经写完了...也没有什么好写的了.....看到这里如果都能看懂的话,稍微理解一下,我们可以用同样的方式爬取博客园,Acmer等等题解中的代码来提高AC率,这里我就不加了...
欢迎各位fork~完整代码就不贴了,90%的代码都已经在这里了
//另外加一句~最近的个人感慨,你爱的人不一定能陪你到最后,但是爱你的人一定会!
然后我的博客听说最近不能留言了...不知道是不是真的麻烦各位大佬告诉我一下,谢谢~(*^▽^*)