第二次个人作业
背景
为了更好地提升代码能力,jason哥想要收集相应的题目,有针对性地刷题。而需要收集洛谷所有题目,但是工作量太大,所以jason哥急需大家运用爬虫技术,得到洛谷各种难度的题目和题解。考虑到近来流行的AIGC技术,jason哥认为,在AI的帮助下,这项工作的难度会大大降低。
项目要求
在AIGC技术的帮助下,利用Copilot等工具,运用Python完成爬虫,并用Tkinter库制作相应的GUI页面,将爬取到的题目以markdown文件存储,放到相应文件夹下。
github存储库
我在这里
项目简介
项目前端
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a11be9547ae78aa2fe1208b93b33f2ed.png)
- 一共有两个界面,一个是”开始爬取界面“,点击button后就会弹出”爬取-洛谷习题“界面,而后者为主界面,用于爬取题目。
- 题目有三个筛选条件,分别为:”题目难度“,包括暂无评定入门,普及-,普及/提高-,普及+/提高,提高+/省选-,省选/NOI-,NOI/NOI+/CTSC;”关键词“,需要用户键入,如题目编号;还有”题目“年份供用户选择。
- 爬取的信息会在左下角的文本框中实时显示出来,并且设置有进度条,可供用户了解当前爬取进度,如下图:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/ec9100f0c15f32b60cabfdd02925e42c.png)
所用的爬取技术
- 首先是网页内容的获取,我们可以使用定义headers,和cookie来模拟用户使用浏览器访问网页
#一般的获取html函数
def get_html(url):
# 模拟用户使用浏览器访问
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.1.4031 SLBChan/103",
"cookie": "__client_id=af4215a6f73e4641a2ae5ed49f35ef0b93b0709b; login_referer=https%3A%2F%2Fwww.luogu.com.cn%2Fauth%2Flogin; _uid=664601; C3VK=a66952"
}
response = requests.get(url=url, headers=headers)
return response.text
- 利用以上功能我们能爬取到除了题解和题目难度外的所有网页信息,但是由于题解和题目难度有反爬机制存在,不能直接爬取其网页内容,怎么办呢?这里提供一种方法(其实是unicode解码后定位不了第一篇题解,所以剑走偏锋):
- 我们可以发现(以P1000题解为例),第一篇题解的博主的博客主页链接由两部分组成,一个是网页前缀”https://www.luogu.com.cn/blog/_post/“,一个是网页后缀数字”17186“,每篇题解的网页前缀都是相同的,只有网页后缀不同,所以我们可以在爬取到的题解html乱码中,利用正则表达式找出那个数字,再和网页前缀拼接,就组成了题解的网址,由于该网址是没有反爬机制的,所以我们可以很轻松的利用上一个方法获得网页信息。获取题目难度同理。
#获取题解博客的后缀
def get_postfix(text):
pattern = r"%22id%22%3A(\d+)"
match = re.search(pattern, text)
if match:
return match.group(1)
return None
#获取题目难度编码
def get_dif(url):
thtml = get_html(url)
text = urllib.parse.unquote(thtml)
pattern = r'"difficulty":(\d)'
numbers = re.findall(pattern, text)
return numbers
- 接下来是将题目和题解转换为.md格式。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/bb165f3bdf784c9981a04f4f1446ad18.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/016168e6820ec5fd1911724bf77e0853.png)
#将题目网页内容转化为md格式
def get_pMD(html):
bs = BeautifulSoup(html, "html.parser")
core = bs.select("article")[0]
md = str(core)
md = re.sub("<h1>", "# ", md)
md = re.sub("<h2>", "## ", md)
md = re.sub("<h3>", "#### ", md)
md = re.sub("</?[a-zA-Z]+[^<>]*>", "", md)
return md
#将题解网页内容转化为md格式
def get_sMD(html):
bs = BeautifulSoup(html, "html.parser")
core = bs.findAll("div", attrs = {"id" : "article-content"})
md = str(core)
md = re.sub("<h1>", "# ", md)
md = re.sub("<h2>", "## ", md)
md = re.sub("<h3>", "#### ", md)
md = re.sub("</p>", "<br>", md)
return md
- 接下来是创建 题目难度-关键词1-关键词2 文件夹存储有关文件。我们可以把难度,关键词1,关键词2先用一个变量或数组表示,便于为文件命名。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4fe1f3a8e3c9adb5c942a21168472dfd.png)
if key_list:
born_portfolio(savePath + dif + "-" + key_list[0] + "-" + key_list[1])
path = savePath + dif + "-" + key_list[0] + "-" + key_list[1] + "\\"
else:
born_portfolio(savePath + dif)
path = savePath + dif + "\\"
#将md文件存下
def saveData(data, filename):
file = open(filename, "w", encoding="utf-8")
for d in data:
file.writelines(d)
file.close()
#生成文件夹
def born_portfolio(name):
if not os.path.exists(name):
os.mkdir(name)
else:
print("Portfolio already exists")
- 接着根据题目关键词设置该题目和题解所对应的存储路径。接着生成存放该题目和题解的文件夹,并将其存入。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/18a5680f6872f53f6bd2623e3ef7e324.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f98455157a567ceb7771e5ee6c893d28.png)
born_portfolio(path + "P" + str(i) + "--" + t_list[i - 1000]) #生成存放题目和题解的文件夹
new_path = path + "P" + str(i) + "--" + t_list[i - 1000] + "\\" #定义路径到该文件夹下
saveData(problem, new_path + "P" + str(i) + "--" + t_list[i - 1000] + ".md")
saveData(solution, new_path + "P" + str(i) + "--" + t_list[i - 1000] + "题解" + ".md")
- 到这就爬取成功了,至于爬取有条件的题目就不在话下,可到具体代码中查看。
单元测试
AIGC表格
子任务 | 预估哪些部分使用AIGC | 实际中哪些部分使用AIGC |
---|
获取题目及题解的html信息 | 预计使用 | 未使用 |
将题目题解的信息转换成md形式 | 预计使用 | 使用 |
将题目题解文件存下 | 预计使用 | 使用 |
总结 | 适合实现目标明确,容易描述的任务 | 感觉就没有AI不会的… |
收获与总结
- 本来在接触这次作业之前,我是完全没关注过有关爬虫的任何知识的,就仅仅听说过这个名词而已。很感谢老师布置的这次作业,能让我在未知领域中探索,并逐渐拨云见日,理清项目的全貌,任务完成后的这种成就感只有亲身经历过才能知道。在这次作业中,我搜集信息,学习信息,整理信息的能力得到了极大的锻炼,也对code有了更深的了解和兴趣。
PSP表格
任务 | 预估耗时 | 实际耗时 |
---|
学习爬虫知识 | 1d | 0.5d |
爬取题目 | 1d | 0.5d |
解决反爬机制 | 0.5d | 1d |
写出可执行程序 | 1d | 2d |
制作GUI界面 | 0.5d | 0.5d |
总计 | 4d | 4.5d |
- 这次的作业感觉做的很折磨,从零开始太迷茫了…这次作业我利用了chatGPT来辅助工作,极大提升了工作效率。在完成过程中经历了很多磕磕碰碰,好不容易把页面爬出来了,结果有反爬机制,解决了反爬机制,又要考虑生成文件的格式,接着制作GUI界面~~虽然但是,我个人认为结果还是好的。在这次过程中花在学习知识,收集信息的时间比较长,希望下次能够改进。