在第三关过了之后,一直卡在第三关,我还不太明白是怎么回事,直接靠网址调到第四关。
http://www.heibanke.com/lesson/crawler_ex03/
点击进入页面后也是和第三关一样,需要登录才能进行下一步操作(如果之前登录了,服务器会根据浏览器保存的cookie自动返回登录后的页面)。登录后的页面如图:
根据提示,密码不再是之前的30以内的数字了,是需要找出来的很长的密码。至于怎么找,当前页面没有任何提示,那么我们先随便输入昵称和密码进入下一个页面。跳转的页面如图:
点击“在这里耐心找”按钮,跳转到密码列表的页面:
页面内容给出了密码的随机位置,以及该位置的值,一共有13页,最后一页只有4位,那么密码就有100位。所以需要按照位置顺序简单组合密码的值就可以得到最终的密码了。
值得注意的两点:
1.每一页的载入速度非常慢(正如页面内容所述),大约需要15秒,这是黑板课老师故意的;
2.每个页面是动态加载的,会随机给出100个位置中的8个,而且100个位置中有许多位置是重复的,换言之13页检索完也不能得到100位的密码,需要重复检索以得到其余位置的密码。
由此可见,必须使用多线程才能迅速得到密码。
思路:
多线程编程使用threading模块,登录过程和第3关一样,基本只需要在第3关的基本上增加获取密码的功能就可以了。
先定义一个长度为100的全局list用于保存密码,初始全部置为x(这里也可以再定义一个数组用于标记对应位置密码是否已获取):
pwdlist = ['x' for i in range(0, 100)]
再定义一个全局计数器用于统计已获取密码的位数:
count = 0
当位数达到100时,则终止线程,打印密码。
每个线程都不断访问密码页面(由于每一页都是随机的,于是这里每一次访问的都是第一页),将获得的密码与已有的密码进行比较,若是新的密码(即该位为1),则将其写入pwdlist中,并置count++:
for index in range(0, len(password_pos_list)):
if pwdlist[int(password_pos_list[index]) - 1] == 'x':
count += 1
pwdlist[int(password_pos_list[index]) - 1] = password_val_list[index]
这里对于密码的提取用到了
BeautifulSoup
,登录的方法和第三关的
相同。之前提取相关数字内容使用了正则表达式,但如果一个正则匹配稍有差池,那可能程序就处在永久的循环之中,而且有的小伙伴们也对写正则表达式的写法用得不熟练。这里有个强大的工具,Beautiful Soup,可以很方便地提取出HTML或XML标签中的内容。
# coding=utf-8
import re
import requests
from bs4 import BeautifulSoup
from threading import Thread
login_website = 'http://www.heibanke.com/accounts/login'
pwd_website = 'http://www.heibanke.com/lesson/crawler_ex03/pw_list/'
# 登录记账本
def login_fun():
s = requests.Session()
s.get(login_website) # 访问登录页面获取登录要用的csrftoken
token1 = s.cookies['csrftoken'] # 保存csrftoken
# 将csrftoekn存入字段csrfmiddlewaretoken
dataWebsite1 = {'username': 'ljlljlljl',
'password': '111111',
'csrfmiddlewaretoken': token1
}
s.post(login_website, data=dataWebsite1)
return s
class MyThread(Thread):
def __init__(self, s):
Thread.__init__(self)
self.s = s
def run(self):
global count
global pwdlist
global exit
ruler = re.compile(r'.*>(\d*)<.*') # 提取密码位置和值的正则表达式
while count < 100:
pwdpage = s.get(pwd_website).content
password_pos = BeautifulSoup(pwdpage, 'html.parser').findAll('td', {'title': 'password_pos'})
password_val = BeautifulSoup(pwdpage, 'html.parser').findAll('td', {'title': 'password_val'})
password_pos_list = [] # 密码位置list
password_val_list = [] # 密码值list
if password_pos:
for i in password_pos:
password_pos_list.append(ruler.findall(str(i))[0])
for j in password_val:
password_val_list.append(ruler.findall(str(j))[0])
print self.name
print password_pos_list
print password_val_list
for index in range(0, len(password_pos_list)):
if pwdlist[int(password_pos_list[index]) - 1] == 'x':
count += 1
pwdlist[int(password_pos_list[index]) - 1] = password_val_list[index]
print count
if exit == 0:
exit = 1
print ''.join(pwdlist)
if __name__ == '__main__':
s = login_fun()
exit = 0
count = 0
pwdlist = ['x' for i in range(0, 100)]
for i in range(0, 20): # 线程数,可自定义
thread = MyThread(s)
thread.start()