Python - 特殊函数+aiohttp+selenium
单线程多任务的异步爬虫
-
协程基础
- 特殊函数:
- 就是async关键字修饰的一个函数的定义
- 特殊之处
- 特殊函数被调用后会返回一个协程对象
- 特殊函数调用后内部的程序语句没有被立即执行
- 特殊函数:
-
协程
- 对象 协程==特殊的函数 ,协程表示的就是一组特定的操作
-
任务对象
- 高级的协程(对协程的进一步封装)
- 任务对象 =协程==特殊的函数
- 任务对象==特殊的函数
- 任务对象 =协程==特殊的函数
- 绑定回调:
- task.add_done_callback(task)
- 参数task:当前回调函数对应的任务对象
- task.result():返回的就是任务对象对应的特殊函数的返回值
- task.add_done_callback(task)
- 高级的协程(对协程的进一步封装)
-
事件循环对象
- 创建事件循环对象
- 将任务对象注册到改对象中并且开启该对象
- 作用:
- 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)
- 动作连
- 无头浏览器
- 规避检测
- selenium和爬虫之间的关联