PythonChallenge_2

PythonChallenge_2

一、实验说明

1. 环境登录

无需密码自动登录,系统用户名shiyanlou

2. 环境介绍

本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面上的程序:

  1. LX终端(LXTerminal): Linux命令行终端,打开后会进入Bash环境,可以使用Linux命令
  2. Firefox:浏览器,可以用在需要前端界面的课程里,只需要打开环境里写的HTML/JS页面即可
  3. GVim:非常好用的编辑器,最简单的用法可以参考课程Vim编辑器

3. 环境使用

使用GVim编辑器输入实验所需的代码及文件,使用LX终端(LXTerminal)运行所需命令进行操作。

完成实验后可以点击桌面上方的“实验截图”保存并分享实验结果到微博,向好友展示自己的学习进度。实验楼提供后台系统截图,可以真实有效证明您已经完成了实验。

实验记录页面可以在“我的主页”中查看,其中含有每次实验的截图及笔记,以及每次实验的有效学习时间(指的是在实验桌面内操作的时间,如果没有操作,系统会记录为发呆时间)。这些都是您学习的真实性证明。

二、课程介绍

本次PythonChallenge系列一共有11个项目,持续更新中~~~,每个项目课程里面会详细讲解3个pythonchallenge通关题目以及不同解决方案,课后习题里面布置一个任务题,该题目会在下一个项目中被揭开面纱。

本系列题目属于在线闯关题,由于实验楼暂时不提供访问外网,因此请各位验证答案的时候烦劳点击自己的浏览器访问网页以验证答案。

所有题目和参考的解决方案版权归PythonChallenge官网,课程编写属于实验楼原创,欢迎在问答区积极提问,小编会积极解答,也欢迎在评论区吐槽~

三、实验回顾

在上一个项目课中,我们学到了很多知识,比如:string模块中的一些函数,字典的使用以及函数lambda。记性不算太差的你们估计还记得在上一次的课程中小编有留给大家一个课后作业,不知道你们完成的怎样呢?

不管是否得到答案,让小编带着你一起解决这个问题吧!

四、作业题解析

问题:找出满足以下条件的字母:字母是小写的,在该字母两边各有3个大写字母做保镖。

小编脑洞:

第一眼看到该图片的感觉是,确实图片中的几根蜡烛生动形象的描述了大小写字母的关系,那么是哪些字母,图片并没有明确的表示,看来只有看源代码了。

依旧是一大堆乱七八糟的字符,与上一个问题的字符内容不同的时候,这里面的字符是由大小写英文字母组成。

这里有一个误区,通往下一个网页的关键单词的组成字母存在于这样的字符串XXXxXXX中,并且那些字母就是被那3个大写字母包围的小写字母,重新审核他给的原问题:

One small letter, surrounded by EXACTLY three big bodyguards on each of its sides.

注意里面的单词EXACTLY,也就是说刚好是3个大写字母而不是4个大写字母,因此为了确保,旁边三个大写字母两边必须分别是一个小写字母!所以答案就很明显了,该单词的字母存在于由9个字母组成的字符串中,排列形式是xXXXxXXXx。也就是说从网页源码的一大堆杂乱的字符串中找出符合xXXXxXXXx这样排列的子字符串就可以得出需要的小写字母,然后将那些满足条件的小写字母连接好就是通往答案的单词。

首先,那一堆乱七八糟的字符已经上传到实验楼的服务器上了,大家输入以下命令行下载文档:

wget http://labfile.oss.aliyuncs.com/courses/409/character.txt

废话有点多,看看大神们的解决方案:

方法一:正则匹配,最简单粗暴的方式

import re
text = open(‘character.txt‘).read()
"".join(re.findall(‘[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]‘,text))

这里用到了正则表达式re模块,关于正则表达式的内容,推荐学习 30分钟入门正则表达式,里面的正则表达式测试工具也是相当好用,以下介绍正则语句[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]:

  1. 首先,函数re.fidall(regex,str)是返回所有str内容中被正则语句regex匹配的所有字符串;
  2. [^A-Z]是匹配一个非大写字母;
  3. [A-Z]{3}指的是匹配3个连续的大写字母;
  4. [a-z]匹配一个小写字母。

方法二:使用string模块,最常规的方法

import string

s = open(‘character.txt‘,‘r‘).read()
lwr=string.lowercase
upr=string.uppercase

for n in range(1,len(s)-7):
    if (s[n-1] in lwr) and (s[n] in upr) and (s[n+1] in upr)    and (s[n+2] in upr) and (s[n+3] in lwr)    and (s[n+4] in upr) and (s[n+5] in upr)    and (s[n+6] in upr) and (s[n+7] in lwr):
        print s[n+3]

该方法没有讲究什么技巧,直接判断是否存在满足条件xXXXxXXXx的字符,然后把最中间的那个小写字母x给打印出来。

方法三:使用reduce函数调用子函数处理字符串

创建一个test_0.py文件,编写代码:

# coding:utf-8

def level_3(state, c):
    if not c.isalpha():  # 先判断字符是否为是字母
        return state

    if state: # 如果元组不为空
        chars_found, state_lower, upper_count = state
    else:
        state_lower = ""
        upper_count = 0
        chars_found = ""

    if c.islower():  # 如果字符是小写字母
        if upper_count == 3:  # 如果大写字母的数量为3
            if state_lower:  # 如果找到符合条件的小写字母
                chars_found += state_lower

            state_lower = c
        else:
            state_lower = ""

        upper_count = 0

    else:
        upper_count += 1

    return chars_found, state_lower, upper_count

if __name__ == "__main__":
    with open(‘character.txt‘) as f:
        s = f.read()
    s += "x" # 最右边添加一个小写字母
    print reduce(level_3, s, ())[0]

关于reduce函数,可以使用help命令查阅该函数的英文文档。用法如下:

  • reduce(function, sequence[,initial])函数中有两个参数和一个可选参数;function参数是一个函数,sequence参数是一个序列,使用reduce函数可以给函数function提供参数进行累加,参数来源于序列sequence的元素;
  • 比如:reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])就是计算((((1+2)+3)+4)+5);也就是function的结果和`sequence中下一个序列元素作为function的参数;
  • initial参数如果被给出,那么它将作为初始化结果,缺省情况下是空的。

那么应用在test_0.py文件中的reduce函数的作用就是,使用level_3函数对字符串s中的每一个字母进行处理,且函数初始返回结果state为空元祖()

五、第4个挑战题

小编脑洞

这一次的题目没有给任何提示,只是上面的一张图片,图片的名字为chainsaw,意思是肢解,用链锯隔开,那么是不是意味着接下来的东西是一个

经过这几次闯关,我们知道一般答案都隐藏在网页源代码中,审查元素,发现:

首先,有提示说,使用urllib或许能帮助我们,但是不要一直追踪链接,因为一直点击链接是没有终止循环的那一天,因为链接数量超过400个。

那么答案应该就是隐藏在链接中,刚好在图片的下面找到一个href链接语句:a href="linkedlist.php?nothing=12345"那么点击这个链接查看结果:

提示下一个nothing的值为44827,那么把链接地址:http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=12345中的nothing值改为44837就是了,其实这个时候不用查看链接的结果也知道,下一个页面也是这样提示nothing的值,根据源代码中的提示,这个链接不超过400个,因此如果手动点击400次会非常浪费时间而且无聊,那么就使用Pythonurllib模块玩一玩吧~

关于这个题目,因为要访问外网才能遍历链接(只有会员才能在实验环境中访问外网,若需访问外网请点击链接购买会员。),因此在这里只介绍一种方法,希望免费用户在自己的电脑上进行编程验证代码,并用自己的方式解决这个问题:

import urllib
import urllib2

data= {}
number = ‘12345‘

# 循环400次
for i in range(400):
    # 给字典`data`中的`nothing`赋初始值
    data[‘nothing‘] = number
    url_values = urllib.urlencode(data)
    url = ‘http://www.pythonchallenge.com/pc/def/linkedlist.php‘
    full_url = url + ‘?‘ + url_values

    foo = urllib2.urlopen(full_url)
    foo = foo.read()
    print foo  # 打印网页内容
    foo = foo.split(" ")  # 使用空格作为分隔符分开单词

    number = [i for i in foo if i.isdigit()][0]

然后就会循环400次打印捕捉到的网页内容

然后你根据这些内容跳链接,实际上这种方式有障碍,那就是遇到网页内容中没有数字的内容时会报错,例如在上面的脚本运行一段时间后会打印以下内容:

注意看最后一句,Yes. Divide by two and keep going.该句中没有任何数字而是提示,需要将前一个数字除以2才能继续前进。

遇到这种情况,通常是把初始的number改为92118/2,但是这样显得不怎么pythonic,而且,一般答案应该是给一个链接比如内容应该是以.html结尾的,因此需要把以上情况都考虑到原始脚本的条件中,修改以上脚本:

import re
import urllib

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

nothing = "12345"
# 匹配以数字结尾的字符串以继续搜索
search = re.compile(" (\d*)$")
匹配最终的网页类型字符串以跳出循环
search_html = re.compile("\.html$")  # 

for i in xrange(400):
    print "%s: " % nothing,

    line = urllib.urlopen( "%s%s" % (url,nothing) ).read()
    print line

    # 如果找到最终的网页链接则终止查询
    if search_html.findall (line):
        break

    match = search.findall (line)
    if match:
        # 下一个nothing值
        nothing = match [0]
    else:
        # 上一个nothing除以二
        nothing = str(int(nothing) / 2)

其实这个题目的原理很简单,一直搜索链接,遇到数字就进入下一次搜索,遇到非数字就考虑是否是.html结尾,不是就给上一个数字除2继续搜索。

六、第5个挑战题

问题:读出来

小编脑洞

依旧源代码:

注意语句:<peakhell scr="banner.p">以及peak hell sound familiar?

了解到应该是一个跟peak hell读音非常接近的Python用法,由于banner.p是一个文件,那么文件处理中跟peak hell发音相似的只有pickle模块,那么问题就很明显了,应该就是让我们使用pickle模块对banner.p文件进行处理,答案应该就隐藏在经过处理后的字符串中。

那么小编已经把文件banner.p放在个人的git仓库中了,请输入以下命令下载文件并查看:

git clone http://git.shiyanlou.com/wing1995/shiyanlou_cs409
cd shiyanlou_cs409
ls

pickle模块介绍:

pythonpickle模块实现了基本的数据序列和反序列化。通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储;通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。

由于数据文件有了,那么要做的就是使用pikle模块中load()函数从文件中重构python对象:

import pickle

# 以二进制形式读取文件内容
data = pickle.load(open(‘banner.p‘, ‘rb‘))
for each in data:
    print each

观察数据打印结果:

结果是一个列表,列表里面的数据非常有规律,列表里面包含有多个子列表,子列表的内容是由一到多个元组组成,那些元祖里面都是只有2个元素,第一个元素是字符型,要么是‘ ‘,要么是‘#‘,第二个元素是数字,仔细分析这些子列表,发现:

很有意思,都是有规律的,猜测:子列表中每一个元祖代表字符(第一个元素)出现的次数(第二个元素)。

尝试打印:

for each in data:
    "".join([i[1] * i[0] for i in each])

见证奇迹的时刻到来了:

额,好吧,实验楼虚拟环境显示图不全,再来一张比较明显的:

这明明就是单词channel!

看来答案非常明显,把url链接中单词peak换成channel就好。所有大家看着这个字符图片是不是觉得很有意思,关于这个字符图画的拓展课程,实验楼也已经写好,在Python图片转字符画

鉴于实验楼的命令行窗口显示不清楚,我们先把输出的字符写入到图片文件中查看字符画:

import pickle, pprint
import Image, ImageDraw

im = Image.new("1", (95, 24))
draw = ImageDraw.Draw(im)
data = pickle.load(open(‘banner.p‘, ‘rb‘))
line = 0

for i in data:
    xpos = 0
    for j in i:
        if j[0] == " ":
            draw.line([(xpos,line), (xpos+j[1],line)], 255)
        xpos += j[1]
    line += 1
im.save("banner.bmp")

以上代码需要安装Image模块后才能实现,关于该模块的安装和学习在课程Python图片转字符画课程Python破解验证码里面有详细介绍,欢迎各位学完那些课程再来查看以上代码是否更加熟悉。

当然,不一定要直接转成图片文件,也可以直接把字符串的输出结果写入到文件中。

七、作业

请完成以下作业,并写入实验报告,不一定要做出结果,但是一定要写出思路以及对这门课程的总结:

问题:请访问挑战题链接查看源代码,找到通往下一个网址的链接。(提示:与zip有关)

时间: 2024-10-11 06:17:49

PythonChallenge_2的相关文章

pythonchallenge(二)

PythonChallenge_2 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou,密码shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面上的程序: 1. LX终端(LXTerminal): Linux命令行终端,打开后会进入Bash环境,可以使用Linux命令 2. Firefox:浏览器,可以用在需要前端界面的课程里,只需要打开环境里写的HTML/JS页面即可 3. GVim:非常好用的编辑器,最简单的用法可