urllib与requests

前情摘要

一、web请求全过程剖析

我们浏览器在输入完网址到我们看到网页的整体内容, 这个过程中究竟发生了些什么?

我们看一下一个浏览器请求的全过程

image-20230318102622715

image-20230318102559233

image-20230318102644337

接下来就是一个比较重要的事情了. 所有的数据都在页面源代码里么? 非也~ 这里要介绍一个新的概念

那就是页面渲染数据的过程, 我们常见的页面渲染过程有两种,

  1. 服务器渲染, 你需要的数据直接在页面源代码里能搜到

    这个最容易理解, 也是最简单的. 含义呢就是我们在请求到服务器的时候, 服务器直接把数据全部写入到html中, 我们浏览器就能直接拿到带有数据的html内容. 比如,

    image-20230318102710961

    由于数据是直接写在html中的, 所以我们能看到的数据都在页面源代码中能找的到的.

    这种网页一般都相对比较容易就能抓取到页面内容.

  2. 前端JS渲染, 你需要的数据在页面源代码里搜不到

    这种就稍显麻烦了. 这种机制一般是第一次请求服务器返回一堆HTML框架结构. 然后再次请求到真正保存数据的服务器, 由这个服务器返回数据, 最后在浏览器上对数据进行加载. 就像这样:

    image-20230318102729857

    js渲染代码(示例)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>案例:动态渲染页面</title>
        <style>
            table{
                width: 300px;
                text-align: center;
            }
        </style>
    </head>
    <body>
        <table border="1" cellspacing="0">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>姓名</th>
                    <th>年龄</th>
                </tr>
            </thead>
            <tbody>
            <!--        js渲染-->
            </tbody>
        </table>
    
        <script>
            //提前准备好的数据
            var users = [
                {id: 1, name: '张三', age: 18},
                {id: 2, name: '李四', age: 28},
                {id: 3, name: '王麻子', age: 38}
            ]
    
            //获取tbody标签
            var tbody = document.querySelector('tbody')
    
            //1.循环遍历users数据
            users.forEach(function (item) {
                //这里的item 就是数组中的每一个对象
                console.log(item)
                //2. 每一个对象生成一个tr标签
                var tr = document.createElement('tr')
    
                //循环遍历item
                for(var key in item){
                    //生成td标签
                    var td = document.createElement('td')
                    td.innerHTML = item[key]
    
                    //5.把td 插入到tr内部
                    tr.appendChild(td)
                }
                //把本次的tr插入到tbody的内部
                tbody.appendChild(tr)
            })
        </script>
    </body>
    </html>
    

    这样做的好处是服务器那边能缓解压力. 而且分工明确. 比较容易维护. 典型的有这么一个网页

    image-20201215175207478

    那数据是何时加载进来的呢? 其实就是在我们进行页面向下滚动的时候, jd就在偷偷的加载数据了, 此时想要看到这个页面的加载全过程, 我们就需要借助浏览器的调试工具了(F12)

    image-20201215175536447

    ![image-20201215175637599](D:\data\1045699\Desktop\luffy_code\eight_class\day06 urllib与requests\teacher_code\assets\image-20201215175637599.png)

    image-20201215175848471

    ![image-20201215180141450](D:\data\1045699\Desktop\luffy_code\eight_class\day06 urllib与requests\teacher_code\assets\image-20201215180141450.png)

    看到了吧, 页面上看到的内容其实是后加载进来的.

OK, 在这里我不是要跟各位讲jd有多牛B, 也不是说这两种方式有什么不同, 只是想告诉各位, 有些时候, 我们的数据不一定都是直接来自于页面源代码. 如果你在页面源代码中找不到你要的数据时, 那很可能数据是存放在另一个请求里.

 1.你要的东西在页面源代码. 直接拿`源代码`提取数据即可
 2.你要的东西,不在页面源代码, 需要想办法找到真正的加载数据的那个请求. 然后提取数据

二、浏览器工具的使用

Chrome是一款非常优秀的浏览器. 不仅仅体现在用户使用上. 对于我们开发人员而言也是非常非常好用的.

对于一名爬虫工程师而言. 浏览器是最能直观的看到网页情况以及网页加载内容的地方. 我们可以按下F12来查看一些普通用户很少能使用到的工具.

image-20230318102757175

其中, 最重要的Elements, Console, Sources, Network.

Elements是我们实时的网页内容情况, 注意, 很多兄弟尤其到了后期. 非常容易混淆Elements以及页面源代码之间的关系.

注意,

  1. 页面源代码是执行js脚本以及用户操作之前的服务器返回给我们最原始的内容
  2. Elements中看到的内容是js脚本以及用户操作之后的当时的页面显示效果.

你可以理解为, 一个是老师批改之前的卷子, 一个是老师批改之后的卷子. 虽然都是卷子. 但是内容是不一样的. 而我们目前能够拿到的都是页面源代码. 也就是老师批改之前的样子. 这一点要格外注意.

在Elements中我们可以使用左上角的小箭头.可以直观的看到浏览器中每一块位置对应的当前html状况. 还是很贴心的.

image-20230318102818780

第二个窗口, Console是用来查看程序员留下的一些打印内容, 以及日志内容的. 我们可以在这里输入一些js代码自动执行.

image-20230318102836180

等咱们后面讲解js逆向的时候会用到这里.

第三个窗口, Source, 这里能看到该网页打开时加载的所有内容. 包括页面源代码. 脚本. 样式, 图片等等全部内容.

image-20230318102851082

第四个窗口, Network, 我们一般习惯称呼它为抓包工具. 在这里, 我们能看到当前网页加载的所有网路网络请求, 以及请求的详细内容. 这一点对我们爬虫来说至关重要.

image-20230318102905238

image-20230318102920522

image-20230318102935686

image-20230318102949401

其他更加具体的内容. 随着咱们学习的展开. 会逐一进行讲解.

三、反爬虫的一般手段

爬虫项目最复杂的不是页面信息的提取,反而是爬虫与反爬虫、反反爬虫的博弈过程

  • User-Agent

    浏览器的标志信息,会通过请求头传递给服务器,用以说明访问数据的浏览器信息

    反爬虫:先检查是否有UA,或者UA是否合法

  • 代理IP

  • 验证码访问

  • 动态加载网页

  • 数据加密

  • ...

四、常见HTTP状态码

  • 200:这个是最常见的http状态码,表示服务器已经成功接受请求,并将返回客户端所请

  • 100-199 用于指定客户端应相应的某些动作。

  • 200-299 用于表示请求成功。

  • 300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。

  • 400-499 用于指出客户端的错误。

    • 404:请求失败,客户端请求的资源没有找到或者是不存在
  • 500-599 服务器遇到未知的错误,导致无法完成客户端当前的请求。

urllib与requests

一、urllib的学习

学习目标

了解urllib的基本使用


1、urllib介绍

除了requests模块可以发送请求之外, urllib模块也可以实现请求的发送,只是操作方法略有不同!

urllib在python中分为urllib和urllib2,在python3中为urllib

下面以python3的urllib为例进行讲解

import urllib.request

# 了解!!!!!!!!!!!
url = 'http://www.baidu.com'
response = urllib.request.urlopen(url)
print(response)
# 打印状态码
print(response.getcode())
# 获取请求的url
print(response.geturl())
# 获取响应内容
# print(response.read())
print(response.read().decode('UTF-8'))

urlib编译

import urllib.request

url = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E6%9D%9C%E5%8D%A1%E8%BF%AA&fenlei=256&rsv_pq=0xc3e6c17800092a97&rsv_t=b943aXFJg91J%2FYd2pUe1N7Sed6MBjVYPE%2Bpi%2FaAIVXUg2OW0s%2F%2BeFR%2Bh2G3%2B&rqlang=en&rsv_enter=1&rsv_dl=tb&rsv_sug3=11&rsv_sug1=11&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&prefixsug=%25E6%259D%259C%25E5%258D%25A1%25E8%25BF%25AA&rsp=5&inputT=6355&rsv_sug4=6596&rsv_sug=1'

print(urllib.request.unquote(url))

2、urllib的基本方法介绍

2.1 urllib.Request

  1. 构造简单请求

    import urllib
    #构造请求
    request = urllib.request.Request("http://www.baidu.com")
    #发送请求获取响应
    response = urllib.request.urlopen(request)
    
  2. 传入headers参数

    import urllib
    #构造headers
    headers = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"} 
    #构造请求
    request = urllib.request.Request(url, headers = headers)
    #发送请求
    response = urllib.request.urlopen(request)
    
  3. 传入data参数 实现发送post请求(示例)

    import urllib.request
    import urllib.parse
    import json
    
    url = 'https://ifanyi.iciba.com/index.php?c=trans&m=fy&client=6&auth_user=key_ciba&sign=99730f3bf66b2582'
    
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15',
    
    }
    data = {
        'from': 'zh',
        'to': 'en',
        'q': 'lucky 是一个帅气的老'
    }
    # 使用post方式
    # 需要
    data = urllib.parse.urlencode(data).encode('utf-8')
    req = urllib.request.Request(url, data=data, headers=headers)
    res = urllib.request.urlopen(req)
    print(res.getcode())
    print(res.geturl())
    data = json.loads(res.read().decode('utf-8'))
    print(data)
    

2.2 response.read()

获取响应的html字符串,bytes类型

#发送请求
response = urllib.request.urlopen("http://www.baidu.com")
#获取响应
response.read()

3、urllib请求百度首页的完整例子

import urllib
import json

url = 'http://www.baidu.com'
#构造headers
headers = {"User-Agent" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"}
#构造请求
request = urllib.request.Request(url, headers = headers)
#发送请求
response = urllib.request.urlopen(request)
#获取html字符串
html_str = response.read().decode('utf-8')
print(html_str)

4、小结

  1. urllib.request中实现了构造请求和发送请求的方法
  2. urllib.request.Request(url,headers,data)能够构造请求
  3. urllib.request.urlopen能够接受request请求或者url地址发送请求,获取响应
  4. response.read()能够实现获取响应中的bytes字符串

requests模块的入门使用

一、requests模块的入门使用

学习目标:
  1. 了解 requests模块的介绍
  2. 掌握 requests的基本使用
  3. 掌握 response常见的属性
  4. 掌握 requests.text和content的区别
  5. 掌握 解决网页的解码问题
  6. 掌握 requests模块发送带headers的请求
  7. 掌握 requests模块发送带参数的get请求

1、为什么要重点学习requests模块,而不是urllib

  • 企业中用的最多的就是requests
  • requests的底层实现就是urllib
  • requests在python2 和python3中通用,方法完全一样
  • requests简单易用

2、requests的作用与安装

作用:发送网络请求,返回响应数据

安装:pip install requests

3、requests模块发送简单的get请求、获取响应

需求:通过requests向百度首页发送请求,获取百度首页的数据

import requests

url = 'http://www.baidu.com'
# 发起get请求
response = requests.get(url)
# print(response)
# 打印响应状态码
# print(response.status_code)
# 默认编码
# print(response.encoding)
# 获取访问的url
# print(response.url)
# 获取请求头
# print(response.request.headers)
# print(response.ok)
# 获取响应的json数据(如果返回的数据类型不是json  则报错)
# print(response.json())
# 获取响应内容为bytes的类型
# print(response.content)
# print(response.content.decode())
# print(response.content.decode('UTF-8'))  # UTF-8/GBK
# 返回字符串类型
response.encoding = 'UTF-8'
print(response.text)

response的常用属性:

  • response.text 响应体 str类型

  • response.encoding 从HTTP header中猜测的响应内容的编码方式

  • respones.content 响应体 bytes类型

  • response.status_code 响应状态码

  • response.request.headers 响应对应的请求头

  • response.headers 响应头

  • response.cookies 响应的cookie(经过了set-cookie动作)

  • response.url 获取访问的url

  • response.json() 获取json数据 得到内容为字典 (如果接口响应体的格式是json格式时)

  • response.ok

    如果status_code小于200,response.ok返回True。

    如果status_code大于200,response.ok返回False。

思考:text是response的属性还是方法呢?

  • 一般来说名词,往往都是对象的属性,对应的动词是对象的方法
3.1 response.text 和response.content的区别
  • response.text
    • 类型:str
    • 解码类型: requests模块自动根据HTTP 头部对响应的编码作出有根据的推测,推测的文本编码
    • 如何修改编码方式:response.encoding="gbk/UTF-8"
  • response.content
    • 类型:bytes
    • 解码类型: 没有指定
    • 如何修改编码方式:response.content.deocde("utf8")

获取网页源码的通用方式:

  1. response.content.decode()
  2. response.content.decode("UTF-8")
  3. response.text

以上三种方法从前往后尝试,能够100%的解决所有网页解码的问题

所以:更推荐使用response.content.deocde()的方式获取响应的html页面

3.2 练习:把网络上的图片保存到本地

我们来把www.baidu.com的图片保存到本地

思考:

  • 以什么方式打开文件
  • 保存什么格式的内容

分析:

import requests

# 图片的url
url = 'https://www.baidu.com/img/bd_logo1.png'

# 响应本身就是一个图片,并且是二进制类型
response = requests.get(url)

# print(response.content)

# 以二进制+写入的方式打开文件
with open('baidu.png', 'wb') as f:
    # 写入response.content bytes二进制类型
    f.write(response.content)

4、发送带header的请求

我们先写一个获取百度首页的代码

import requests

url = 'https://www.baidu.com'

response = requests.get(url)

print(response.content)

# 打印响应对应请求的请求头信息
print(response.request.headers)
4.1 思考

对比浏览器上百度首页的网页源码和代码中的百度首页的源码,有什么不同?

代码中的百度首页的源码非常少,为什么?

4.2 为什么请求需要带上header?

模拟浏览器,欺骗服务器,获取和浏览器一致的内容

4.3 header的形式:字典
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
4.4 用法
requests.get(url, headers=headers)
4.5 完整的代码
import requests

url = 'https://www.baidu.com'

headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

# 在请求头中带上User-Agent,模拟浏览器发送请求
response = requests.get(url, headers=headers)

# print(response.content)

# 打印请求头信息
print(response.request.headers)

5、发送带参数的请求

我们在使用百度搜索的时候经常发现url地址中会有一个 ?,那么该问号后边的就是请求参数,又叫做查询字符串

5.1 什么叫做请求参数:

例1: http://www.webkaka.com/tutorial/server/2015/021013/

例2:https://www.baidu.com/s?wd=python&a=c

例1中没有请求参数!例2中?后边的就是请求参数

5.2 请求参数的形式:字典
kw = {'wd':'长城'}
5.3 请求参数的用法
requests.get(url,params=kw)
5.4 关于参数的注意点

在url地址中, 很多参数是没有用的,比如百度搜索的url地址,其中参数只有一个字段有用,其他的都可以删除 如何确定那些请求参数有用或者没用:挨个尝试! 对应的,在后续的爬虫中,越到很多参数的url地址,都可以尝试删除参数

5.5 两种方式:发送带参数的请求
  • https://www.baidu.com/s?wd=python发起请求可以使用requests.get(url, params=kw)的方式

    # 方式一:利用params参数发送带参数的请求
    import requests
    
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
    
    # 这是目标url
    # url = 'https://www.baidu.com/s?wd=python'
    
    # 最后有没有问号结果都一样
    url = 'https://www.baidu.com/s?'
    
    # 请求参数是一个字典 即wd=python
    kw = {'wd': 'python'}
    
    # 带上请求参数发起请求,获取响应
    response = requests.get(url, headers=headers, params=kw)
    
    # 当有多个请求参数时,requests接收的params参数为多个键值对的字典,比如 '?wd=python&a=c'-->{'wd': 'python', 'a': 'c'}
    
    print(response.content)
    
  • 也可以直接对https://www.baidu.com/s?wd=python完整的url直接发送请求,不使用params参数

    # 方式二:直接发送带参数的url的请求
    import requests
    
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
    
    url = 'https://www.baidu.com/s?wd=python'
    
    # kw = {'wd': 'python'}
    
    # url中包含了请求参数,所以此时无需params
    response = requests.get(url, headers=headers)
    

6、小结

  1. requests模块的介绍:能够帮助我们发起请求获取响应
  2. requests的基本使用:requests.get(url)
  3. 以及response常见的属性:
    • response.text 响应体 str类型
    • respones.content 响应体 bytes类型
    • response.status_code 响应状态码
    • response.request.headers 响应对应的请求头
    • response.headers 响应头
    • response.request._cookies 响应对应请求的cookie
    • response.cookies 响应的cookie(经过了set-cookie动作)
  4. 掌握 requests.text和content的区别:text返回str类型,content返回bytes类型
  5. 掌握 解决网页的解码问题:
    • response.content.decode()
    • response.content.decode("UTF-8")
    • response.text
  6. 掌握 requests模块发送带headers的请求:requests.get(url, headers={})
  7. 掌握 requests模块发送带参数的get请求:requests.get(url, params={})

二、requests模块的深入使用

学习目标:
  1. 能够应用requests发送post请求的方法
  2. 能够应用requests模块使用代理的方法
  3. 了解代理ip的分类

1、使用requests发送POST请求

思考:哪些地方我们会用到POST请求?

  1. 登录注册( POST 比 GET 更安全)
  2. 需要传输大文本内容的时候( POST 请求对数据长度没有要求)

所以同样的,我们的爬虫也需要在这两个地方回去模拟浏览器发送post请求

1.1 requests发送post请求语法:
  • 用法:

    response = requests.post("http://www.baidu.com/", data = data, headers=headers)
    
  • data 的形式:字典

1.2 POST请求练习

下面面我们通过金山翻译的例子看看post请求如何使用:

地址:https://www.iciba.com/fy

思路分析
  1. 抓包确定请求的url地址

    截屏2022-04-20 下午3.22.11

  2. 确定请求的参数

    截屏2022-04-20 下午3.23.07

  3. 确定返回数据的位置

    image-20220420152404175

  4. 模拟浏览器获取数据

    import requests
    import json
    
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
    
    url = 'https://ifanyi.iciba.com/index.php?c=trans&m=fy&client=6&auth_user=key_ciba&sign=99730f3bf66b2582'
    
    data = {
        'from': 'zh',
        'to': 'en',
        'q': 'lucky 是一个帅气的老师'
    }
    
    res = requests.post(url, headers=headers, data=data)
    # print(res.status_code)
    
    # 返回的是json字符串 需要在进行转换为字典
    data = json.loads(res.content.decode('UTF-8'))
    # print(type(data))
    print(data)
    print(data['content']['out'])
    

1.3 小结

在模拟登陆等场景,经常需要发送post请求,直接使用requests.post(url,data)即可

2、使用代理

2.1 为什么要使用代理
  1. 让服务器以为不是同一个客户端在请求
  2. 防止我们的真实地址被泄露,防止被追究
2.2 理解使用代理的过程

使用代理的过程

2.3 理解正向代理和反向代理的区别(扩展)

正向代理和反向代理的区别

通过上图可以看出:

  • 正向代理:对于浏览器知道服务器的真实地址,例如VPN
  • 反向代理:浏览器不知道服务器的真实地址,例如nginx

详细讲解:

正向代理是客户端与正向代理客户端在同一局域网,客户端发出请求,正向代理 替代客户端向服务器发出请求。服务器不知道谁是真正的客户端,正向代理隐藏了真实的请求客户端。
反向代理:服务器与反向代理在同一个局域网,客服端发出请求,反向代理接收请求 ,反向代理服务器会把我们的请求分转发到真实提供服务的各台服务器Nginx就是性能非常好的反向代理服务器,用来做负载均衡

20190715113403480

2.4 代理的使用
  • 用法:

    requests.get("http://httpbin.org/ip",  proxies = proxies)
    
  • proxies的形式:字典

  • 例如:

    proxies = {
          "http": "http://12.34.56.79:9527",
          "https": "https://12.34.56.79:9527",
    }
    
2.5 代理IP的分类

根据代理ip的匿名程度,代理IP可以分为下面三类:

  • 透明代理(Transparent Proxy):透明代理的意思是客户端根本不需要知道有代理服务器的存在,但是它传送的仍然是真实的IP。使用透明代理时,对方服务器是可以知道你使用了代理的,并且他们也知道你的真实IP。你要想隐藏的话,不要用这个。透明代理为什么无法隐藏身份呢?因为他们将你的真实IP发送给了对方服务器,所以无法达到保护真实信息。
  • 匿名代理(Anonymous Proxy):匿名代理隐藏了您的真实IP,但是向访问对象可以检测是使用代理服务器访问他们的。会改变我们的请求信息,服务器端有可能会认为我们使用了代理。不过使用此种代理时,虽然被访问的网站不能知道你的ip地址,但仍然可以知道你在使用代理,当然某些能够侦测ip的网页也是可以查到你的ip。(https://wenku.baidu.com/view/9bf7b5bd3a3567ec102de2bd960590c69fc3d8cf.html)
  • 高匿代理(Elite proxy或High Anonymity Proxy):高匿名代理不改变客户机的请求,这样在服务器看来就像有个真正的客户浏览器在访问它,这时客户的真实IP是隐藏的,完全用代理服务器的信息替代了您的所有信息,就象您就是完全使用那台代理服务器直接访问对象,同时服务器端不会认为我们使用了代理。IPDIEA覆盖全球240+国家地区ip高匿名代理不必担心被追踪。

在使用的使用,毫无疑问使用高匿代理效果最好

从请求使用的协议可以分为:

  • http代理
  • https代理
  • socket代理等

不同分类的代理,在使用的时候需要根据抓取网站的协议来选择

2.6 代理IP使用的注意点
  • 反反爬

    使用代理ip是非常必要的一种反反爬的方式

    但是即使使用了代理ip,对方服务器任然会有很多的方式来检测我们是否是一个爬虫,比如:

    • 一段时间内,检测IP访问的频率,访问太多频繁会屏蔽

    • 检查Cookie,User-Agent,Referer等header参数,若没有则屏蔽

    • 服务方购买所有代理提供商,加入到反爬虫数据库里,若检测是代理则屏蔽

      所以更好的方式在使用代理ip的时候使用随机的方式进行选择使用,不要每次都用一个代理ip

  • 代理ip池的更新

    购买的代理ip很多时候大部分(超过60%)可能都没办法使用,这个时候就需要通过程序去检测哪些可用,把不能用的删除掉。

3、配置

  • 浏览器配置代理

    右边三点==> 设置==> 高级==> 代理==> 局域网设置==> 为LAN使用代理==> 输入ip和端口号即可

    参考网址:https://jingyan.baidu.com/article/a681b0dece76407a1843468d.html

  • 代码配置

    urllib

    handler = urllib.request.ProxyHandler({'http': '114.215.95.188:3128'})
    opener = urllib.request.build_opener(handler)
    # 后续都使用opener.open方法去发送请求即可
    

    requests

    # 用到的库
    import requests
    # 写入获取到的ip地址到proxy
    # 一个ip地址
    proxy = {
        'http':'http://221.178.232.130:8080'
    }
    """
    # 多个ip地址
    proxy = [
      {'http':'http://221.178.232.130:8080'},
      {'http':'http://221.178.232.130:8080'}
    ]
    import random
    proxy = random.choice(proxy)
    """
    
    # 使用代理
    proxy = {
        'http': 'http://58.20.184.187:9091'
    }
    
    result = requests.get("http://httpbin.org/ip", proxies=proxy)
    
    print(result.text)
    

4、小结

  1. requests发送post请求使用requests.post方法,带上请求体,其中请求体需要时字典的形式,传递给data参数接收
  2. 在requests中使用代理,需要准备字典形式的代理,传递给proxies参数接收
  3. 不同协议的url地址,需要使用不同的代理去请求

三、requests模块处理cookie相关的请求

学习目标

掌握requests处理cookie的三种方法

1 爬虫中使用cookie

为了能够通过爬虫获取到登录后的页面,或者是解决通过cookie的反扒,需要使用request来处理cookie相关的请求

1.1 爬虫中使用cookie的利弊
  • 带上cookie的好处
    • 能够访问登录后的页面
    • 能够实现部分反反爬
  • 带上cookie的坏处
    • 一套cookie往往对应的是一个用户的信息,请求太频繁有更大的可能性被对方识别为爬虫
    • 那么上面的问题如何解决 ?使用多个账号
1.2 requests处理cookie的方法

使用requests处理cookie有三种方法:

  • cookie字符串放在headers中
  • 把cookie字典放传给请求方法的cookies参数接收
  • 使用requests提供的session模块

2、cookie添加在heades中

2.1 headers中cookie的位置

headers中的cookie

  • headers中的cookie:
    • 使用分号(;)隔开
    • 分号两边的类似a=b形式的表示一条cookie
    • a=b中,a表示键(name),b表示值(value)
    • 在headers中仅仅使用了cookie的name和value
2.2 cookie的具体组成的字段

cookie的具体字段

由于headers中对cookie仅仅使用它的name和value,所以在代码中我们仅仅需要cookie的name和value即可

2.3 在headers中使用cookie

复制浏览器中的cookie到代码中使用

headers = {
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
"Cookie":" Pycharm-26c2d973=dbb9b300-2483-478f-9f5a-16ca4580177e; Hm_lvt_98b9d8c2fd6608d564bf2ac2ae642948=1512607763; Pycharm-26c2d974=f645329f-338e-486c-82c2-29e2a0205c74; _xsrf=2|d1a3d8ea|c5b07851cbce048bd5453846445de19d|1522379036"}

requests.get(url,headers=headers)

注意:

cookie有过期时间 ,所以直接复制浏览器中的cookie可能意味着下一程序继续运行的时候需要替换代码中的cookie,对应的我们也可以通过一个程序专门来获取cookie供其他程序使用;当然也有很多网站的cookie过期时间很长,这种情况下,直接复制cookie来使用更加简单

3、使用cookies参数接收字典形式的cookie

  • cookies的形式:字典
cookies = {"cookie的name":"cookie的value"}

  • 使用方法:
requests.get(url,headers=headers,cookies=cookie_dict)

  • 实例(爬取雪球网)

    在网络中找到当前请求的网址 点击cookies 将当前的k,value复制到代码中

    image-20220420190426741

    cookie_dict = {
        'u': '1990923459',
        'bid': '1f110dfd43538f4b8362dfcd21ffbb64_l27g4lfl',
        'xq_is_login': '1',
        'xq_r_token': '5dcbe83944f0b75325f91246061d4a2a01999367'
    }
    
    

    完整代码

    import requests
    
    # 携带cookie登录雪球网  抓取完善个人资料页面
    headers = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
        'Referer': 'https://xueqiu.com/u/1990923459',
        'Host': 'xueqiu.com',
    }
    url = 'https://xueqiu.com/users/connectnew?redirect=/setting/user'
    
    cookie_dict = {
        'u': '1990923459',
        'bid': '1f110dfd43538f4b8362dfcd21ffbb64_l27g4lfl',
        'xq_is_login': '1',
        'xq_r_token': '5dcbe83944f0b75325f91246061d4a2a01999367'
    }
    res = requests.get(url, headers=headers, cookies=cookie_dict)
    with open('雪球网.html', 'w') as f:
        f.write(res.content.decode('UTF-8'))
        print(res.content.decode('UTF-8'))
    
    

    成果

    image-20220420190612671

4、使用requests.session处理cookie

前面使用手动的方式使用cookie,那么有没有更好的方法在requets中处理cookie呢?

requests 提供了一个叫做session类,来实现客户端和服务端的会话保持

会话保持有两个内涵:

  • 保存cookie,下一次请求会带上前一次的cookie
  • 实现和服务端的长连接,加快请求速度
4.1 使用方法
session = requests.session()
response = session.get(url,headers)

session实例在请求了一个网站后,对方服务器设置在本地的cookie会保存在session中,下一次再使用session请求对方服务器的时候,会带上前一次的cookie

4.2 动手练习:模拟登陆
思路分析
  1. 准备url地址和请求参数
  2. 构造session发送post请求
  3. 使用session请求个人主页,观察是否请求成功

5、小结

  1. cookie字符串可以放在headers字典中,键为Cookie,值为cookie字符串
  2. 可以把cookie字符串转化为字典,使用请求方法的cookies参数接收
  3. 使用requests提供的session模块,能够自动实现cookie的处理,包括请求的时候携带cookie,获取响应的时候保存cookie

四、requests模块的其他方法

学习目标
  1. 掌握requests中cookirJar的处理方法
  2. 掌握requests解决https证书错误的问题
  3. 掌握requests中超时参数的使用

1、requests中cookirJar的处理方法

使用request获取的resposne对象,具有cookies属性,能够获取对方服务器设置在本地的cookie,但是如何使用这些cookie呢?

1.1 方法介绍
  1. response.cookies是CookieJar类型
  2. 使用requests.utils.dict_from_cookiejar,能够实现把cookiejar对象转化为字典
1.2 方法展示
import requests

url = "http://www.baidu.com"
#发送请求,获取resposne
response = requests.get(url)
print(type(response.cookies))

#使用方法从cookiejar中提取数据  等同于  dict(response.cookies)
cookies = requests.utils.dict_from_cookiejar(response.cookies)
print(cookies)

输出为:

<class 'requests.cookies.RequestsCookieJar'>
{'BDORZ': '27315'}

注意:

在前面的requests的session类中,我们不需要处理cookie的任何细节,如果有需要,我们可以使用上述方法来解决

2、requests处理证书错误

经常我们在网上冲浪时,经常能够看到下面的提示:

12306ssl错误

出现这个问题的原因是:ssl的证书不安全导致

2.1 代码中发起请求的效果

那么如果在代码中请求会怎么样呢?

import requests

url = "https://www.12306.cn/mormhweb/"
response = requests.get(url)

返回证书错误,如下:

ssl.CertificateError ...

2.2 解决方案

为了在代码中能够正常的请求,我们修改添加一个参数

import requests

url = "https://www.12306.cn/mormhweb/"
response = requests.get(url, verify=False)

3、超时参数的使用

在平时网上冲浪的过程中,我们经常会遇到网络波动,这个时候,一个请求等了很久可能任然没有结果

在爬虫中,一个请求很久没有结果,就会让整个项目的效率变得非常低,这个时候我们就需要对请求进行强制要求,让他必须在特定的时间内返回结果,否则就报错

3.1 超时参数使用方法如下:
response = requests.get(url,timeout=3)

通过添加timeout参数,能够保证在3秒钟内返回响应,否则会报错

注意:

这个方法还能够拿来检测代理ip的质量,如果一个代理ip在很长时间没有响应,那么添加超时之后也会报错,对应的这个ip就可以从代理ip池中删除

古诗文模拟登陆

import base64
import json
import requests


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__":
    session = requests.Session()
    # 对验证码进行下载
    url = 'https://so.gushiwen.cn/RandCode.ashx'
    res = session.get(url)
    with open('yzm.jpg', 'wb') as f:
        f.write(res.content)
    # 识别验证码
    img_path = "./yzm.jpg"
    result = base64_api(uname='luckyboyxlg', pwd='17346570232', img=img_path, typeid=3)
    # print(result)
    # 开始进行模拟登陆
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36',
        'referer': 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
    }
    data = {
        '__VIEWSTATE': 'jCBCra0OizbfPOkkaq8AZq63uZiPE/sE/FRvIRQ/OU2U41waB87FQZOiC3LJYmcK4RQFA0qAqOueMPbLgAGaKuStZmqh7Fpbpe3oRc81IYfUtFwcE0qH0MXg4LY69710V4JPmbTfMYPsDXHvdCy3lYV8ddo=',
       '__VIEWSTATEGENERATOR': 'C93BE1AE',
        'from': 'http://so.gushiwen.cn/user/collect.aspx',
        'email': '793390457@qq.com',
        'pwd': '17346570232',
        'code': result,
        'denglu': '登录'
    }
    url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'
    res = session.post(url, headers=headers, data=data)
    print(res.content.decode())

图片懒加载抓取

import os.path

import requests
from lxml import etree


headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}
url = 'https://qq.yh31.com/xq/wq/'
res = requests.get(url, headers=headers)
html = res.content.decode()
# print(html)
tree = etree.HTML(html)
src_list = tree.xpath('//div[@class="sr"]/li/dt/a/img/@data-src')
path = 'img'
if not os.path.exists(path):
    os.mkdir(path)
i = 0
for url in src_list:
    res = requests.get(url, headers=headers)
    with open(os.path.join(path, str(i)+'.jpg'), 'wb') as f:
        f.write(res.content)
    print(i)
    i += 1
posted @ 2023-03-18 10:42  凫弥  阅读(64)  评论(0编辑  收藏  举报