1.单线程+多任务的异步协程
特殊函数: 如果一个函数的定义被asyncio修饰后,则该函数就成了一个特殊的函数。 协程: 就是一个协程对象。特殊函数调用的返回就是协程对象。 特殊函数被调用后,函数内部的实现语句不会被立即执行,然后该函数调用就会返回一个协程对象。 特殊函数的调用的返回 == 协程对象。 任务对象(只是在爬虫里面才会有任务对象): 其实就是对协程对象的进一步封装。 结论: 任务对象,就是一个高级的协程对象 任务对象 == 高级的协程对象 == 特殊函数调用的返回 为什么要使用任务对象? 因为任务对象可以绑定回调函数,协程对象和特殊的函数都没有回调函数 事件循环对象:是整个异步中的重要操作,没有它就实现不了异步 作用:将其内部注册的任务对象进行异步执行。
使用单线程+多任务的异步协程的编码流程
1.定义特殊函数
2.创建协程对象
3.封装任务对象
4.创建事物循环对象
5.将任务对象注册到事物循环对象并且开启循环对象
2.代码实现
import asyncio import time from time import sleep start = time.time() # 函数调用 async def get_request(url): # 加上async之后,在调用特殊函数的时候,里面的代码不会被执行,但是会返回一个coroutine(协程)对象 print('get url:',url) await asyncio.sleep(3) # 必须等待阻塞操作执行完毕之后,才执行后面的代码,这一步是在模拟请求网页,如果不等待的话,那么事件循环对象不会等待这段代码执行,所以网页获取不到 print('请求结束',url) return url # 回调函数:必须有一个参数 def parse(task): print('我是回调函数',task.result())
# 创建3个协程对象 urls = [ '1.com','2.com','3.com' ]
# 封装任务列表 tasks_list = [] # 事件循环对象列表 for url in urls: c = get_request(url) # 根据特殊函数返回协程对象 task = asyncio.ensure_future(c) # 创建一个任务对象:基于携程创建 # 绑定回调函数 task.add_done_callback(parse) # parse回调函数名,不加扩号 tasks_list.append(task) loop = asyncio.get_event_loop() # get_event_loop返回一个事件循环对象 loop.run_until_complete(asyncio.wait(tasks_list)) # 将任务对象注册到事件循环对象中,并且开启事件循环。 # asyncio.wait是挂起的意思,当任务出现阻塞的时候挂起,去执行其他任务,如果不加的话,在遇到阻塞的时候不会挂起。有挂起监听的意思。 # 事件循环体现在当任务遇到阻塞是挂起(挂起等待阻塞结束)去执行其他任务。当挂起的任务不阻塞之后,loop事件函数回去执行该任务。事件循环是体现在这里面。但是具体的循环多少次是无法确定的。循环:应该是cpu在任务间来回执行 print(time.time()-start)
注意事项:
1.回调函数什么时候被执行?
任务对象执行结束之后,才执行的函数叫做回调函数
# 回调函数:必须有一个参数,就是该回调函数默认绑定的任务对象
参数的作用是:用来返回该任务对象所对应特殊函数的返回值
注意事项:
在特殊函数内部的实现语句中不可以出现不支持异步的模块对象的代码,否则就会终止多任务异步协程的特殊效果
time模块不支持异步。requests也不支持异步
多任务异步爬虫
# -*- coding: utf-8 -*- # @Time : 2019/10/11 20:52 # @Author : p0st # @Site : # @File : 6多任务异步爬虫.py # @Software: PyCharm import asyncio import requests import time import aiohttp from lxml import etree # 特殊函数:发起请求获取页面源码数据 # async def get_requets(url): # # 其中异步效果失效了,所以变成串行了 # page_text = requests.get(url).text # return page_text async def get_requets(url): # 获取一个请求对象。使用aiohttp在请求结束后必须要关闭这个aiohttp请求对象。 # 但是我们不知道什么时候去手动关闭,所以我们使用with来自动关闭 async with aiohttp.ClientSession() as obj: # 在使用aiohttp对象时,后台requests模块是一样 # 就proxy参数不一样是字符串,不是字典了,是http://ip:port async with await obj.get(url) as response: # 也要关闭response对象 page_text = await response.text() return page_text # 这里使用text()方法来获取字符串的响应数据,使用read来返回bytes类型的数据 # 回调函数 def get_text(task): page_text = task.result() tree =etree.HTML(page_text) print(task,tree.xpath('//*[@id="10"]//text()')) urls = [ 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip2', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip2', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip2', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', 'http://127.0.0.1:5000/ip1', ] start = time.time() # 创建协程对象 tasks = [] for url in urls: c = get_requets(url) task = asyncio.ensure_future(c) # 给任务对象绑定回调函数,用于数据解析 task.add_done_callback(get_text) # get_text tasks.append(task) loop = asyncio.get_event_loop() # 创建时间循环对象 loop.run_until_complete(asyncio.wait(tasks)) print(time.time()-start)
3.aiohttp模块详解
asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。 asyncio的编程模型就是一个消息循环。
异步网络请求模块:aiohttp 是一个支持异步的网络请求模块,是基于asyncio来实现的。 基本架构写法: async def get_requets(url): # 获取一个请求对象。使用aiohttp在请求结束后必须要关闭这个aiohttp请求对象。 # 但是我们不知道什么时候去手动关闭,所以我们使用with来自动关闭 async with aiohttp.ClientSession() as obj: # 在使用aiohttp对象时,后台requests模块是一样 # 就proxy参数不一样是字符串,不是字典了http://ip:port async with await obj.get(url) as response: # 也要关闭response对象 page_text = await response.text() return page_text # 这里使用text()方法来获取字符串的响应数据,使用read()来返回bytes类型的数据,而不是属性text 添加细节: - 添加async关键字 每一个with前面加上saync - 添加await关键字 每一个阻塞前的操作都加上await 请求 获取响应数据
4.selenium
from selenium import webdriver import time #实例化某一款浏览器对象 bro = webdriver.Chrome(executable_path='./l/chromedriver.exe') #基于浏览器发起请求 bro.get('https://www.jd.com/') #商品搜索 #标签定位 search_input = bro.find_element_by_id('key') #往定位到的标签中录入数据 search_input.send_keys('袜子') #点击搜索按钮 btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button') time.sleep(2) btn.click() time.sleep(2) #滚轮滑动(js注入) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') time.sleep(3) bro.quit()
from selenium import webdriver from lxml import etree import time bro = webdriver.Chrome(executable_path='./l/chromedriver.exe') bro.get('https://www.fjggfw.gov.cn/Website/JYXXNew.aspx') time.sleep(1) # 放回当前浏览器显示的所有源码数据,包括动态数据 page_text = bro.page_source #获取前三页页码锁对应的数据 all_oage_text = [page_text] for i in range(3): next_page_btn = bro.find_element_by_xpath('//*[@id="kkpager"]/div[1]/span[1]/a[7]') # 获取下一页的btn的xpath next_page_btn.click()# 点击下一页操作。 time.sleep(1) all_oage_text.append(bro.page_source) # 将循环的数据添加到list中 # 数据解析 for item in all_oage_text: tree = etree.HTML(item) title = tree.xpath('//*[@id="list"]/div[1]/div/h4/a/text()')[0] print(title)
from selenium import webdriver from selenium.webdriver import ActionChains # 动作链抽象的一个类 from lxml import etree import time bro = webdriver.Chrome(executable_path='./l/chromedriver.exe') bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable') time.sleep(1) # bro.switch_to.frame('iframeResult') #frame的参数为iframe标签的id属性值 bro.switch_to.frame('iframeResult')#frame的参数为iframe标签的id属性值,告诉bro去嵌套的页面里面去找 div_tag = bro.find_element_by_id('draggable') # 基于动作链实现活动操作 action = ActionChains(bro) # 告诉以后的行为动作在bro所对应的浏览器里面 action.click_and_hold(div_tag) #d 点击且长按的去操作div_tag for i in range(5): action.move_by_offset(20,0).perform() # 20指的是像素,perform表示让动作链立即执行。 time.sleep(0.1) # time.sleep(3) # bro.quit() # selenuim 的鼠标操作https://www.cnblogs.com/yhleng/p/7508648.html
就是没有界面的浏览器 from selenium import webdriver from time import sleep from selenium.webdriver.chrome.options import Options # 创建一个参数对象,用来控制chrome以无界面模式打开 chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') bro = webdriver.Chrome(executable_path='./l/chromedriver.exe',chrome_options=chrome_options) bro.get('https://www.baidu.com/') sleep(10) bro.save_screenshot('./123.jpg') print(bro.page_source)
import time from selenium.webdriver import Chrome, ChromeOptions options = ChromeOptions() # options.headless = True options.add_experimental_option('excludeSwitches', ['enable-automation']) # 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的 browser = Chrome(r'./l/chromedriver.exe', options=options) browser.get('www.baidu.com') browser.save_screenshot('baidu.png') browser.quit()
pass