一丶单线程+多任务的异步协程

特殊函数

# 如果一个函数的定义被async修饰后,则该函数就是一个特殊的函数

async def get_request(url):
    print('正在请求~~', url)
    await asyncio.sleep(2)
    print('请求结束!!', url)

协程对象

#  - 对象: 特殊函数被调用后,函数内部的实现语句不会被立即执行,然后该函数调用会返回一个协程对象。
#  - 结论: 协程对象==特殊的函数调用


### c 是 协程对象
    # 函数调用: 返回的是一个协程对象
c = get_request(url)  # <generator object get_request at 0x000001C626DCF048

任务对象

# 任务对象
    - 1.起始就是对协程对象的进一步封装。
    - 2.结论:任务对象==高级的协程对象==特殊的函数调用
    - 3.绑定回调:
        - 回调函数什么时候被执行?
            - 任务对象执行结束后执行回调函数
        - task.add_done_callback(func)
            - func必须要有一个参数,该参数表示的是该回调函数对应的任务对象
            - 回调函数的参数.result():任务对象对应特殊函数内部的返回值
                
                
                
### 任务对象
task = asyncio.ensure_future(c)

### 绑定回调函数 parase ,传入函数名即可
task.add_done_callback(parase)

事件循环对象

# 事件循环对象
    - 作用:将其内部注册的任务对象进行异步执行。

    
### 创建一个事件循环对象
loop = asyncio.get_event_loop()
# 循环执行 ,  将任务对象注册到事件循环对象中并且开启事件循环
loop.run_until_complete(asyncio.wait(tasks_list))  

编码流程

# 编码流程

    - 定义特殊函数
    - 创建协程对象
    - 封装任务对象
    - 创建事件循环对象
    - 将任务对象注册到事件循环中且开启事件循环对象

注意事项***

- 注意:在特殊函数内部的实现语句中不可以出现不支持异步的模块对应的代码,否则
    就是终止多任务异步协程的异步效果

- 注意重点:requests模块不支持异步,在多任务的异步协程中不可以使用requests

aiohttp模块的使用

- 概念:支持异步的网络请求模块
- 编码流程:
    - 写基本架构:
            with aiohttp.ClientSession() as s:
                with s.get(url) as response:
                    page_text = response.text()
                    return  page_text
    - 补充细节:
        - 添加async关键字
            - 每一个with前加上async
        - 添加await关键字
            - 加载每一步的阻塞操作前加上await
                - 请求
                - 获取响应数据
                
                
# -*-coding:utf-8-*-
# Author:Ds
'''
    多任务的异步爬虫
'''
import asyncio
import time
# aiohttp 异步网络请求模块
import aiohttp

from lxml import etree


async def get_request(url):
    # 开启一个连接请求
    async with aiohttp.ClientSession() as s:
        # 发送一个连接请求
        async with s.get(url=url) as response:
            # await 的使用情况:
                # 请求和响应都存在网络传输,
            page_text =await response.text()
            return page_text

def parase(task):
    '''
        # 解析page_text
    :param task:
    :return:
    '''

    # 获取 执行函数调用的结果
    page_text=task.result()

    # 实例化etree解析对象
    tree=etree.HTML(page_text)
    page_data=tree.xpath('//*[@]/a[1]/span[1]/i/@class')[0]
    print(page_data)


urls = [
    'http://127.0.0.1:5000/ip01',
    'http://127.0.0.1:5000/ip02',
    'http://127.0.0.1:5000/ip01',
    'http://127.0.0.1:5000/ip02',
    'http://127.0.0.1:5000/ip01',
    'http://127.0.0.1:5000/ip02',

]

start_time = time.time()

# 列表 协程对象列表
coroutine_list = []
# 列表 任务对象列表
tasks_list = []
for url in urls:
    ### c 是 协程对象
    # 函数调用: 返回的是一个协程对象
    c = get_request(url)  # <generator object get_request at 0x000001C626DCF048

    ### 任务对象
    task = asyncio.ensure_future(c)
    ### 绑定回调函数
    task.add_done_callback(parase)
    tasks_list.append(task)
    coroutine_list.append(c)

# print(coroutine_list)
# print(tasks_list)

### 创建一个事件循环对象
loop = asyncio.get_event_loop()
# 循环执行 ,  将任务对象注册到事件循环对象中并且开启事件循环
loop.run_until_complete(asyncio.wait(tasks_list))

print('总耗时:', time.time() - start_time)
loop.close()

二丶selenium模块

概述

    - 概念:基于浏览器自动化的一个模块。
    - Appium是基于手机的自动化的模块。
    - selenium和爬虫之间的关联
        - 便捷的爬取到动态加载的数据
            - 可见即可得
        - 便捷的实现模拟登陆

基本使用

    - 基本使用:
        - 环境安装
            - pip install selenium
            - 下载浏览器的驱动程序
                - http://chromedriver.storage.googleapis.com/index.html
                - 浏览器版本和驱动程序的映射关系:
                	https://blog.csdn.net/huilan_same/article/details/51896672

捕获动态数据

# -*-coding:utf-8-*-
# Author:Ds

from selenium import webdriver
import time
from lxml import etree

options=webdriver.ChromeOptions()
options.binary_location = r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

browser=webdriver.Chrome('chromedriver.exe',options=options)
url='https://www.fjggfw.gov.cn/Website/JYXXNew.aspx'
browser.get(url)
time.sleep(1)

# page_source : 获取页面资源 所有数据
page_text=browser.page_source

# 列表存储页面源码数据
all_page_text=[]

for i in range(3):
    next_page_btn=browser.find_element_by_xpath('//*[@]/div[1]/span[1]/a[7]')
    next_page_btn.click()
    time.sleep(1)
    # 往列表里添加一整页数据
    all_page_text.append(browser.page_source)


for page_text in all_page_text:
    tree=etree.HTML(page_text)
    # 获取页面指定动态数据的第一条数据
    title=tree.xpath('//*[@]/div[1]/div/h4/a/text()')[0]
    print(title)

动作链

- 动作链
    - 在使用find系列的函数进行标签定位的时候如果出现了NoSuchElementException如何处理?
        - 如果定位的标签是存在于iframe标签之下的,则在进行指定标签定位的时候
            必须使用switch_to.frame()的操作才可。
# -*-coding:utf-8-*-
# Author:Ds
from selenium import webdriver
import time
from lxml import etree

options=webdriver.ChromeOptions()
options.binary_location = r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

browser=webdriver.Chrome('chromedriver.exe',options=options)

browser.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')

time.sleep(1)

# 传入iframe的ID #frame的参数为iframe标签的id属性值
browser.switch_to.frame('iframeResult')
# 找到要执行动作的标签元素
div_tag=browser.find_element_by_id('draggable')

# 实例化一个动作链对象, 传入浏览器对象
action=webdriver.ActionChains(browser)
action.click_and_hold((div_tag))

for i in range(3):
    # 执行动作链 , perform() 表示立即执行
    action.move_by_offset(5,0).perform()
    time.sleep(0.1)

browser.quit()

无头浏览器

- 无头浏览器
    - phantomjs
    - 谷歌无头浏览器(推荐)
# -*-coding:utf-8-*-
# Author:Ds
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
import time
from lxml import etree


# 设置浏览器无头信息
chrome_options=Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.binary_location=r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"


browser=webdriver.Chrome('chromedriver.exe',chrome_options=chrome_options)

browser.get('https://www.taobao.com')

# 截图保存
browser.save_screenshot('./taobao_index.png')
print(browser.page_source)

如何规避selenium被监测到的风险

- 如何规避selenium被监测到的风险
    - 网站可以根据:'window.navigator.webdriver' 的返回值鉴定是否使用了selenium
        - undefind:正常
        - true:selenium
# -*-coding:utf-8-*-
# Author:Ds
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
import time
from lxml import etree


# 设置浏览器无头信息
chrome_options=Options()
chrome_options.binary_location=r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

### 设置 不被监测
#  console输入: 'window.navigator.webdriver' 为true则检查出是使用的 selenium .
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])


browser=webdriver.Chrome('chromedriver.exe',chrome_options=chrome_options)

browser.get('https://www.taobao.com')

三丶12306的模拟登录

# -*-coding:utf-8-*-
# Author:Ds
# 打码平台
from CJY import Chaojiying_Client
# 自动化工具
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver import ActionChains
import time

headers={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
}



# 处理图片模块
from PIL import Image

# 处理图片验证码
def transform(imgPath,imgType):
    chaojiying=Chaojiying_Client('打码平台用户','打码平台密码','901821')
    # chaojiying2=Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
    img=open(imgPath,'rb').read()
    return chaojiying.PostPic(img,imgType)['pic_str']

# 自动化工具设置
chrome_options=Options()
chrome_options.binary_location=r"E:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

# 生成浏览器对象
browser=webdriver.Chrome('chromedriver.exe',chrome_options=chrome_options)

# 发送请求
browser.get('https://kyfw.12306.cn/otn/login/init')
time.sleep(3)

# 截取 整张12306的页面
browser.save_screenshot('main.png')

# 在main.png 中 截取下载验证码
img_tag=browser.find_element_by_xpath('//*[@]/div/ul[2]/li[4]/div/div/div[3]/img')

# 获取 验证码图片的位置和大小
location=img_tag.location # 最左下角位置(x,y)
size=img_tag.size # img标签对应的图片大小
print(location,size)

# 裁剪范围
rangle=(int(location['x']),int(location['y']),int(location['x'])+size['width'],int(location['y'])+size['height'])
print(rangle) # 左下坐标  和 右上坐标

# 裁取验证码
i=Image.open('./main.png')
frame=i.crop(rangle)
frame.save('./code.png')


# 解析验证码中的内容 , 获得坐标
result=transform('./code.png',9004)
print(result)
time.sleep(2)
# 260,140|260,139  ==> [[260,140],[260,139]]
# 构建数据  '266,77|263,168|199,147'
# all_list=[[x] for x in result.split('|')]
# print(all_list)
all_list=[]
for el in  result.split('|'):
    x,y=el.split(',')
    all_list.append([int(x),int(y)])
print(all_list)

# 循环 打码平台识别出的图片坐标
for x,y in all_list:
    # 执行点击动作链
    ActionChains(browser).move_to_element_with_offset(img_tag,x,y).click().perform()
    time.sleep(0.5)


# 填写用户名
username=browser.find_element_by_xpath('//*[@]')
username.send_keys('XXXX')
# 填写用户密码
password=browser.find_element_by_xpath('//*[@]')
password.send_keys('XXXX')

time.sleep(1)

# 点击登录
login_btn=browser.find_element_by_xpath('//*[@]')
login_btn.click()

# 获取页面资源,保存到本地
page_text=browser.page_source
with open('./traintest.html','w' ,encoding='utf-8') as f:
    f.write(page_text)
time.sleep(8)

# 释放浏览器对象资源
browser.quit()