• 多任务异步爬虫及selenium模块使用


    多任务异步爬虫及selenium模块使用

    知识结构

    • 线程池
    • 多任务的异步协程
      • asyncio
        • 特殊的函数
        • 协程
        • 任务对象
          • 任务对象绑定
        • 事件循环
    from multiprocessing.dummy import Pool
    pool.map(func,alist):
    可以让func回调函数处理alist中的每一个列表元素,这个处理的过程是基于异步。
    
    import requests
    import time
    from multiprocessing.dummy import Pool
    start = time.time()
    # 线程池中有3个线程
    pool = Pool(3)
    urls = [
        'http://127.0.0.1:5000/index',
        'http://127.0.0.1:5000/index',
        'http://127.0.0.1:5000/index'
    ]
    #用作与网络请求(耗时)
    def req(url):
        return requests.get(url).text
    
    page_text_list = pool.map(req,urls)
    print(page_text_list)
    print('总耗时:',time.time()-start)
    
    ____________________________
    ['hello bobo!!!', 'hello bobo!!!', 'hello bobo!!!']
    总耗时: 2.1126856803894043
    

    多任务异步爬虫

    1.协程基础

    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('www.1.com')
    
    #创建一个任务对象
    task = asyncio.ensure_future(c)
    #给任务对象绑定一个回调函数
    task.add_done_callback(parse)
    
    #创建一个事件循环对象
    loop = asyncio.get_event_loop()
    #将任务对象注册到该对象中并且开启该对象
    loop.run_until_complete(task)#让loop执行了一个任务
    
    

    2.多任务协程

    import asyncio
    from time import sleep
    import time
    
    # 被async修饰的特殊的函数
    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)
    
    
    {#  示范页面 #}
    <html lang="en">
    <head>
    	<meta charset="UTF-8" />
    	<title>测试bs4</title>
    </head>
    <body>
    	<div>
    		<p>百里守约</p>
    	</div>
    	<div class="song">
    		<p>李清照</p>
    		<p>王安石</p>
    		<p>苏轼</p>
    		<p>柳宗元</p>
    		<a href="http://www.song.com/" title="赵匡胤" target="_self">
    			<span>this is span</span>
    		宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
    		<a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
    		<img src="http://www.baidu.com/meinv.jpg" alt="" />
    	</div>
    	<div class="tang">
    		<ul>
    			<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
    			<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
    			<li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
    			<li><a href="http://www.sina.com" class="du">杜甫</a></li>
    			<li><a href="http://www.dudu.com" class="du">杜牧</a></li>
    			<li><b>杜小月</b></li>
    			<li><i>度蜜月</i></li>
    			<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
    		</ul>
    	</div>
    </body>
    </html>
    

    3.多任务异步爬虫(错误示范)

    import asyncio
    import requests
    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):
        #requests模块不支持异步,中断了整个的异步效果
        page_text = requests.get(url).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)
    
    # 由于使用了requests不适用多任务异步,因此引入了aiohttp,这个模块支持多任务异步,看下面的4
    

    4.基于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)
    
    {# 示范页面 #}
    <html lang="en">
    <head>
       <meta charset="UTF-8" />
       <title>测试bs4</title>
    </head>
    <body>
       <div>
          <p>百里守约</p>
       </div>
       <div class="song">
          <p>李清照</p>
          <p>王安石</p>
          <p>苏轼</p>
          <p>柳宗元</p>
          <a href="http://www.song.com/" title="赵匡胤" target="_self">
             <span>this is span</span>
          宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
          <a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
          <img src="http://www.baidu.com/meinv.jpg" alt="" />
       </div>
       <div class="tang">
          <ul>
             <li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
             <li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
             <li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
             <li><a href="http://www.sina.com" class="du">杜甫</a></li>
             <li><a href="http://www.dudu.com" class="du">杜牧</a></li>
             <li><b>杜小月</b></li>
             <li><i>度蜜月</i></li>
             <li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
          </ul>
       </div>
    </body>
    </html>
    

    5.总结

    单线程+多任务异步协程asyncio
    - 特殊函数:
        - 就是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
    
    

    selenium模块

    # 首先想使用这个模块需要下载
    	pip3 install selenium
    # 再下载一个浏览器驱动程序,这里以谷歌浏览器为例
    	驱动下载地址:http://chromedriver.storage.googleapis.com/index.html
       步骤:
       	1.根据自己浏览器的版本下载驱动程序
          2.将下载好的驱动程序放入python36/Scripts,文件夹中。
          3.1 
          	.py页面引入
          	from selenium import webdriver,
             #实例化一个浏览器对象
          	driver = webdriver.Chrome()
             它会自动调用驱动程序打开浏览器进行操作
          3.2  
          	或者将驱动程序放在当前路径下,创建浏览器对象时
             driver = webdriver.Chrome(r'chromedriver')即可
          
       		
    

    1.演示

    from selenium import webdriver
    from time import sleep
    
    # 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的
    driver = webdriver.Chrome(r'chromedriver')
    # 用get打开百度页面
    driver.get("http://www.baidu.com")
    # 查找页面的“设置”选项,并进行点击
    driver.find_elements_by_link_text('设置')[0].click()
    sleep(2)
    # # 打开设置后找到“搜索设置”选项,设置为每页显示50条
    driver.find_elements_by_link_text('搜索设置')[0].click()
    sleep(2)
    
    # 选中每页显示50条
    m = driver.find_element_by_id('nr')
    sleep(2)
    m.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
    m.find_element_by_xpath('.//option[3]').click()
    sleep(2)
    
    # 点击保存设置
    driver.find_elements_by_class_name("prefpanelgo")[0].click()
    sleep(2)
    
    # 处理弹出的警告页面   确定accept() 和 取消dismiss()
    driver.switch_to_alert().accept()
    sleep(2)
    # 找到百度的输入框,并输入 美女
    driver.find_element_by_id('kw').send_keys('美女')
    sleep(2)
    # 点击搜索按钮
    driver.find_element_by_id('su').click()
    sleep(2)
    # 在打开的页面中找到“Selenium - 开源中国社区”,并打开这个页面
    driver.find_elements_by_link_text('美女_百度图片')[0].click()
    sleep(3)
    
    # 关闭浏览器
    driver.quit()
    

    2.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()
    

    3.动态加载数据的捕获

    #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)
    
    # 拿到当前页面所有的html数据
    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()
    

    4.动作链

    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'), 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()
    

    5.案例:爬取12306的数据

    from selenium import webdriver
    from time import sleep
    from PIL import Image
    from selenium.webdriver import ActionChains
    from CJY import Chaojiying_Client
    
    # 实例化一个浏览器对象
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    bro.get('https://kyfw.12306.cn/otn/login/init')
    sleep(2)
    
    # 对12306登陆的页面截图,保存到当前路径
    bro.save_screenshot('./main.png')
    # 找到html中验证码的图片
    img_tag = bro.find_element_by_path('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
    # 获取验证码在12306登陆界面中的起始位置,以图片的左下角的坐标为准
    location = img_tag.location
    # 求验证码的图片大小
    size = img_tag.size
    # 计算出图片左下和右上的坐标,确定验证码在12306界面的位置
    rangle = (int(location['x']),int(location['y']),int(location['x']+int(size['width'])),int(location['y']+int(size['height'])))
    # 打开之前对12306的截图
    i = Image.open('./main.png')
    # 根据rangle计算出来的坐标进行裁剪,将验证码图片裁剪下来
    frame = i.crop(rangle)
    frame.save('code.png')
    
    # 执行超级鹰代码
    result = transformCode('./code.png',9004)
    print(result) # 119,69|161,65|102,126
    
    # 对坐标进行解析
    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('J-userName').send_keys('xxxx')
    sleep(1)
    bro.find_element_by_id('J-password').send_keys('xxxx')
    sleep(1)
    
    # 点击登陆
    bro.find_element_by_id('J-login').click()
    sleep(5)
    # 登陆之后,打印登陆之后的界面
    print(bro.page_source)
    # 退出登陆
    bro.quit()
    
    #封装超级鹰识别验证码的函数
    # imgPath为图片路径,imgType=验证码类型 
    def transformCode(imgPath,imgType):
        chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
          									# 账号				# 密码			  # softid=软件ID  在用户中心。
        
        im = open(imgPath, 'rb').read()
        return chaojiying.PostPic(im, imgType)['pic_str']
    
    
    

    打码平台:超级鹰代码

    import requests
    from hashlib import md5
    
    class Chaojiying_Client(object):
    
        def __init__(self, username, password, soft_id):
            self.username = username
            password =  password.encode('utf8')
            self.password = md5(password).hexdigest()
            self.soft_id = soft_id
            self.base_params = {
                'user': self.username,
                'pass2': self.password,
                'softid': self.soft_id,
            }
            self.headers = {
                'Connection': 'Keep-Alive',
                'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
            }
    
        def PostPic(self, im, codetype):
            """
            im: 图片字节
            codetype: 题目类型 参考 http://www.chaojiying.com/price.html
            """
            params = {
                'codetype': codetype,
            }
            params.update(self.base_params)
            files = {'userfile': ('ccc.jpg', im)}
            r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
            return r.json()
    
        def ReportError(self, im_id):
            """
            im_id:报错题目的图片ID
            """
            params = {
                'id': im_id,
            }
            params.update(self.base_params)
            r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
            return r.json()
    
    
    # 使用
    #执行代码
    # chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')
    # im = open('a.jpg', 'rb').read()
    # print chaojiying.PostPic(im, 1902)
    

    6.-selenium规避风险

    - selenium规避风险
        - 正经打开一个网站进行window.navigator.webdriver的js注入,返回值为undefined
        - 使用selenium打开的页面,进行上述js注入返回的是true
    
    #规避检测
    from selenium import webdriver
    from selenium.webdriver import ChromeOptions
    option = ChromeOptions()
    # 向option中添加固定键值对
    option.add_experimental_option('excludeSwitches', ['enable-automation'])
    # 作用在浏览器上
    bro = webdriver.Chrome(executable_path='./chromedriver.exe',options=option)
    #
    url = 'https://www.taobao.com/'
    #
    bro.get(url)
    

    7.无头浏览器:没有可视化界面,提高爬取效率

    - 无头浏览器
        - phantomJs # 以前使用的模块,现在不更新了。
        - 谷歌无头
    
    
    #无头浏览器
    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)
    
    
  • 相关阅读:
    nosql----redis持久化详解
    linux下发送邮件
    自动化运维工具----ansiable安装与配置
    nginx----统计网站访问量
    机试指南第二章-经典入门-排序例题自解
    机试指南第二章-经典入门-排序模板
    Theano入门
    OpenJudge 4120 硬币
    OpenJudge 1088 滑雪
    OpenJudge 4152 最佳加法表达式
  • 原文地址:https://www.cnblogs.com/zhuzhizheng/p/11997723.html
Copyright © 2020-2023  润新知