qiuyouzhi 发表于 2021-2-11 18:08:28

Python Challenge 攻略

本帖最后由 qiuyouzhi 于 2024-11-1 10:46 编辑


Python Challenge 攻略

网址:http://www.pythonchallenge.com/


第一关

进入第一关,给了我们一张图片和一个提示:



提示意思:
尝试改变 URL 地址。

2 的 38 次方的值为 274877906944,修改URL为:

http://www.pythonchallenge.com/pc/def/274877906944.html


第二关

给了我们一张图片,一个提示,和一串字符串:



观察图片内容,貌似是映射关系。

观察后,发现映射关系中的两个字符顺序差值都为2。

既然如此,我们可以利用这个规律转换下下面的字符串。

代码如下:

str1 = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
res = ''

for each in str1:
    if each.isalpha():
      res += chr(ord(each) + 2)
    else:
      res += each

print(res)

结果:

i hope you didnt tr{nsl{te it |y h{nd. th{ts wh{t computers {re for. doing it in |y h{nd is inefficient {nd th{t's why this text is so long. using string.m{ketr{ns() is recommended. now {pply on the url.

发现有一些奇怪的字符夹杂在了字符串里面,如 { 和 | 。

如 wh{t ,它的正确形式为 what 。

如 |y h{nd,它的正确形式为 by hand 。

综上所述,可以推断出 { 代表 a ,| 代表 b 。

修改下代码:

str1 = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
res = ''

for each in str1:
    if each.isalpha():
      res += chr(ord(each) + 2)
    else:
      res += each

print(res.replace('{', 'a').replace('|', 'b'))

结果为:

i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url.

大意就是让我们把 URL (map.html) 也使用相同的规律更改。

map 更改后的结果为 ocr 。

修改 URL:http://www.pythonchallenge.com/pc/def/ocr.html


第三关

这关图片没啥用,只有一个字符串:

recognize the characters. maybe they are in the book,
but MAYBE they are in the page source.

下面的 MAYBE 是大写的,说明答案很有可能在网页源代码里面。

翻了翻,发现了一处地方可能是答案:

<!--
find rare characters in the mess below:
-->

<!-- (目标字符串)
-->

上面那行英文的大意就是找出出现次数最少的字符。

提示都给到这了,想必大家都会写了8 。

代码:

str1 = """请在此处粘贴长字符串"""
count = {}

for each in str1:
    if each not in count:
      count = 1
    else:
      count += 1

print(''.join(each for each in count if count == min(count.values())))

答案为 equality 。

修改 URL :

http://www.pythonchallenge.com/pc/def/equality.html


第四关

One small letter, surrounded by EXACTLY three big bodyguards on each of its sides.
简单编程题。

代码:

str1 = """请在此处粘贴长字符串"""

for i in range(4, len(str1) - 4):
    tmp1 = str1 + str1 + str1
    tmp2 = str1 + str1 + str1
    if tmp1.isupper() and tmp2.isupper() and str1.islower() and str1.islower() and str1.islower():
      print(str1, end = '')

最后的结果为 linkedlist 。

修改 URL,但这次网页显示的是 linkedlist.php,所以我们把 URL 改成要求的形式。

http://www.pythonchallenge.com/pc/def/linkedlist.php


第五关

细读源代码,发现了两个突破口:

第一个是一个提示:
<!-- urllib may help. DON'T TRY ALL NOTHINGS, since it will never
end. 400 times is more than enough. -->

第二个是一个 URL ,点击页面里面的图片即可跳转。

http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=12345

结合两个线索,应该就是多次尝试 URL 里面的 nothing 的值了。

访问一下这个 URL,查看内容:

and the next nothing is 44827

替换 URL 里面的 nothing:

and the next nothing is 45439

说明了 nothing 会在页面里面给出,我们只需写一个爬虫:

from requests import get

num = "12345"
url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing={}"

for i in range(400): # 因为线索一表明了试400次足够了
    res = get(url.format(num))
    num = res.text.split()[-1]
    print(res.text)

等待爬虫运行完毕后,看下所有打印结果,发现里面有个 peak.html,那么这个就是答案了。

修改URL :

http://www.pythonchallenge.com/pc/def/peak.html


第六关

这一关给了你一个图片,下面给了一个 "pronounce it"。

没啥突破口,读下源代码,发现两个线索:

1, 代码里面有一个注释:

<!-- peak hell sounds familiar ? -->

大意就是 peak hell 读起来像什么。

2, 发现一个文件:

http://www.pythonchallenge.com/pc/def/banner.p


peak hell 读起来像 pickle ,说明下面的 banner.p 文件需要使用 pickle 处理。

将文件下载下来,打印下 pickle.load 的结果试试:

import pickle, pprint

ans = pickle.load(open('banner.p', 'rb'))
pprint.pprint(ans)

结果是一个很长的列表。

分析这个列表,发现它的元素很奇怪,是许多个子列表里面包含着许多个元组。

每个元组都有两个元素,第一个元素是空格或者井号(#),第二个是一个数字。

通过分析,猜测这是一幅字符画,数字代表着前面字符的出现次数。

于是写出如下代码:

import pickle

ans = pickle.load(open('banner.p', 'rb'))
for i in ans:
    for each in i:
      print(each * each, end = '')
    print()

大家运行一下,很有趣的!

结果就是 channel 了。

修改 URL:

http://www.pythonchallenge.com/pc/def/channel.html



第七关 & 第八关

网页内只有一张图片,且源代码中也没有任何可利用的信息。

观察图片,内容为一条裤子的拉链。拉链英文为 zipper, 将其放入网页 url 中试一试:

[:3] todo: tell everyone that [:n] is to take the first n characters...
翻译: [:3] 待办事项: 告诉所有人 [:n] 的意思是提取前 n 个字符...

这段话前面还有一个 [:3], 也就是提取前三个字符。可以猜测,需要提取 zipper 的前三个字符,也就是 zip.

zip 是一个压缩包的格式,把网页后缀从 .html 改为 .zip, 弹出了下载界面。

解压缩该压缩包后,发现了不少以数字命名的文件,还有一个 readme.txt。

welcome to my zipped list.

hint1: start from 90052
hint2: answer is inside the zip

看起来形式和第六关差不多,只不过改成了文件。

编写代码:
id = "90052"
while True:
    filename = f"{id}.txt"
    with open(filename) as f:
      content = f.read()
      print(content)
      id = content.split()[-1]

运行后很快,报错了,程序停止。

读取的最后一个文件的内容为 Collect the comments. 收集注释。

文本文件肯定是没有注释的,那么注释八成就是这个压缩包的注释。这也和 readme.txt 中的第二个提示呼应。

修改代码:
import zipfile

fz = zipfile.ZipFile("channel.zip")
id = "90052"
while True:
    filename = f"{id}.txt"
    print(str(fz.getinfo(filename).comment.decode("utf-8")), end = '')
    with fz.open(filename) as f:
      content = f.read()
      id = content.split()[-1].decode("utf-8")
      if not id.isdigit(): break

中间涉及到了一些关于 zipfile 模块的用法,若不了解可自行查阅。

查看输出结果:


由此得出答案:hockey.

修改 URL: http://www.pythonchallenge.com/pc/def/hockey.html

进入网页,只有寥寥一行字:

it's in the air. look at the letters.
翻译:它在空气中。看看那些字母。

观察刚才输出的字符画,发现 hockey 这六个字母都由 oxygen 的六个字母组成。

修改 URL: http://www.pythonchallenge.com/pc/def/oxygen.html

第九关

页面内只有一张图片。在图片正中央有一个很明显的由灰色色块组成的条。

考虑将其读取出来。

import cv2

img = cv2.imread("oxygen.png")
for j in range(img.shape - 21):
    print(img // 2, j], end = "")

输出结果为 RGB 值。不难发现,每一个像素的 RGB 值都是由三个相同的数字组成。

考虑将其转换为字符串。

import cv2

img = cv2.imread("oxygen.png")
for j in range(img.shape - 21):
    px = img // 2, j]
    print(chr(px), end = "")

输出结果:
sssssmmmmmmmaaaaaaarrrrrrrttttttt       ggggggguuuuuuuyyyyyyy,,,,,,,       yyyyyyyooooooouuuuuuu       mmmmmmmaaaaaaadddddddeeeeeee       iiiiiiittttttt.......       ttttttthhhhhhheeeeeee       nnnnnnneeeeeeexxxxxxxttttttt       llllllleeeeeeevvvvvvveeeeeeelllllll       iiiiiiisssssss       [[[[[[]]]]]]]

美化后结果:
smart guy, you made it. the next level is

用相同的方法再将该列表中的数字转换为字符,得出结果为 integrity。

修改 URL: http://www.pythonchallenge.com/pc/def/integrity.html

第十关

网页内有一张蜜蜂的图片,还有一段文字 "Where is the missing link?"

阅读网页源代码,发现如下信息:

1. 网页内部有一片区域(图片中的蜜蜂所在位置),包含一个超链接。
2. 代码最下方有一块注释:
<!--
un: 'BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084'
pw: 'BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08'
-->

点击图片里的蜜蜂,跳转到一个新的网页,并要求我们登录。

不难猜测,注释中的 un 和 pw 分别对应 username 和 password。下面要做的是如何解析里面的数据。

两个字符串都以 BZ 开头,是 bz 格式的压缩文件。

使用 Python 的 bz2 模块处理:
import bz2

username = b"BZh91AY&SYA\xaf\x82\r\x00\x00\x01\x01\x80\x02\xc0\x02\x00 \x00!\x9ah3M\x07<]\xc9\x14\xe1BA\x06\xbe\x084"
password = b"BZh91AY&SY\x94$|\x0e\x00\x00\x00\x81\x00\x03$ \x00!\x9ah3M\x13<]\xc9\x14\xe1BBP\x91\xf08"

print(bz2.decompress(username).decode()) # 'huge'
print(bz2.decompress(password).decode()) # 'file'

将用户名和密码分别输入,跳转到下一关。


第十一关

网页内有一张图片,上面有不少黑点。网页标题为 Connect the dots, 连接这些点。

大致看了看,连接后也看不出什么形状。

看看源代码,发现这么一段:
<!--
first+second=?

first: ...

second: ....

-->

省略部分是一大串数字。

这些看起来像坐标,把它们连起来试试呢?

from PIL import Image, ImageDraw

a = list(map(int, """146,399,163,403,170,393,169,391,166,386,170,381,170,371,170,355,169,346,167,335,170,329,170,320,170,
310,171,301,173,290,178,289,182,287,188,286,190,286,192,291,194,296,195,305,194,307,191,312,190,316,
190,321,192,331,193,338,196,341,197,346,199,352,198,360,197,366,197,373,196,380,197,383,196,387,192,
389,191,392,190,396,189,400,194,401,201,402,208,403,213,402,216,401,219,397,219,393,216,390,215,385,
215,379,213,373,213,365,212,360,210,353,210,347,212,338,213,329,214,319,215,311,215,306,216,296,218,
290,221,283,225,282,233,284,238,287,243,290,250,291,255,294,261,293,265,291,271,291,273,289,278,287,
279,285,281,280,284,278,284,276,287,277,289,283,291,286,294,291,296,295,299,300,301,304,304,320,305,
327,306,332,307,341,306,349,303,354,301,364,301,371,297,375,292,384,291,386,302,393,324,391,333,387,
328,375,329,367,329,353,330,341,331,328,336,319,338,310,341,304,341,285,341,278,343,269,344,262,346,
259,346,251,349,259,349,264,349,273,349,280,349,288,349,295,349,298,354,293,356,286,354,279,352,268,
352,257,351,249,350,234,351,211,352,197,354,185,353,171,351,154,348,147,342,137,339,132,330,122,327,
120,314,116,304,117,293,118,284,118,281,122,275,128,265,129,257,131,244,133,239,134,228,136,221,137,
214,138,209,135,201,132,192,130,184,131,175,129,170,131,159,134,157,134,160,130,170,125,176,114,176,
102,173,103,172,108,171,111,163,115,156,116,149,117,142,116,136,115,129,115,124,115,120,115,115,117,
113,120,109,122,102,122,100,121,95,121,89,115,87,110,82,109,84,118,89,123,93,129,100,130,108,132,110,
133,110,136,107,138,105,140,95,138,86,141,79,149,77,155,81,162,90,165,97,167,99,171,109,171,107,161,
111,156,113,170,115,185,118,208,117,223,121,239,128,251,133,259,136,266,139,276,143,290,148,310,151,
332,155,348,156,353,153,366,149,379,147,394,146,399""".replace('\n', '').split(',')))
b = list(map(int, """156,141,165,135,169,131,176,130,187,134,191,140,191,146,186,150,179,155,175,157,168,157,163,157,159,
157,158,164,159,175,159,181,157,191,154,197,153,205,153,210,152,212,147,215,146,218,143,220,132,220,
125,217,119,209,116,196,115,185,114,172,114,167,112,161,109,165,107,170,99,171,97,167,89,164,81,162,
77,155,81,148,87,140,96,138,105,141,110,136,111,126,113,129,118,117,128,114,137,115,146,114,155,115,
158,121,157,128,156,134,157,136,156,136""".replace('\n', '').split(',')))

img = Image.open("good.jpg")
draw = ImageDraw.Draw(img)
draw.line(a, fill = 256)
draw.line(b, fill = 256)

img.show()

画出了一头牛。将 URL 修改为 cow, 提示是公牛,那就改成 bull.

http://www.pythonchallenge.com/pc/return/bull.html

第十二关

网页内有一张图片和一行文字:len(a) = ?

先找到 a 列表位置。阅读源代码,发现图片内藏了一个超链接,点开是一行列表:

a =

要求求出第 30 项的长度。

以下回答来自 ChatGPT:
这个数列是"说出前一个数"的数列,通常称为“看和说”序列。每一项都是对前一项的描述。

具体来说:

第1项是 1。
第2项是对第1项的描述,读作“一 个 1”,即 11。
第3项是对第2项的描述,读作“两个 1”,即 21。
第4项是对第3项的描述,读作“一 个 2,一个 1”,即 1211。
第5项是对第4项的描述,读作“一个 1,一个 2,两个 1”,即 111221。

编写代码:
a = ["1"]
for i in range(40):
    tmp, cnt = "", 0
    for j in range(len(a)):
      if j == 0 or a == a: cnt += 1
      else:
            tmp += str(cnt) + a
            cnt = 1
    a.append(tmp + str(cnt) + a[-1])

print(len(a)) # 5808

最后求出答案,为 5808.

修改 URL: http://www.pythonchallenge.com/pc/return/5808.html

第十三关

import cv2
import numpy as np

img = cv2.imread("cave.jpg")
odd = []
even = []
for i in range(0, img.shape, 2):
    tmp = []
    for j in range(0, img.shape, 2):
      tmp.append(img)
    odd.append(tmp)
   
cv2.imshow("odd", np.array(odd))
cv2.waitKey(0)
生成的图片中包含一个单词 evil, 也就是答案。

第十四关
with open("evil2.gfx", "rb") as f:
    content = f.read()

print(content)

for i in range(5):
    f = open(f"{i}.jpg", "wb")
    f.write(content)
    f.close()

得出五张图片,文字分别是 dis, pro, port, ional 的上半部分 (靠猜测猜出来的) 和被划掉的 ity.

答案即为 disproportional.

qiuyouzhi 发表于 2024-11-1 16:26:12

from PIL import Image

img = Image.open("wire.png")
res = Image.new("RGB", (100, 100))
l, y, cnt = 0, 0, 0
while l < 10000:
    for x in range(cnt, 100 - cnt):
      res.putpixel((x, y), img.getpixel((l, 0)))
      l += 1
    for y in range(cnt, 99 - cnt):
      res.putpixel((x, y), img.getpixel((l, 0)))
      l += 1
    for x in range(99 - cnt, cnt, -1):
      res.putpixel((x, y), img.getpixel((l, 0)))
      l += 1
    for y in range(98 - cnt, cnt, -1):
      res.putpixel((x, y), img.getpixel((l, 0)))
      l += 1
    cnt += 1

res.show()
页: [1]
查看完整版本: Python Challenge 攻略