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)
      • 动作连
      • 无头浏览器
      • 规避检测
原文地址:https://www.cnblogs.com/zgboy/p/13172668.html