最近做题的时候要写一些题解,在把牛客网的题目复制下来的时候,数学公式的处理比较麻烦,所以我用Python的selenium、urllib.request和BeautifulSoup4库对题目信息进行了爬取,写题解的时候时间节约了很多。

2. 前期准备

安装selenium、urllib和BeautifulSoup库。

pip3 install urllib

pip3 install selenium

pip3 install beautifulsoup4

3. 获取网页内容

# 导入库
import urllib.request
import bs4
import time
from bs4 import BeautifulSoup
from selenium import webdriver
# 题目属性
problemId = "204552"
# 打开浏览器,模拟登陆
# 此处用的是Chrome,如果没有安装可以替换为其他支持的浏览器
driver = webdriver.Chrome()
获取网页内容
# 获取页面内容
# 题目链接
url = f"https://ac.nowcoder.com/acm/problem/{problemId}"
# 打开网页
driver.get(url)
# 网页加载等待时间
time.sleep(3)
# 找到 输入 用户名 和密码框,并且设置内容
username = driver.find_element_by_id('jsEmailIpt')
# 输入账号名,xxx替换为自己的账户名
username.send_keys('xxx')
time.sleep(1)
password = driver.find_element_by_id('jsPasswordIpt')
#输入密码,xxx替换为自己的密码
password.send_keys('xxx')
time.sleep(1)
# 分析网页,找到登录按钮
login = driver.find_elements_by_css_selector('div[class=col-input-login] a')[0]
# 点击按钮
login.click()
time.sleep(3)
# 格式化源代码
soup = BeautifulSoup(driver.page_source,'lxml')
# 退出浏览器
driver.quit()
存储和预处理
# 存储
data_dict = {}
# 找到主体内容
mainContent = soup.find_all(name="div", attrs={"class" :"terminal-topic"})[0]
# 去除公式的重复html元素
for each in mainContent.find_all('mrow'):
each.decompose()
for each in mainContent.find_all(name="span", attrs={"class" :"katex-html"}):
each.decompose()
# 更换换行符
for each in mainContent.find_all('br'):
each.replace_with("\n\n")
4. 内容处理
4.1. Limit
先从比较简单的信息入手,找到题目标题、时间、和内存限制。
# Limit
# 找到题目标题、时间、和内存限制
div = mainContent.find_all(name="div", attrs={"class":"subject-item-wrap"})[0].find_all("span")
# 放入字典中存储
data_dict['Title'] = f"牛客网 NC{problemId} " + soup.title.contents[0]
# Time Limit
data_dict['Time Limit'] = div[0].contents[0].split(':')[1]
# Memory Limit
data_dict['Memory Limit'] = div[1].contents[0].split(':')[1]
定义函数,处理主体内容中诡异的空格和公式的符号。
def divTextProcess(div):
"""
处理
 
"""
# 获取文本
strBuffer = div.get_text()
# 替换公式标记
strBuffer = strBuffer.replace("{", " $").replace("}", "$ ")
# 去除多个空格
strBuffer = strBuffer.replace(" ", "")
# 去除多个换行符
strBuffer = strBuffer.replace("\n\n\n", "\n")
# 去除内容中用\xa0表示的空格
strBuffer = strBuffer.replace("\xa0", "")
# 去除首位空格
strBuffer = strBuffer.strip()
# 返回结果
return strBuffer
4.2. Problem Description
获取题目描述
# 处理题目描述
div = mainContent.find_all(name="div", attrs={"class": "subject-question"})[0]
data_dict['Problem Description'] = divTextProcess(div)
4.3. Input
输入描述
div = mainContent.find_all(name="pre")[0]
data_dict['Input'] = divTextProcess(div)
4.4. Output
输出描述
div = mainContent.find_all(name="pre")[1]
data_dict['Output'] = divTextProcess(div)
4.5. Sample Input & Onput
输入样例,用代码框环境包围。
# Input
div = mainContent.find_all(name="div", attrs={"class":"question-oi-cont"})[0]
data_dict['Sample Input'] = "```cpp" + div.get_text() + '```'
# Onput
div = mainContent.find_all(name="div", attrs={"class":"question-oi-cont"})[1]
data_dict['Sample Onput'] = "```cpp" + div.get_text() + '```'
4.6. Note
备注
# 若有备注
if len(mainContent.find_all(name="pre")) >= 5:
div = mainContent.find_all(name="pre")[-1]
data_dict['Note'] = divTextProcess(div)
4.7. Source
题目链接
data_dict['Source'] = '[' + data_dict['Title'] + ']' + '(' + url + ')'
5. 输出
for each in data_dict.keys():
print('### ' + each + '\n')
print(data_dict[each].replace("\n\n**", "**").replace("**\n\n", "**") + '\n')
下面是最后的输出结果
### Title
牛客网 NC204552 咪咪游戏
### Time Limit
C/C++ 1秒,其他语言2秒
### Memory Limit
C/C++ 524288K,其他语言1048576K
### Problem Description
牛牛最近喜欢玩咪咪游戏,于是自己写了个程序编了个游戏让牛妹来玩。游戏是这样的:
牛牛有一个长的字符串(只包26含个小写字母),他想让牛妹判断这个字符串是好的。
定义一个串是好的:这个串是由连续的mq连接而成的。
比如 $mqmq$ 说明这个串是好的, $mqmqm$ 或 $mqmqx$ 都是不好的。现在牛牛 想问牛妹这个串是否是好的,如果好的输出 $Yes$ ,否则输出 $No$
### Input
第一行一个整数Q,表示询问次数
就下来Q行,一个字符串$s
### Output
Q行,每行输出 $Yes$ 或 $No$
### Sample Input
// 这里会有```cpp代码环境,在这里为了展示方便去掉了
4
mqmq
mqmqm
mqakioi
mqqmmq
### Sample Onput
Yes
No
No
No
### Note

对于 $60\%$ 的数据满足: $|s|<=10,Q<=10$ 且保证只出现m,q两个字符

对于 $100\%$ 的数据: $|s| <=10^5,Q<=10$

对于所有数据保证只出现26个英文小写字母