zoukankan      html  css  js  c++  java
  • Python

    单线程多任务的异步爬虫

    • 协程基础

      • 特殊函数:
        • 就是async关键字修饰的一个函数的定义
        • 特殊之处
          • 特殊函数被调用后会返回一个协程对象
          • 特殊函数调用后内部的程序语句没有被立即执行
    • 协程

      • 对象 协程==特殊的函数 ,协程表示的就是一组特定的操作
    • 任务对象

      • 高级的协程(对协程的进一步封装)
        • 任务对象 =协程==特殊的函数
          • 任务对象==特殊的函数
      • 绑定回调:
        • task.add_done_callback(task)
          • 参数task:当前回调函数对应的任务对象
          • task.result():返回的就是任务对象对应的特殊函数的返回值
    • 事件循环对象

      • 创建事件循环对象
      • 将任务对象注册到改对象中并且开启该对象
      • 作用:
        • loop可以将其内部注册的所有的任务对象进行异步执行
    • 挂起:交出cpu的使用权,

    • 重点:在特殊函数的内部的实现中,不可以出现不支持异步的模块代码,如果出现了则会中断整个的异步效果!!!

    • requests 一定是不支持异步

    • aiohttp是一个支持异步的网络请求模块

      • 环境安装

      • 编码流程

        • 大致的架构

          •   with aiohttp.ClientSession() as s:
                 #s.get(url,headers,params,proxy="http://ip:port")
                 with s.get(url) as response:
                     #response.read()二进制(.content)
                     page_text = response.text()
                     return page_text
            
        • 补充细节

          • 在每一个with前加上async关键字

          • 需要在每一个阻塞操作前加上await关键字

            •   async with aiohttp.ClientSession() as s:
                    #s.get(url,headers,params,proxy="http://ip:port")
                    async with await s.get(url) as response:
                        #response.read()二进制(.content)
                        page_text = await response.text()
                        return page_text
              

    特殊函数

    import asyncio
    from time import sleep
    #特殊函数
    
    async def get_request(url):
        print('正在下载:',url)
        sleep(2)
        print('下载完成',url)
    
        return 'page_text'
    
    #回调函数的定义(一定是一个普通的函数)
    def parse(task):
        #参数表示的就是任务对象
        print('i am callback!!!',task.result())
    
    
    #特殊函数的调用
    c = get_request('wwww.1.com')
    
    #创建一个任务对象
    task = asyncio.ensure_future(c)
    #给任务对象绑定一个回调函数
    task.add_done_callback(parse)
    
    #创建一个事件循环对象
    loop = asyncio.get_event_loop()
    
    # 将任务对象注册到改对象中并且开启该对象
    loop.run_until_complete(task) #让loop执行了一个任务
    

    多任务异步

    import asyncio
    from  time import sleep
    import time
    #特殊的函数
    async def get_request(url):
        print('正在下载',url)
        await  asyncio.sleep(2)
        print('下载完成',url)
        return 'i am page_text!!!'
    def parse(task):
        page_text = task.result()
        print(page_text)
    
    
    # 多任务的异步协程
    start = time.time()
    urls = ['www.1.com','www.2.com','www.3.com']
    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()
    #asyncio.wait(tasks):给每一个任务对象赋予一个可被挂起的权限
    loop.run_until_complete(asyncio.wait(tasks))
    print('总耗时:',time.time()-start)
    

    基于aiohttp的多任务异步爬虫

    import asyncio
    import aiohttp
    import time
    from bs4 import BeautifulSoup
    #将被请求的url全部整合到一个列表中
    urls = ['http://127.0.0.1:5000/bobo','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom']
    start = time.time()
    
    async def get_request(url):
        async with aiohttp.ClientSession() as s:
            #s.get(url,headers,params,proxy="http://ip:port")
            async with await s.get(url) as response:
                #response.read()二进制(.content)
                page_text = await response.text()
                return page_text
    
    def parse(task):
        page_text = task.result()
        soup = BeautifulSoup(page_text,'lxml')
        data = soup.find('div',class_="tang").text
        print(data)
    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)
    

    selenium 的基本使用

    • 概念:基于浏览器自动化的一个模块.

    • 环境的安装

      • pip install selenium
    • selenium和爬虫之间的关联:

      • 模拟登录
      • 便捷的捕获到动态加载的数据(重点)
        • 特点:可见及可得
        • 缺点:效率低
    • selenium的集体使用

    • 动作链:ActionChains,一系列的行为动作

      • 使用流程:
        • 实例化一个动作连对象,需要将指定的浏览器和动作连对象进行绑定
        • 执行相关的连续的动作
        • perform()立即执行动作连制定好的动作
    • 12306模拟登录分析:

      • 验证码的的处理:
    • selenium规避风险

      • 正经打开一个网站进行window.navigator.webdriver的js注入,返回值为undefined
      • 使用selenium打开的页面,进行上述js注入返回的是true
    • 无头浏览器

      • phantomJs
      • 谷歌无头
    selenium 的基本使用代码
    from selenium import webdriver
    from time import sleep
    #结合着浏览去的驱动实例化一个浏览器对象
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    
    #请求的发送
    url = 'https://www.jd.com/'
    bro.get(url)
    sleep(1)
    #标签定位
    # bro.find_element_by_xpath('//input[@id="key"]')
    search = bro.find_element_by_id('key')
    search.send_keys('mac pro')#向指定标签中录入文本数据
    sleep(2)
    btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button')
    btn.click()
    sleep(2)
    #JS注入
    bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    
    #捕获到当前页面的数据
    page_text = bro.page_source
    print(page_text)
    sleep(3)
    
    bro.quit()
    

    selenium 动态加载数据的捕获代码

    #http://125.35.6.84:81/xk/,将药监总局前3页的企业名称进行爬取
    from selenium import webdriver
    from lxml import etree
    from time import sleep
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    url = 'http://125.35.6.84:81/xk/'
    bro.get(url)
    page_text = bro.page_source
    
    all_page_text = [page_text]
    #点击下一页
    for i in range(2):
        nextPage = bro.find_element_by_xpath('//*[@id="pageIto_next"]')
        nextPage.click()
        sleep(1)
        all_page_text.append(bro.page_source)
    
    for page_text in all_page_text:
        tree = etree.HTML(page_text)
        li_list = tree.xpath('//*[@id="gzlist"]/li')
        for li in li_list:
            name = li.xpath('./dl/@title')[0]
            print(name)
    
    
    sleep(2)
    bro.quit()
    

    selenium动作链

    from selenium import webdriver
    from selenium.webdriver import ActionChains#动作连
    from time import sleep
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    
    url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
    
    bro.get(url)
    #NoSuchElementException:定位的标签是存在与iframe之中,则就会抛出这个错误
    #解决方法:switch_to.frame进行指定子页面的切换
    bro.switch_to.frame('iframeResult')
    div_tag = bro.find_element_by_xpath('//*[@id="draggable"]')
    
    #实例化一个动作连对象
    action = ActionChains(bro)
    action.click_and_hold(div_tag)#点击且长按
    
    #perform()让动作连立即执行
    for i in range(5):
        action.move_by_offset(xoffset=15,yoffset=15).perform()
        sleep(2)
    action.release()
    sleep(5)
    bro.quit()
    

    12306模拟登陆代码

    from selenium import webdriver
    from selenium.webdriver import ActionChains
    from time import sleep
    from PIL import Image #安装PIL或者是Pillow
    from CJY import Chaojiying_Client #超级鹰
    
    #封装一个识别验证码的函数
    def transformCode(imgPath,imgType):
        chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
        im = open(imgPath, 'rb').read()
        return chaojiying.PostPic(im, imgType)['pic_str']
    
    
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    
    bro.get('https://kyfw.12306.cn/otn/login/init')
    sleep(2)
    #将当前浏览器页面进行图片保存
    bro.save_screenshot('./main.png')
    #将验证码的局部区域进行裁剪
    #捕获标签在页面中的位置信息
    img_tag = bro.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
    location = img_tag.location#标签的起始位置坐标(左下角坐标)
    size = img_tag.size#标签的尺寸
    #裁剪范围对应的矩形区域
    rangle = (int(location['x']),int(location['y']),int(location['x']+size['width']),int(location['y']+size['height']))
    #使用Image工具进行指定区域的裁剪
    i = Image.open('./main.png')
    frame = i.crop(rangle)#crop就是根据指定的裁剪范围进行图片的截取
    frame.save('code.png')
    
    #调用打码平台进行验证码的识别
    result = transformCode('./code.png',9004)
    print(result) #x1,y1|x2,y2|x3,y3
    
    #x1,y1|x2,y2|x3,y3 ==>[[x1,y1],[x2,y2],[x3,y3]]
    all_list = []#[[x1,y1],[x2,y2],[x3,y3]]
    if '|' in result:
        list_1 = result.split('|')
        count_1 = len(list_1)
        for i in range(count_1):
            xy_list = []
            x = int(list_1[i].split(',')[0])
            y = int(list_1[i].split(',')[1])
            xy_list.append(x)
            xy_list.append(y)
            all_list.append(xy_list)
    else:
        x = int(result.split(',')[0])
        y = int(result.split(',')[1])
        xy_list = []
        xy_list.append(x)
        xy_list.append(y)
        all_list.append(xy_list)
    
    
    for point in all_list:
        x = point[0]
        y = point[1]
        ActionChains(bro).move_to_element_with_offset(img_tag,x,y).click().perform()
        sleep(1)
    
    
    bro.find_element_by_id('username').send_keys('xxxxxx')
    sleep(1)
    bro.find_element_by_id('password').send_keys('xxxx')
    sleep(1)
    
    bro.find_element_by_id('loginSub').click()
    
    sleep(10)
    print(bro.page_source)
    bro.quit()
    
    

    违规检测 + 无头浏览器代码

    #规避检测
    # from selenium import webdriver
    # from selenium.webdriver import ChromeOptions
    # option = ChromeOptions()
    # option.add_experimental_option('excludeSwitches', ['enable-automation'])
    #
    # bro = webdriver.Chrome(executable_path='./chromedriver.exe',options=option)
    #
    # url = 'https://www.taobao.com/'
    #
    # bro.get(url)
    
    #无头浏览器
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    from time import sleep
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    
    bro = webdriver.Chrome(executable_path='./chromedriver.exe',chrome_options=chrome_options)
    url = 'https://www.taobao.com/'
    bro.get(url)
    sleep(2)
    bro.save_screenshot('123.png')
    
    print(bro.page_source)
    
    
    • 单线程+多任务的异步协程
      • 特殊函数
        • 调用,函数内部程序语句不会被立即执行
        • 调用之后会返回一个协程对象
      • 协程
        • 对象表示一组操作
      • 任务对象
        • 高级的协程。任务对象就表示一组指定的操作
        • 绑定回调:
          • add_done_callback(task):task.result()
      • 事件循环
        • eventLoop.
        • 需要将所有的任务对象注册到该对象中
        • 开启事件循环
      • await:
        • 被用作在特殊函数内部(被阻塞)。
      • wait(tasks):
        • 给每一个任务对象赋予一个可被挂起的权限。
      • 注意:特殊函数内部不可以出现不支持异步模块的代码。
      • aiohttp:支持异步的网络请求模块。
        async with aiohttp.ClintSession() as s:
        async with await s.get(url) as response:
        page_text = await response.text()
        return page_text
    • selenium
      • selenium和爬虫之间的关联
        • 模拟登录
        • 便捷获取动态加载的数据
      • 相关的方法和属性:
        • get(url)
        • find_xxx:标签定位
        • switch_to.frame(id)
        • send_keys(xxx)
        • click()
        • page_source
        • js注入:execute_script(jsCode)
        • 动作连
        • 无头浏览器
        • 规避检测
  • 相关阅读:
    学习进度表 06
    课堂练习第七周
    学习进度表 05
    学习进度表 04
    分组情况
    求子数组最大值
    codeforce 8A-8C
    nginx 设置服务,开机启动
    转 ubuntu 安装php
    Nginx小记
  • 原文地址:https://www.cnblogs.com/zgboy/p/13172668.html
Copyright © 2011-2022 走看看