Python 爬虫闯关(第四关)- 续

168 阅读5分钟
不正经开场白

上回 Python 爬虫闯关(第四关) 中,我们发现程序执行过程非常漫长,将近 24 分钟啊!另外页面也有提示说:网页会慢半拍。那么也许需要一个高效率的方法进行解析,多线程?

1

题意再理解

本关的题目内容,这里再简单说一下:

页面给出了闯关所需密码的随机位置,以及该位置的值,我们需要遍历所有页面,获取到 100 位的密码。

不清楚的翻下历史。

其中,值得注意的两点:

  1. 每一页的载入速度非常慢(正如页面内容所述),大约需要 15 秒,这是黑板课后台故意设置的;

  2. 每个页面是动态加载的,每次获取到的密码的位置并不是相同的,也就是检索完 13 页数据也不能得到 100 位的密码,需要重复检索以得到其余位置的密码。

由此可见,必须使用多线程才能迅速得到密码。

这就是多线程

2

闯关思路

多线程编程使用 threading 模块,这里我们对 Python 多线程我们不再详细展开,后面有机会再给大家说,有兴趣的也可以自己学习下。

登录过程和第 3 关一样,基本只需要在第 3 关的基本上增加获取密码的功能就可以了。

  1. 初始化一个字典,用来存放密码;

  2. 每个线程都不断获取页面的全部位置和值,并将它们更新到字典中;

  3. 判断字典长度,如果字典的长度小于 100,则继续抓取页面数据并跟新字典,直到长度满足 100;

  4. 将字典中的数据转换为字符串,然后通过 post 提交到服务器进行登录;

3

实现

代码有点长,代码源文件可以回复【第四关】获取。

# coding=utf-8import requests, bs4import threadingimport timedef login():    # 登录URL,获取cookie    login_url = 'http://www.heibanke.com/accounts/login/?next=/lesson/crawler_ex03/'    login_data = {'username':'liuhaha', 'password':'123456'}    # 获取默认cookie    response = requests.get(url)    if response.status_code == 200:        print('Welcome')    cookies = response.cookies    # 登录     login_data['csrfmiddlewaretoken'] = cookies['csrftoken']    login_response = requests.post(login_url, allow_redirects=False, data=login_data, cookies=cookies)    if login_response.status_code == 200:        print('login sucessfully')    return login_response.cookiesdef getPassword(page):    global pwd_data    payload['page'] = page    print(threading.currentThread().getName() + ', loading %s?page=%s ...' %(pwd_url, page))    pwd_response = requests.get(pwd_url, cookies=cookies, params=payload)    soup = bs4.BeautifulSoup(pwd_response.text, "html.parser")    pwd_pos = soup.findAll('td', {'title':'password_pos'})    pwd_value = soup.findAll('td', {'title':'password_val'})    for index in range(len(pwd_pos)):        pwd_data[int(pwd_pos[index].getText())] = pwd_value[index].getText()    print(threading.currentThread().getName() + ', now the pwd_data length is %s' % len(pwd_data))class MyThread(threading.Thread):      def __init__(self, s):          threading.Thread.__init__(self)        self.s = s      def run(self):          global pwd_data        global count        while len(pwd_data) < 100:            count += 1            print('The sub-thread has run %s times' % count)            getPassword(count % 13)if __name__ == '__main__':      start = time.time()    payload = {}    # 存放密码键值对    pwd_data = {}    # 记录运行次数    count = 0    # 题目URL    url = 'http://www.heibanke.com/lesson/crawler_ex03/'     # 获取密码URL    pwd_url = 'http://www.heibanke.com/lesson/crawler_ex03/pw_list/'    # 获取登录成功后的cookie    cookies = login()    threads = []    for i in range(0, 5):  # 线程数,可自定义          thread = MyThread(cookies)        threads.append(thread)        thread.start()    # 等待所有线程完成    for thread in threads:        thread.join()    # 拼接password    password = ''    for key in sorted(pwd_data.keys()):        password = password + pwd_data[key]    print(password)    # 重新登录    playload = {'username':'liuhaha', 'password':password}    playload['csrfmiddlewaretoken'] = cookies['csrftoken']    r = requests.post(url, data=playload, cookies=cookies)    print(u'执行结果:' + str(r.status_code))    if r.status_code == 200:        # print(r.text)        if u"成功" in r.text:            print(u'闯关成功!密码为:' + password)            print(u'用时 %s s' % (time.time() - start))            # break    else:        print(u'Failed')

多线程下,也许是网站限制,发现不管设置几个线程,运行时间总是在 470s 左右,不到 8 分钟,虽说时间仍然很长,但是比单线程版本已经有了明显提升。

有同学后台问我,这个网站怎么设置了多个线程不管用啊,其实我也解决不了,我们了解原理不就好了~

近期可能会有赠书活动,大家多关注哦!

推荐阅读

我的 Python 学习资源分享

如何优雅地使用 rm 防止误删除?

Python 爬虫闯关(第三关)

教你用 Python 来朗读网页

Python 爬取「知识星球」

你还在用 format 格式化字符串?

Python 玩转 Excel

戳原文,有更适合电脑端的版本哦~