zoukankan      html  css  js  c++  java
  • 3-爬虫-模拟登录、图片验证码处理、多任务异步爬虫(进程池、协程、生产者消费者模式)

    今日内容

    • 模拟登录
    • 异步爬虫

        - 线程池
        - 单线程+多任务异步协程
        - 生产者消费者模式

    模拟登录

    • 验证码的识别
      • 线上的打码平台
        • 超级鹰
          • url:https://www.chaojiying.com/about.html
          • 使用流程:
            • 注册:注册一个用户中心的账号
            • 登录:用户中心的身份
              • 创建一个软件ID: 899370
              • 下载示例代码
        • 云打码
    • 动态变化的请求参数
      • 动态变化请求参数的处理
        • 一般会隐藏在前台页面中
        • 是由相关的js函数动态生成

    超级鹰的示例代码

    #!/usr/bin/env python
    # coding:utf-8
    
    import requests
    from hashlib import md5
    
    class Chaojiying_Client(object):
    
        def __init__(self, username, password, soft_id):
            self.username = username
        password =  password.encode('utf8')
            self.password = md5(password).hexdigest()
            self.soft_id = soft_id
            self.base_params = {
                'user': self.username,
                'pass2': self.password,
                'softid': self.soft_id,
            }
            self.headers = {
                'Connection': 'Keep-Alive',
                'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
            }
    
        def PostPic(self, im, codetype):
            """
            im: 图片字节
            codetype: 题目类型 参考 http://www.chaojiying.com/price.html
            """
            params = {
                'codetype': codetype,
            }
            params.update(self.base_params)
            files = {'userfile': ('ccc.jpg', im)}
            r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
            return r.json()
    
        def ReportError(self, im_id):
            """
            im_id:报错题目的图片ID
            """
            params = {
                'id': im_id,
            }
            params.update(self.base_params)
            r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
            return r.json()
    
    
    if __name__ == '__main__':
        chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰用户名的密码', '96001')    #用户中心>>软件ID 生成一个替换 96001
        im = open('a.jpg', 'rb').read()                                                    #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
        print chaojiying.PostPic(im, 1902)                                                #1902 验证码类型  官方网站>>价格体系 3.4+版 print 后要加()
    超级鹰示例代码

    识别古诗文网中的验证码图片

    import requests
    from lxml import etree
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
    }
    
    
    
    def transdform_code_img(img_path,img_type):
        chaojiying = Chaojiying_Client('超级鹰账号', '密码', '软件ID')    #用户中心>>软件ID 生成一个替换 96001
        im = open(img_path, 'rb').read()                                                    #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
        return chaojiying.PostPic(im, img_type)['pic_str']
    
    
    main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
    page_text = requests.get(url=main_url,headers=headers).text
    #解析验证码图片地址
    tree = etree.HTML(page_text)
    img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
    img_data = requests.get(url=img_src,headers=headers).content
    with open('./code.jpg','wb') as fp:
        fp.write(img_data)
    
    
    transdform_code_img('./code.jpg',1004)

    实现模拟登录

    sess = requests.Session()
    
    main_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
    page_text = sess.get(url=main_url,headers=headers).text
    #解析验证码图片地址
    tree = etree.HTML(page_text)
    img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
    img_data = sess.get(url=img_src,headers=headers).content
    with open('./code.jpg','wb') as fp:
        fp.write(img_data)
    
    __VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
    __VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]')[0]    
    code_text = transdform_code_img('./code.jpg',1004)
    print(code_text)
    
    login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
    data = {
        '__VIEWSTATE': __VIEWSTATE,
        '__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
        'from': 'http://so.gushiwen.org/user/collect.aspx',
        'email': '账号',
        'pwd': '密码',
        'code': code_text,
        'denglu': '登录',
    }
    login_page_text = sess.post(url=login_url,headers=headers,data=data).text
    with open('./login.html','w',encoding='utf-8') as fp:
        fp.write(login_page_text)

    异步爬虫

    线程池

    import requests
    import time
    from multiprocessing.dummy import Pool  # 线程池
    from lxml import etree
    
    
    # 异步爬虫
    start = time.time()
    
    
    def get_request(url):
        page_text = requests.get(url).text
        return page_text
    
    
    def parse(page_text):  # 数据解析
        tree = etree.HTML(page_text)
        a_href = tree.xpath('//a[@id="feng"]/@href')[0]
        print(a_href)
    
    
    urls = ['http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/tom']
    
    pool = Pool(3)
    # 使用get_request将urls列表中每一个列表元素进行指定操作
    page_text_list = pool.map(get_request, urls)  # 异步请求,返回结果也是一个列表
    pool.map(parse, page_text_list)  # 异步解析
    
    print('总耗时:', time.time() - start)

    flask搭建的server

    #!/usr/bin/env python 
    # -*- coding:utf-8 -*-
    from flask import Flask, render_template
    from time import sleep
    
    # 安装flask模块
    
    # 1.实例化app对象
    app = Flask(__name__)
    
    
    @app.route('/main')
    def main():
        return 'i am main'
    
    
    @app.route('/bobo')
    def index1():
        sleep(2)
        return render_template('test.html')
    
    
    @app.route('/jay')
    def index2():
        sleep(2)
        return render_template('test.html')
    
    
    @app.route('/tom')
    def index3():
        sleep(2)
        return render_template('test.html')
    
    
    if __name__ == "__main__":
        app.run()
    flask搭建的server

    协程

       特殊的函数:表示一组指定的操作
        - 如果一个函数的定义被async修饰了,则该函数就变成了一个特殊的函数
        - 特殊之处:
          - 1.刚该特殊函数被调用后,函数内部的语句不会被立即执行
          - 2.该函数调用后会返回一个协程对象

       协程
        - 协程对象。可以由特殊函数调用获得。
        - 一组指定操作      =     特殊函数      =      协程对象
        - 协程对象      =     一组指定操作


      任务
        - 就是高级的协程对象。任务对象就是对协程进行了进一步封装。
        - 任务对象      =       协程       =      特殊函数      =      一组指定操作
        - 任务对象       =       一组指定的操作
        - 任务对象可以给它绑定一个回调
          

      事件循环
        - 可以当做是一个容器对象。该容器是用来装载任务对象。
        - 当我们有了一个或多个任务对象,可以将任务对象注册/装载到事件循环对象中
        - 当事件循环对象启动后,就可以异步的执行其内部装载的任务对象。

    import asyncio
    from time import sleep
    import time
    
    
    # 特殊函数,前边加了async就是特殊函数
    async def get_request(url):
        print('正在请求:', url)
        sleep(2)
        print('请求结束:', url)
        return 123
    
    
    def parse(task): #必须要有一个参数,就是回调函数的调用者(任务对象)
        print('i am parse,参数task=',task)
        print(task.result())#特殊函数return的返回值
    
    
    # 特殊函数执行的到协程对象
    c = get_request("www.1.com")
    
    
    # 写成对象进一步封装得到任务对象
    task = asyncio.ensure_future(c)
    # 单独的任务对象也可以执行,但如果任务对象想要异步执行就要把它注册到事件循环对象中执行
    
    
    #给任务对象绑定回调函数
    task.add_done_callback(parse)
    
    
    # 创建事件循环对象
    loop = asyncio.get_event_loop()
    
    
    # 将任务对象注册到事件循环对象中,并且启动事件循环对象
    loop.run_until_complete(task)

    多任务

    注意:在特殊函数实现内部不可以出现不支持异步的模块代码,否则会中断整个异步效果

    wait(tasks)
      - 该方法的参数一定得是一个任务列表。可以将任务列表中的每一个任务对象进行可挂起操作,这样当任务对象想要被挂起就才可以挂起
      - 挂起:挂起任务对象就是说让当前的任务对象交出cpu的使用权


    await关键字
      - 在异步中确保阻塞操作被执行

    import asyncio
    from time import sleep
    import time
    start = time.time()
    #特殊的函数
    #注意:在特殊函数实现内部不可以出现不支持异步的模块代码,否则会中断整个异步效果
    async def get_request(url):
        print('正在请求:',url)
        await asyncio.sleep(2)  # await等待阻塞操作执行
        print('请求结束:',url)
        return url
    
    urls = ['www.1.com','www.2.com','www.3.com']
    tasks = [] #任务列表
    for url in urls:
        #返回三个协程对象
        c = get_request(url)
        #返回三个任务对象
        task = asyncio.ensure_future(c)
        tasks.append(task)
    
    loop = asyncio.get_event_loop()
    #wait()?
    loop.run_until_complete(asyncio.wait(tasks))
    
    print('总耗时:',time.time()-start)

    aiohttp:支持异步的网络请求模块

    安装aiohttp模块:pip install aiohttp

    aiohttp编码流程

    # 1.写出一个大致架构
    async def get_request(url):
        with aiohttp.ClientSession() as s:
            with s.get(url) as response:
                page_text = response.text()
                return page_text
    
    # 2.补充细节:
    # 在所有with前加上async关键字
    # 在相关阻塞操作前加上await关键字
    async def get_request(url):
        async with aiohttp.ClientSession() as s:
            async with await s.get(url) as response:  # 发起请求返回响应对象
                page_text = await response.text()
                return page_text               

     多任务异步爬虫

    import asyncio
    from time import sleep
    import time
    import requests
    import aiohttp
    from lxml import etree
    
    start = time.time()
    
    
    # 特殊的函数
    # 注意:在特殊函数实现内部不可以出现不支持异步的模块代码,否则会中断整个异步效果
    # async def get_request(url):
    #     #reqeusts是不支持异步
    #     page_text = requests.get(url).text
    #     return page_text
    def parse(task):  # 数据解析
        page_text = task.result()
        tree = etree.HTML(page_text)
        print(tree.xpath('//img/@src'))
    
    
    async def get_request(url):
        # 创建了一个请求对象
        async with aiohttp.ClientSession() as s:
            # requests:get(url,params,headers,proxies)
            # aiohttp.get(url,params,headers,proxy='https://ip:port')
            async with await s.get(url) as response:  # 发起请求返回响应对象
                # 获取响应数据
                # text()返回字符串形式响应数据
                # read()返回byte类型的响应数据
                page_text = await response.text()
                return page_text
    
    
    urls = ['http://127.0.0.1:5000/jay', 'http://127.0.0.1:5000/bobo', 'http://127.0.0.1:5000/tom']
    
    tasks = []
    for url in urls:
        c = get_request(url)
        task = asyncio.ensure_future(c)
        task.add_done_callback(parse)
        tasks.append(task)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    
    print('总耗时:', time.time() - start)

     生产者消费者模式

    import threading
    import requests
    from lxml import etree
    import os
    from urllib import request
    from queue import Queue
    
    
    class Producer(threading.Thread):
        headers = {
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
        }
    
        def __init__(self, page_queue, img_queue, *args, **kwargs):
            super(Producer, self).__init__(*args, **kwargs)
            self.page_queue = page_queue
            self.img_queue = img_queue
    
        def run(self):
            while True:
                if self.page_queue.empty():
                    break
                url = self.page_queue.get()
                self.parse_page(url)
    
        def parse_page(self, url):
            response = requests.get(url=url,headers=self.headers)
            text = response.text
            html = etree.HTML(text)
    
            img_list = html.xpath('//div[@class="page-content text-center"]/div/a/img')
            for img in img_list:
                img_url = img.xpath('./@data-original')[0]
                img_name = img.xpath('./@alt')[0]+'.jpg'
                self.img_queue.put((img_url, img_name))
    
    
    
    class Consumer(threading.Thread):
        def __init__(self, page_queue, img_queue, *args, **kwargs):
            super(Consumer, self).__init__(*args, **kwargs)
            self.page_queue = page_queue
            self.img_queue = img_queue
    
        def run(self):
            while True:
                if self.page_queue.empty() and self.img_queue.empty():
                    break
                img_url, img_name = self.img_queue.get()
                request.urlretrieve(img_url, "imgs/" + img_name)
                print(img_name + " 下载完成!")
    
    # 定义一个主方法,该方法向处理方法中传值
    def main():
        page_queue = Queue(50) #存储页码链接
        img_queue = Queue(100)#存储解析出来的图片链接
        #想要爬取前10也的数据
        for x in range(1, 11):
            url = "https://www.doutula.com/photo/list/?page=%d" % x
            page_queue.put(url) #将10页的页码链接加入到了page_queue
    
        for x in range(3):
            t = Producer(page_queue, img_queue)
            t.start()
    
        for x in range(3):
            t = Consumer(page_queue, img_queue)
            t.start()
    
    
    if __name__ == '__main__':
        main() 
  • 相关阅读:
    MySQL8.0.x免安装配置
    Java中锁的实现与内存语义
    并发中的volatile
    「LOJ #2163」「POI2011」Tree Rotations
    「CodeChef REBXOR」Nikitosh and xor
    「Codeforces 429D」Destiny
    「Luogu P2042」「NOI2005」维护数列
    「SPOJ SEQ」 Recursive Sequence
    「GCJ 2008 Round 1A C」numbers
    「LOJ #6016」崂山白花蛇草水
  • 原文地址:https://www.cnblogs.com/wgwg/p/13255042.html
Copyright © 2011-2022 走看看