• 2. requests高阶使用


    requests高阶使用

    1.图片懒加载

    图片懒加载:
    	当用户刷新页面的时候,页面中的图片只会加载出局部的而不是所有,只有当满足了指定的条件才可以将剩余的图片加载出来。
    如何决定图片是否要加载出来?
    	标签中使用伪属性。例如src2
    
    # 网站:站长素材
    
    import requests
    from lxml import etree
    import os
    
    # 创建储存图片目录
    dirname = '站长素材'
    if not os.path.exists(dirname):
        os.mkdir(dirname)
    
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36'
    }
    
    # 获取前几页数据
    for i in range(1,6):
        if i == 1:
            url = 'https://sc.chinaz.com/tupian/hunsha.html'
        else:
            url = 'https://sc.chinaz.com/tupian/hunsha_%d.html'%(i)
    
        response = requests.get(url,headers=headers)
        response.encoding = 'utf-8'
        page_text = response.text # 获取页面数据
        # print(page_text)
    
        # 实例化对象
        tree = etree.HTML(page_text)
    
        # 定位标签
        div_list = tree.xpath('//*[@id="container"]/div')
        for div in div_list:
            title = div.xpath('./div/a/img/@alt')[0] + '.jpg'
            # 获取页面标签的伪属性src2
            src = 'https:' + div.xpath('./div/a/img/@src2')[0]
            # print(title,src)
            # 获取图片数据
            img = requests.get(src,headers=headers).content
            filename = dirname + '/' + title
            # 保存图片
            with open(filename,'wb') as fp:
                fp.write(img)
                print(title,'保存成功')
    

    2.cookie反爬机制

    1. Cookie反爬机制:
        - cookie是可以设置有效时常的
        - cookie中是可以存在动态变化的键值对数据
        - 因此,将cookie写死在headers字典里不是一个明智选择
    
    2. 处理cookie反爬:
        - 手动处理:将cookie写在headers中 
        - 自动处理:session的对象处理。
            - session对象可以像requests一样进行请求发送。只不过区别在于:
            - 如果在请求发送的过程中会产生cookie,则cookie会被自动存储到session对象中,而requests不会储存cookie。
            - session在常规的使用中至少会被调用两次。
            	第一次捕获cookie,之后携带cookie请求数据
                
    
    '''
    需求: 爬取雪球网咨询信息 -- https://xueqiu.com/
    分析:
        1.判定爬取的数据是否为动态加载:
            当滚轮滑到底部的时候会加载更多的数据
        2.通过抓包工具Network中的XHR,定位ajax请求数据包,获取数据
    '''
    
    import requests
    
    # 获取session对象
    sess = requests.Session()
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36'
    }
    # 使用session捕获cookie
    sess.get('https://xueqiu.com/',headers=headers)
    
    url = 'https://xueqiu.com/statuses/hot/listV2.json?since_id=-1&max_id=195148&size=15'
    
    # session请求数据
    response = sess.get(url,headers=headers)
    json_text = response.json()
    print(json_text)
    

    3.代理操作

    1. 代理服务器:
    	作用:转发请求和响应
    2.代理和爬虫之间的关联?
    	在短时间内,向一个网站发起高频的请求,则网站会将请求的ip监测到且加入黑名单。
    3.代理类型
        http -- 只能转发http协议的请求
        https-- 只能转发https协议的请求
    4.代理的匿名度
        透明代理:知道你使用了代理,也知道你本机真实ip
        匿名代理:知道你使用了代理,不知道你本机真实ip
        高匿代理:不知道你使用了代理,也不知道你本机真实ip
    5.购买代理服务器:
    	智连代理:http://http.zhiliandaili.cn/
    
    import requests
    from lxml import etree
    import random
    
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36'
    }
    
    # 生成代理池
    url1 = 'http://t.ipjldl.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=5&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=15'
    response = requests.get(url1)
    page_text = response.text
    # print(page_text)
    
    tree = etree.HTML(page_text)
    page_list = tree.xpath('/html/body//text()')
    # print(page_list)
    ip_list = []
    for i in page_list:
        dic = {'https':i}
        ip_list.append(dic)
    print(ip_list)
    
    # 获取ip地址
    url = 'https://www.sogou.com/web?query=ip'
    
    # 使用代理服务器: proxies = {'https': 'ip:端口号'}
    response = requests.get(url,headers=headers,proxies=random.choice(ip_list))
    page_text = response.text
    # print(page_text)
    # 生成对象
    tree = etree.HTML(page_text)
    # 定位标签
    ip = tree.xpath('//*[@id="ipsearchresult"]/strong/text()')[0]
    print(ip)
    

    4.验证码识别

    验证码识别平台:http://www.ttshitu.com/
    - 开发文档 -- python -- 如下:
    
    import base64
    import json
    import requests
    # 一、图片文字类型(默认 3 数英混合):
    # 1 : 纯数字
    # 1001:纯数字2
    # 2 : 纯英文
    # 1002:纯英文2
    # 3 : 数英混合
    # 1003:数英混合2
    #  4 : 闪动GIF
    # 7 : 无感学习(独家)
    # 11 : 计算题
    # 1005:  快速计算题
    # 16 : 汉字
    # 32 : 通用文字识别(证件、单据)
    # 66:  问答题
    # 49 :recaptcha图片识别 参考 https://shimo.im/docs/RPGcTpxdVgkkdQdY
    # 二、图片旋转角度类型:
    # 29 :  旋转类型
    #
    # 三、图片坐标点选类型:
    # 19 :  1个坐标
    # 20 :  3个坐标
    # 21 :  3 ~ 5个坐标
    # 22 :  5 ~ 8个坐标
    # 27 :  1 ~ 4个坐标
    # 48 : 轨迹类型
    #
    # 四、缺口识别
    # 18:缺口识别
    # 五、拼图识别
    # 53:拼图识别
    def base64_api(uname, pwd,  img,typeid):
        with open(img, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            b64 = base64_data.decode()
        data = {"username": uname, "password": pwd,"typeid":typeid, "image": b64}
        result = json.loads(requests.post("http://api.ttshitu.com/predict", json=data).text)
        if result['success']:
            return result["data"]["result"]
        else:
            return result["message"]
        return ""
    
    
    if __name__ == "__main__":
        img_path = "./img.jpg" # 验证码图片
        result = base64_api(uname='bb328410948', pwd='bb328410948', img=img_path,typeid=3)
        print(result)
    

    5.模拟登陆

    - 模拟登录
        - 验证码识别平台:http://www.ttshitu.com/
        - 动态变化的请求参数(抓包工具全局搜索)
            - 隐藏在前台页面中
            - 是由js动态生成
        - 注意Cookie
    
    
    # 网站: 古诗文网 
    # https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx
    
    import base64
    import json
    import requests
    from lxml import etree
    
    # 获取session对象 -- 获取cookie,携带cookie请求
    session = requests.Session()
    
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36'
    }
    
    # 验证码识别函数
    def base64_api(uname, pwd,  img,typeid):
        with open(img, 'rb') as f:
            base64_data = base64.b64encode(f.read())
            b64 = base64_data.decode()
        data = {"username": uname, "password": pwd,"typeid":typeid, "image": b64}
        result = json.loads(requests.post("http://api.ttshitu.com/predict", json=data).text)
        if result['success']:
            return result["data"]["result"]
        else:
            return result["message"]
        return ""
    
    # 1.动态获取验证码图片
    url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
    response = session.get(url,headers=headers)
    page_text = response.text
    # print(page_text)
    
    tree = etree.HTML(page_text)
    src = 'https://so.gushiwen.cn'+tree.xpath('//*[@id="imgCode"]/@src')[0]
    # print(src)
    code_data = session.get(src,headers=headers).content
    with open('code.jpg','wb')as fp:
        fp.write(code_data)
    
    # 2.识别验证码
    img_path = "./code.jpg"
    result = base64_api(uname='bb328410948', pwd='bb328410948', img=img_path,typeid=3)
    print(result)
    
    # 在前台页面动态捕获动态变化的请求参数值
    __VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
    print(__VIEWSTATE)
    
    # 3.模拟登陆
    url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'
    data = {
        # 动态属性值
        '__VIEWSTATE': __VIEWSTATE,
        '__VIEWSTATEGENERATOR': 'C93BE1AE',
        'from': 'http://so.gushiwen.cn/user/collect.aspx',
        'email': 17600351804,
        'pwd': 123456,
        'code': result,
        'denglu': '登录',
    }
    response = session.post(url,headers=headers,data=data)
    page_text = response.text
    print(page_text)
    with open('../古诗文.html', 'w', encoding='utf-8') as fp:
        fp.write(page_text)
    
    
    

    使用flask在本地开启一个服务端

    1.安装: pip install flask
    2.创建一个templates文件夹,目录下放置静态HTML文件 -- 例如:text.html
        - 配置templates文件夹:
        	右键 -> Mark Directory as -> 选择 Template Folder -> yes
            选择语言: Languages & Frameworks -> Python Template Language
                	选择:Jinja2
    3.创建一个server.py(.py)源文件,启动服务端
    
    

    静态文件: text.html

    <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>
    
    

    源文件: server.py

    from flask import Flask,render_template
    from time import sleep
    
    # 实例化一个app
    app = Flask(__name__)
    
    # 创建视图函数和路由地址
    @app.route('/bobo')
    def indexs():
        sleep(2)
        # 返回静态界面
        return render_template('test.html') 
    
    @app.route('/hehe')
    def indexs1():
        sleep(2)
        return 'hello world!' # 返回字符串
    
    @app.route('/tom')
    def indexs1():
        sleep(2)
        return render_template('test.html')
    
    @app.route('/jay')
    def indexs2():
        sleep(2)
        return render_template('test.html')
    
    if __name__ == '__main__':
        # 运行服务器 -- debug=True开启调试模式
        app.run(debug=True)
    
    

    1.线程池

    import requests
    import time
    from multiprocessing.dummy import Pool # 引入线程池
    
    # 定义请求函数
    def get_request(url):
        page_text = requests.get(url).text
        return len(page_text)
    
    urls = [
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/tom',
    'http://127.0.0.1:5000/jay'
    ]
    
    # 同步请求
    '''
    if __name__ == '__main__':
        start = time.time()
        for url in urls:
            result = get_request(url)
            print(result)
        print('总耗时:',time.time()-start) # 6秒
    '''
    
    # 基于线程池的异步请求
    if __name__ == '__main__':
        start = time.time()
        # 实例化线程池对象 -- 参数3 表示开启3个线程
        pool = Pool(3)
        # 调用 -- map(self, func, iterable, chunksize=None):
        # 使用get_request作为回调函数,保证回调函数必须只有一个参数和返回值
        result_list = pool.map(get_request,urls)
        print(result_list)
    
        print('总耗时:',time.time()-start) # 2秒
    
    
    

    2.协程操作

    1.协程基础

    多任务的异步协程:
        1.特殊的函数
            - 如果一个函数的定义被asnyc关键字修饰,则该函数就是一个特殊的函数。
            - 特殊之处:
                1.当特殊函数被调用后,函数内部的实现语句没有被立即执行
                2.当特殊函数调用后,会返回一个协程对象。
        2.协程对象
            - 当特殊函数调用后,会返回一个协程对象
        3.任务对象: 对协程对象的进一步封装
            - 任务对象 == 一个高级的协程对象 == 特殊函数 == 一组指定形式的操作
            - 任务对象 == 一组指定形式的操作
            - 任务对象的高级之处体现在可以给任务对象指定一个回调函数
                - 给任务对象绑定回调函数:
                    让任务对象调用add_done_callback(func)
        4.事件循环对象
            - loop表示事件循环对象。该对象可以当作是一个容器,专门用来装载/存储一个或多个任务对象/协程对象。
            - 当开启了事件循环后,则该loop对象就可以异步执行其内部装在的任务对象。
    
    

    2.协程的基本操作

    import asyncio
    import time
    
    async def get_request(url):
        print('正在请求:',url)
        time.sleep(2)
        print('请求结束!')
        return 'hello bobo'
    
    def func(task):#必须携带一个参数,值的就是add_done_callback的调用者(任务对象)
        data = task.result() #result()返回任务对象对应特殊函数内部的返回值
        print('回调函数返回值:',data) # 回调函数返回值:hello bobo
    
    #协程对象
    c = get_request('www.1.com')
    
    #基于协程创建一个任务对象
    task = asyncio.ensure_future(c)
    
    #给任务对象绑定回调函数
    task.add_done_callback(func)
    
    #创建了一个事件对象
    loop = asyncio.get_event_loop()
    
    #装载任务对象和启动事件循环
    loop.run_until_complete(task)
    
    

    3.多任务协程操作

    1.wait([task1,task2...])方法:
        - 挂起:让当前挂起的任务对象交出cpu的使用权。
        - wait()作用:给每一个任务对象添加可被挂起的权限。
    2.注意:在特殊函数实现内部,不可以出现不支持异步模块的代码,否则会中断异步效果
    3.await:需要添加在阻塞操作前面,保证阻塞操作在异步中会被异步执行。
    
    
    import asyncio
    import time
    
    # 特殊函数
    # async def get_url(url):
    #     print('发送请求:',url)
    #     time.sleep(1)  # 出现不支持异步操作的代码
    #     print('请求结束!')
    #     return 'Hello World!!'
    
    async def get_url(url):
        print('发送请求:',url)
        await asyncio.sleep(1)  # 支持异步操作
        print('请求结束!')
        return 'Hello World!!'
    
    url_list = ['www.1.com','www.2.com','www.3.com']
    task_list = [] # 任务列表
    start = time.time()
    for url in url_list:
        # 创建协程对象
        c = get_url(url)
        # 创建任务对象
        task = asyncio.ensure_future(c)
        # 把任务对象追加到列表
        task_list.append(task)
    
    # 创建事件循环对象
    loop = asyncio.get_event_loop()
    # 必须使用wait方法,对任务列表进行封装
    loop.run_until_complete(asyncio.wait(task_list))
    
    print('总耗时:',time.time()-start)
    
    

    4.多任务异步爬虫

    - 因为requests不支持异步请求,所有我们使用aiphttp:
        安装: pip install aiphttp
    	
    - aiohttp:异步的网络请求模块
        使用步骤:分两步
        1.编写一个大致的架构
        # 创建一个请求对象
        with aiohttp.ClientSession() as req:
            # 发起指定请求
            with req.get(url) as response:
                #text()字符串相应数据,read()是bytes类型的相应数据
                page_text = response.text()
         2.在大致架构上补充细节
         	- 在每一个with前加上async关键字
         	- 在每一步阻塞操作前加上await关键字
    
    
    import asyncio
    import time
    import requests
    import aiohttp
    from lxml import etree
    
    start = time.time()
    #requets是不支持异步
    # async def get_request(url):
    #     page_text = requests.get(url).text
    #     return page_text
    
    async def get_request(url):
        #创建一个请求对象
        async with aiohttp.ClientSession() as req:
            #发起指定请求
            #注意:get和post和requests中的区别在于proxies参数。在aiohttp设置代理使用的时proxy='http://ip:port'
            async with await req.get(url) as response:
                #text()字符串相应数据,read()byte类型的相应数据
                page_text = await response.text()
                return page_text
    
    # 回调函数 -- 获取页面指定内容
    def parse(task):
        page_text = task.result()
        tree = etree.HTML(page_text)
        data = tree.xpath('//a[@id="feng"]/text()')[0]
        print(data)
    
    urls = [
        'http://127.0.0.1:5000/bobo',
        'http://127.0.0.1:5000/tom',
        'http://127.0.0.1:5000/jay'
    ]
    
    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)
    
    
  • 相关阅读:
    Django Web开发学习笔记(1)
    SessionFactory 执行原生态的SQL语句
    Java中使用FileputStream导致中文乱码问题的修改方案
    JavaScript中的namespace
    SpringMVC配置文件
    Python 贝叶斯分类
    Struct(二)
    Struct2 (一)
    SpingMVC ModelAndView, Model,Control以及参数传递
    window.onload
  • 原文地址:https://www.cnblogs.com/jia-shu/p/14695515.html
Copyright © 2020-2023  润新知