【2.0】爬虫模块之requests

【一】Requests模块简介

【1】简解

  • Requests 是⽤Python语⾔编写,基于urllib,采⽤Apache2 Licensed开源协议的 HTTP 库。
  • 它⽐ urllib 更加⽅便,可以节约我们⼤量的⼯作,完全满⾜HTTP测试需求。
  • 是一个功能强大、简洁易用的第三方Python库,用于发送HTTP请求。

【2】来源

  • 可以模拟发送http请求

  • urlib2:内置库,不太好用,繁琐

  • 封装出requests模块,应用非常广泛(公司,人)

    • requests模块最初由Kenneth Reitz于2010年创建并开源,旨在提供一种更人性化的方式来发送HTTP请求。
    • 它的出现填补了Python标准库中urllib和urllib2模块使用起来不够友好的问题。
  • 小插曲
    • Kenneth Reitz是一位知名的Python爱好者和开源社区活跃分子。他也是许多其他流行Python工具的作者之一,比如tablib、pipenv等。
    • Kenneth Reitz众筹换电脑(性能跟不上了),捐钱(谷歌公司捐了),2 万 8 千美元
    • 买了游戏设备,爆料出来,骗捐,辟谣

【二】基础知识储备

【1】http协议特点

参考链接:https://www.cnblogs.com/dream-ze/p/17337378.html

  • 应用层协议:
    • HTTP是一种应用层协议,用于定义客户端和服务器之间传输数据的规则。
    • 与其他应用层协议如MySQL、Redis、MongoDB等不同,它使用的是基于请求-响应模式的交互方式。
  • 基于请求-响应模式:
    • HTTP采用客户端主动发起请求,并等待服务器响应的模式。这意味着服务端不能主动向客户端推送消息。
    • 要实现主动推送消息,可以使用轮询、长轮询或WebSocket协议。
  • 无状态保存:
    • HTTP协议本身是无状态的,它不会保存客户端的状态信息。为了在多个请求之间共享状态,常用的解决方案包括使用Cookie、Session和Token等机制来维持用户会话。
  • 无连接:
    • 每次请求-响应周期都需要建立和断开连接,这会对性能产生影响。
    • 但从HTTP协议的设计角度来看,它是无连接的,即在收到响应后就断开连接。
    • 然而,HTTP 1.1引入了持久连接,允许在一个TCP连接上发送多个HTTP请求,从而提高了性能效率。
    • HTTP 2.0更进一步,可以在一个TCP连接上同时发送多个HTTP请求和响应。

【2】HTTP请求

参考博客:https://www.cnblogs.com/dream-ze/p/17176477.html

  • 请求首行:
    • 请求首行指的是在发送HTTP请求时的第一行
    • 包括
      • 请求方法(GET、POST等)
      • 请求URI(Uniform Resource Identifier)。
  • 请求头:
    • 请求头包含了关于请求的附加信息,以键值对的形式表示。
    • 常见的请求头
      • Content-Type
      • User-Agent
      • Cookie等
    • 它们用来描述请求的相关属性和数据。
  • 请求体:
    • 请求体可选,通常用于POST请求,在请求体中携带参数和数据。
    • 请求体的格式可以是
      • urlencode形式的键值对
      • JSON格式
      • formdata格式。

【3】HTTP响应

  • 响应首行:

    • 响应首行指的是服务器返回响应时的第一行

    • 其中包含了状态码,表示服务器对请求的处理结果。

    • 常见的状态码有200(成功)、301(永久重定向)、302(临时重定向)等。

  • 响应头:

    • 响应头包含了关于响应的附加信息,以键值对的形式表示。
    • 常见的响应头有Set-Cookie、Cache-Control等,它们用来描述响应的相关属性和数据。
  • 响应体:

    • 响应体是服务器返回给客户端的实际数据。
    • 它可以是HTML格式的网页内容,也可以是JSON格式的数据等。

【4】浏览器调试:

  • 调试模式:
    • 通过右键浏览器页面并选择调试模式,可以打开开发者工具,方便进行调试和查看页面的相关信息。
  • Elements:
    • Elements面板用于查看和编辑网页的结构和内容,其中包括响应体中的HTML格式数据。
  • Console:
    • Console面板是开发者用于在JavaScript中输出调试信息的窗口,在这里可以查看通过console.log()等方法输出的内容。
  • Network:
    • Network面板用于监视和查看浏览器发送和接收的网络请求,包括AJAX请求。
    • 可以查看所有请求或者仅显示XHR(XMLHttpRequest)请求。

【三】Requests模块基础使用

【1】安装

pip install requests

【2】简单使用

# 导入 requests 模块
import requests

# 使用 requests 模块发起简单的 get  请求
response = requests.get('https://www.baidu.com/')
# 对页面进行转码 ( 页面可能需要 utf-8 转码也可能是 gbk 转码 )
response.encoding = "utf-8"
# 查看请求到的页面数据
print(response.text)

【四】Requests模块之请求

【1】发送Get请求

  • HTTP默认的请求方法就是GET
    • 没有请求体
    • 数据必须在1K之内!
    • GET请求数据会暴露在浏览器的地址栏中
  • GET请求常用的操作:
    • 在浏览器的地址栏中直接给出URL,那么就一定是GET请求
    • 点击页面上的超链接也一定是GET请求
    • 提交表单时,表单默认使用GET请求,但可以设置为POST
  • 在Python中,我们可以使用requests模块来发送HTTP请求。

  • 其中,GET请求是最常用的一种请求方式,用于从服务器获取数据。

  • 首先,我们需要通过import语句导入requests模块:

import requests
  • 然后,我们可以使用get方法发送一个GET请求,并指定请求的URL:
response = requests.get(url)

在上述代码中,url是我们要请求的目标URL。

  • 发送GET请求后,服务器会返回一个响应对象,我们可以通过访问这个响应对象的属性和方法来获取服务器返回的数据。

  • 其中,一些常用的属性和方法包括:

    • status_code: 响应的状态码,例如200表示请求成功,404表示页面不存在等。

    • text: 响应的内容,通常是服务器返回的HTML文本。

    • json(): 将响应的内容解析为JSON格式。

  • 示例,演示如何发送GET请求并获取服务器返回的数据:

import requests

# 发送GET请求
url = "http://example.com/api/data"
response = requests.get(url)

# 检查响应的状态码
if response.status_code == 200:
    # 提取响应的内容
    data = response.text
    print(data)
else:
    print("请求失败")
  • 在上述示例中,我们发送了一个GET请求到http://example.com/api/data,并打印出了服务器返回的内容。

【2】发送Get请求之携带参数

(1)携带请求体

  • 需要注意的是,发送GET请求时,可以通过URL传递参数
    • 例如:http://example.com/api/data?param1=value1&param2=value2
  • 如果需要传递参数,可以使用params参数来指定,例如:
# 发送带有参数的GET请求
url = "http://example.com/api/data"
params = {
    'param1': 'value1',
    'param2': 'value2'
}
response = requests.get(url, params=params)
  • 在上述示例中,我们通过params参数指定了要传递的参数。

(2)携带请求头

  • 常见的HTTP头部字段包括:

    • Host:指定目标服务器的域名或IP地址。

    • User-Agent:标识发送请求的用户代理(通常是浏览器)。

      • PC浏览器
      • APP浏览器
      • Linux
      • macOS
    • Accept:指定客户端能够接收的内容类型。

    • Content-Type:指定请求或响应中实体的媒体类型。

    • Content-Length:指定实体主体的长度(以字节为单位)。

    • Cookie:向服务器传递保存在客户端的cookie信息。

    • Cache-Control:指定如何缓存和重新验证响应。

    • Referer :大型网站通常都会根据该参数判断请求的来源

[1]携带 请求载体

  • headers
    • 载体标识
  • 添加headers(浏览器会识别请求头,不加可能会被拒绝访问,
  • 比如访问https://www.zhihu.com/explore)
  • 不携带请求载体标识 User-Agent
import requests

response = requests.get('https://www.zhihu.com/explore')
response.status_code  # 500
  • 主动携带请求载体标识 User-Agent
import requests

headers = {
    'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36',
}
respone = requests.get('https://www.zhihu.com/explore',
                       headers=headers)
print(respone.status_code)  # 200

[2]携带 请求参数

方式一
import requests
from urllib.parse import urlencode

wd = '美女'
# 如果查询关键词是中文或者有其他特殊符号,则不得不进行url编码
encode_res = urlencode({'k': wd}, encoding='utf-8')
keyword = encode_res.split('=')[1]
# print(keyword) # %E7%BE%8E%E5%A5%B3
# 然后拼接成url
url = 'https://www.baidu.com/s?wd=%s&pn=1' % keyword

response = requests.get(url,
                        headers={
                            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36',
                        })
response = response.text
print(response)
with open('a.html', 'w', encoding='utf-8') as f:
    f.write(response)
方式二
  • 本质上也是调用urlencode
import requests
from urllib.parse import urlencode

wd = '美女'
pn = 1

response = requests.get('https://www.baidu.com/s',
                        params={
                            'wd': wd,
                            'pn': pn
                        },
                        headers={
                            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36',
                        })
response = response.text

with open('b.html', 'w', encoding='utf-8') as f:
    f.write(response)
  • 验证结果,打开a.html与b.html页面内容一样

[3]携带标识 cookies

  • 登录github,然后从浏览器中获取cookies,以后就可以直接拿着cookie登录了,无需输入用户名密码
方式一:携带在请求中
import requests

Cookies={   'user_session':'xxx',
}

#github对请求头没有什么限制,我们无需定制user-agent,对于其他网站可能还需要定制
response=requests.get('https://github.com/settings/emails',cookies=Cookies) 

print('xxx@qq.com' in response.text) #True
方式二:携带在请求参数中
import requests

header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
    'Cookie':'xxx',
}
data = {
    'linkId': 'xxx'
}
response = requests.post('https://dig.chouti.com/link/vote', headers=header,data=data)
print(response.text)

【3】发送POST请求

  • (1). 数据不会出现在地址栏中
  • (2). 数据的大小没有上限
  • (3). 有请求体
  • (4). 请求体中如果存在中文,会使用URL编码!
  • !!!requests.post()用法与requests.get()完全一致,特殊的是requests.post()有一个data参数,用来存放请求体数据
import requests

header = {
    'Referer': 'http://www.aa7a.cn/user.php?&ref=http%3A%2F%2Fwww.aa7a.cn%2F',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}

data = {
    'username': 'zzz',
    'password': 'xxx',
    'captcha': 'xxxx',
    'remember': 1,
    'ref': ' http://www.aa7a.cn/',
    'act': 'act_login'
}
# data,json请求体的数据
# 编码方式是 urlencoded
# res = requests.post('http://www.aa7a.cn/user.php', data=data, headers=header)
# # 编码是json格式
# # res = requests.post('http://www.aa7a.cn/user.php',json=data,headers=header)
# print(res.text)
  • json=data编码格式:
    • 当使用requests.post方法时,如果将data参数设置为一个字典,并同时将headers参数中的Content-Type设置为application/json,那么data字典将被自动序列化为JSON字符串,并作为请求的主体数据发送。
    • 这样的请求方式常用于与服务器交互时,需要使用JSON格式进行数据传输的情况。
  • data=data编码格式:
    • 默认情况下,requests.post方法将会将data参数以application/x-www-form-urlencoded格式进行编码。
    • 这种编码方式将字典数据转换成键值对的形式,并使用&符号进行连接。
    • 然后,将生成的字符串作为请求的主体数据发送到服务器。这种方式常用于处理表单提交的场景。

【4】POST请求应用之登陆github

  • 对于登录来说,应该输错用户名或密码然后分析抓包流程,用脑子想一想,输对了浏览器就跳转了,还分析个毛线,累死你也找不到包

(1)目标站点分析

  • 浏览器输入:

    • https://github.com/login
  • 然后输入错误的账号密码

    • 抓包
  • 发现登录行为是post提交到

    • https://github.com/session

    • 请求头包含cookie

    • 请求体包含

      commit:Sign in
      utf8:✓ authenticity_token:lbI8IJCwGslZS8qJPnof5e7ZkCoSoMn6jmDTsL1r/m06NLyIbw7vCrpwrFAPzHMep3Tmf/TSJVoXWrvDZaVwxQ==
              login:egonlin
              password:123
      

(2)流程分析

  • 先GET

    • https://github.com/login
    • 拿到初始cookie与authenticity_token
  • 返回POST

    • https://github.com/session
  • 带上初始cookie,带上请求体(authenticity_token,用户名,密码等)

  • 最后拿到登录cookie

  • ps:
    • 如果密码时密文形式,则可以先输错账号,输对密码,然后到浏览器中拿到加密后的密码,github的密码是明文

(3)代码实现

[1]第一次请求

import requests
import re

response=requests.get('https://github.com/login')
response_cookie=response.cookies.get_dict() #拿到初始cookie(未被授权)
authenticity_token=re.findall(r'name="authenticity_token".*?value="(.*?)"',response.text)[0] #从页面中拿到CSRF TOKEN

[2]第二次请求

  • 带着初始cookie和TOKEN发送POST请求给登录页面
    • 带上账号密码
data = {
    'commit': 'Sign in',
    'utf8': '✓',
    'authenticity_token': authenticity_token,
    'login': '317828332@qq.com',
    'password': 'alex3714'
}
response = requests.post('https://github.com/session',
                         data=data,
                         cookies=response_cookie
                         )

login_cookie = response.cookies.get_dict()

[3]第三次请求

  • 以后的登录,拿着login_cookie就可以
    • 比如访问一些个人配置
response = requests.get('https://github.com/settings/emails',
                        cookies=login_cookie)

print('317828332@qq.com' in response.text)  # True

[4]完整

import requests
import re

# (1)第一次请求
response = requests.get('https://github.com/login')
response_cookie = response.cookies.get_dict()  # 拿到初始cookie(未被授权)
authenticity_token = re.findall(r'name="authenticity_token".*?value="(.*?)"', response.text)[0]  # 从页面中拿到CSRF TOKEN

# (2)第二次请求:带着初始cookie和TOKEN发送POST请求给登录页面,带上账号密码
data = {
    'commit': 'Sign in',
    'utf8': '✓',
    'authenticity_token': authenticity_token,
    'login': '317828332@qq.com',
    'password': 'alex3714'
}
response = requests.post('https://github.com/session',
                         data=data,
                         cookies=response_cookie
                         )

login_cookie = response.cookies.get_dict()

# (3)第三次请求:以后的登录,拿着login_cookie就可以,比如访问一些个人配置
response = requests.get('https://github.com/settings/emails',
                        cookies=login_cookie)

print('317828332@qq.com' in response.text)  # True

【5】POST请求补充

# 没有指定请求头,#默认的请求头:application/x-www-form-urlencoed
requests.post(url='xxxxxxxx',
              data={'xxx': 'yyy'})  

# 如果我们自定义请求头是application/json,并且用data传值, 则服务端取不到值
requests.post(url='',
              data={'': 1, },
              headers={
                  'content-type': 'application/json'
              })

# 默认的请求头:application/json
requests.post(url='',
              json={'': 1, },
              )  

【6】自动携带cookie 的session对象

  • 不使用session
import requests

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',
}

# 获取从服务器端响应的cookie
url = 'https://xueqiu.com/'
res = requests.get(url, headers=headers)
cookies = dict(res.cookies)
url = 'https://stock.xueqiu.com/v5/stock/batch/quote.json?symbol=SH000001,SZ399001,SZ399006,SH000688,SH000016,SH000300,BJ899050,HKHSI,HKHSCEI,HKHSTECH,.DJI,.IXIC,.INX'
res = requests.get(url, headers=headers, cookies=cookies)
# print(res.json())
print(res.content.decode())
  • 使用session
import requests
#1.创建一个空白的session对象
session = requests.Session()

headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36',
}
main_url = 'https://xueqiu.com/'
#2.使用session发起的请求,目的是为了捕获到cookie,且将其存储到session对象中
session.get(url=main_url,headers=headers)

url = 'https://xueqiu.com/statuses/hot/listV2.json'
param = {
    "since_id": "-1",
    "max_id": "311519",
    "size": "15",
}
#3.就是使用携带了cookie的session对象发起的请求(就是携带者cookie发起的请求)
response = session.get(url=url,headers=headers,params=param)
data = response.json()
print(data)

【补充】url编码和解码

【补充】长链转短链

【1】简解

  • 长链转短链是一种将长URL转换为短URL的过程。
  • 通常,通过将长URL映射到一个较短的标识符或者域名来实现。
  • 这种转换可以提供一些实际的好处,如更美观的URL、节省空间和更方便的分享。

【2】示例

  • 原始长链:https://www.cnblogs.com/******g/p/16005866.html

  • 转短链服务:申请短域名(例如:m.tb.cn)

  • 生成随机字符串:假设生成的字符串为9QqPdHKXc2n

  • 在数据库中建立映射关系:将随机字符串与原始长链对应存储在数据库中,例如:

    id   随机字符串     真正地址
    1    9QqPdHKXc2n   https://www.cnblogs.com/********g/p/16005866.html
    
  • 返回短链给用户:https://m.tb.cn/h.5bZAfFS?tk=9QqPdHKXc2n

  • 用户使用短链访问:当用户点击短链(https://m.tb.cn/h.5bZAfFS?tk=9QqPdHKXc2n)时,请求会发送到短链服务。

  • 获取随机字符串:短链服务从URL中提取随机字符串(9QqPdHKXc2n)。

  • 查询数据库:短链服务在数据库中查找对应的真正地址。

  • 重定向到真正地址:短链服务将用户重定向到与随机字符串对应的真正地址(https://www.cnblogs.com/********g/p/16005866.html),实现长链到短链的跳转。

  • 通过这个过程,可以将原始长链转换为一个更短、易于分享和记忆的短链,并且当用户点击短链时能够实现跳转到原始长链。

【3】注意事项

  • 长链转短链需要使用合法的短域名,并在后台通过数据库进行映射管理以确保正确的跳转。
  • 此外,短链服务需要处理并发请求,有效地检索数据库并进行重定向操作,确保系统的性能和稳定性。

【五】Requests模块之响应

  • requests模块的响应对象包含了许多参数和方法,可以帮助我们处理和分析HTTP请求的响应。

【1】响应相关参数

(1)响应体相关:

  • response.text: 将响应体转换为字符串形式。
  • response.content: 获取响应体的二进制内容,适用于处理图像、视频等非文本类型的响应。(默认是16进制)
  • response.encoding:获取响应的编码方式
import requests

response = requests.get('http://www.baidu.com/')

print(response.text)
print(response.content)
print(response.encoding)

(2)响应状态码和头部相关:

  • response.status_code: 获取响应的状态码。
  • response.headers: 获取响应头信息,返回一个字典对象。
  • response.cookies: 获取服务器返回的cookie信息。
  • response.cookies.get_dict(): 将cookie信息转换为字典形式。
  • response.cookies.items(): 获取cookie信息并以列表形式返回。
import requests

response = requests.get('http://www.baidu.com/')

print(response.status_code)
print(response.headers)
print(response.cookies)
print(response.cookies.get_dict())
print(response.cookies.items())

(3)其他参数和方法:

  • response.url: 获取当前请求的URL。
  • response.history: 如果有重定向,返回一个列表,包含所有经过的重定向URL。
import requests

response = requests.get('http://www.baidu.com/')

print(response.url)
print(response.history)
  • response.iter_content() :迭代获取响应内容(适用于处理视频、图片等二进制数据)
with open("a.mp4", 'wb') as f:
    for chunk in response.iter_content(chunk_size=1024):
        if chunk:
            f.write(chunk)

【2】解析json数据

  • 要解析 JSON 数据,可以使用 requests 模块的 json() 方法。
  • 该方法将响应内容解析为 JSON 格式,并返回一个 Python 字典对象。
import requests

# 发送请求,获取响应数据
response = requests.get(url)

# 打印响应内容的数据类型
print(type(response.text))

# 打印原始响应内容
print(response.text)

# 将响应内容解析为 JSON 格式
data = response.json()

# 打印解析后的 JSON 数据
print(data)
  • 在上述代码中,假设 url 是您要发送请求的目标 URL。
  • 首先,我们使用 requests 模块的 get() 方法发送请求并获取响应。
  • 然后,我们打印响应内容的数据类型和原始内容。
  • 接下来,我们使用 json() 方法对响应内容进行解析并将其存储在变量 data 中。
  • 最后,我们打印解析后的 JSON 数据。
  • 请注意,如果响应内容不符合 JSON 格式,或者您尝试解析的响应内容不是有效的 JSON 数据,那么调用 json() 方法时将引发 JSONDecodeError 异常。
  • 因此,在实际使用中,建议添加适当的错误处理来处理可能发生的异常情况。

【3】ssl认证(了解)

  • requests 模块中进行 SSL 认证时,可以通过 verify 参数来控制是否验证服务器的 SSL 证书。
  • 默认情况下,verify 参数被设置为 True,表示会验证 SSL 证书。
  • verify 设置为布尔值 False 时,会忽略证书验证,但会引发警告。
  • 下面是一个示例代码及其注释,演示了如何在 requests 中使用 SSL 认证:
import requests

# 发送请求,并禁用 SSL 证书验证
response = requests.get(url, verify=False)

# 打印响应状态码
print(response.status_code)

# 打印响应内容
print(response.text)
  • 在上述代码中,我们使用 requests 模块的 get() 方法发送带有 URL 的请求。
  • verify 参数设置为 False,以禁用 SSL 证书验证。
  • 注意:禁用 SSL 证书验证存在安全风险,因为它允许您的请求受到中间人攻击。
  • 在生产环境中,强烈建议启用 SSL 证书验证,以确保通信的安全性。
  • 如果您希望使用自定义的证书进行验证,可以将 verify 参数设置为证书文件的路径。例如:
import requests

cert_file = "/path/to/my_certificate.pem"

# 发送请求,并使用自定义证书进行 SSL 验证
response = requests.get(url, verify=cert_file)

# 打印响应状态码
print(response.status_code)

# 打印响应内容
print(response.text)
  • 在上述代码中,将 verify 参数设置为自定义证书文件的路径。requests 将使用指定的证书进行验证。

  • 请注意,在实际使用中,如果从未提供过自定义证书,并且要禁用验证,则最好提供证书路径。这样可以避免不必要的错误和警告。

By default Requests will perform location redirection for all verbs except HEAD.

We can use the history property of the Response object to track redirection.

The Response.history list contains the Response objects that were created in order to complete the request. The list is sorted from the oldest to the most recent response.

For example, GitHub redirects all HTTP requests to HTTPS:

>>> r = requests.get('http://github.com')

>>> r.url
'https://github.com/'

>>> r.status_code
200

>>> r.history
[<Response [301]>]
If you're using GET, OPTIONS, POST, PUT, PATCH or DELETE, you can disable redirection handling with the allow_redirects parameter:

>>> r = requests.get('http://github.com', allow_redirects=False)

>>> r.status_code
301

>>> r.history
[]
If you're using HEAD, you can enable redirection as well:

>>> r = requests.head('http://github.com', allow_redirects=True)

>>> r.url
'https://github.com/'

>>> r.history
[<Response [301]>]

【4】使用代理(重要)

(1)在请求参数中使用代理

# 导入模块
import requests  # 发起requests请求
from lxml import etree  # 使用XPath解析页面数据
from fake_useragent import UserAgent  # 使用你的程序来计算随机的UserAgent
import asyncio
import aiohttp

# UA 伪装 headers
headers = {
    'User-Agent': UserAgent().random
}


def get_proxy():
    '''
    url : {89免费代理的url:https://www.89ip.cn/index_2.html}
    :return: 返回所有爬取到的ip列表
    '''
    # 存放所有代理存在的页码
    page_url_lists = []
    # 存放爬取到的所有代码的列表
    ip_lists = []
    # 循环获取每一页的代理页面的url
    for i in range(1, 5):
        if i == 1:
            # 首页的url
            page_url = 'https://www.89ip.cn/'
            # 将代理页url存储到类可用列表里
            page_url_lists.append(page_url)
        else:
            # 处理第二页及以后所有页码的规则
            page_url = f'https://www.89ip.cn/index_{i}.html'
            # 将代理页url存储到类可用列表里
            page_url_lists.append(page_url)
    # 从存储所有代理存在的页面的列表,循环发起请求获得代理ip和端口
    for pages_url in page_url_lists:
        response = requests.get(pages_url, headers=headers)

        response.encoding = 'utf-8'
        page_text = response.text
        tree = etree.HTML(page_text)
        # xpath提取到所有的代理ip列表
        tr_lists = tree.xpath('//div[3]/div[1]/div/div[1]/table/tbody/tr')
        # 循环获取每一个代理ip和端口
        for tr in tr_lists:
            # 获取ip并做数据清洗
            ip = tr.xpath('./td[1]/text()')[0].strip()
            # 获取端口并做数据清洗
            port = tr.xpath('./td[2]/text()')[0].strip()
            # 将获取到的代理ip和端口拼接
            ip_port = ip + ':' + str(port)
            # 将获取到的代理ip和端口存储到类可用列表里
            ip_lists.append({'http': ip_port})
    # 返回所有代理ip列表
    return ip_lists


def text_ip():
    '''
    url:{百度首页,}
    :return: 做测试返回状态码为200正常可用保留,否则再次执行验证
    '''
    url = "https://www.baidu.com/"
    # 获取到所有ip列表
    ip_lists = get_proxy()
    # 循环测试每一个ip和端口是否可用
    for ip_item in ip_lists:
        response = requests.get(url, proxies=ip_item, headers=headers, timeout=10)
        ret = response.status_code
        # 做判断是否返回值为200可用
        if ret == 200:
            print(f'当前状态码为:::{ret},ip可用,返回ip:::{ip_item}')
            return ip_item
        else:
            print(f'当前状态码为:::{ret},当前ip不可用,重新测试ip中')
            text_ip()


if __name__ == '__main__':
    ip_lists = get_proxy()
    ip_item = text_ip()
    url = 'https://www.baidu.com/'
    response = requests.get(url=url, headers=headers, proxies=ip_item)
    print(response.text)

(2)requests-socks

  • 使用代理是在使用requests模块时的一种常见需求,可以通过设置代理来对网络请求进行中转或隐藏真实的客户端 IP 地址。

  • 通过借助第三方库requests-socks来实现代理功能。requests-socks是一个专门为requests模块提供代理支持的库,它允许我们在使用requests时指定代理服务器信息。

安装requests-socks库:

pip install requests-socks

导入相关库和模块:

import requests
import requests.socks as socks
import socket

设置代理服务器的地址和端口:

# 代理服务器的地址和端口
proxy_host = '127.0.0.1'
proxy_port = 8888

创建一个Session对象,并设置代理:

# 创建一个Session对象
session = requests.Session()

# 创建一个代理连接
socks.set_default_proxy(socks.SOCKS5, proxy_host, proxy_port)
socket.socket = socks.socksocket

这里使用的是 SOCKS5 类型的代理,如果你使用的是其他类型的代理,请相应地调整参数。

发起请求:

# 发起GET请求
response = session.get('https://www.example.com')

在这个示例中,我们使用session对象发起了一个GET请求,并指定了目标地址为https://www.example.com

完整示例代码:

import requests
import requests.socks as socks
import socket

# 代理服务器的地址和端口
proxy_host = '127.0.0.1'
proxy_port = 8888

# 创建一个Session对象
session = requests.Session()

# 创建一个代理连接
socks.set_default_proxy(socks.SOCKS5, proxy_host, proxy_port)
socket.socket = socks.socksocket

# 发起请求
response = session.get('https://www.example.com')

# 输出响应内容
print(response.text)

请确保将proxy_hostproxy_port替换为实际的代理服务器地址和端口。通过以上步骤,你就可以在使用requests模块时使用代理服务器进行网络请求了。

【4】超时设置

  • 在使用requests模块时,可以通过设置超时参数来控制请求的超时时间。超时参数可以是一个浮点数或一个元组。

  • 如果超时参数是一个浮点数,它表示接收数据的超时时间,单位为秒。

    • 例如,timeout=0.1代表接收数据的超时时间为0.1秒。
  • 如果超时参数是一个元组,它包含两个值,分别表示连接超时时间和接收数据的超时时间,单位同样为秒。

    • 例如,timeout=(0.1, 0.2)代表连接超时时间为0.1秒,接收数据的超时时间为0.2秒。
  • 以下是使用requests模块进行超时设置的示例代码:

import requests

# 设置接收数据超时时间为0.1秒
response = requests.get('https://www.baidu.com', timeout=0.1)
print(response.status_code)

# 设置连接超时时间为0.1秒,接收数据超时时间为0.2秒
response = requests.get('https://www.baidu.com', timeout=(0.1, 0.2))
print(response.status_code)
  • 通过这样的超时设置,可以限制请求的响应时间,避免长时间阻塞等待结果导致程序无法继续执行。
  • 根据具体的需求,可以灵活调整超时时间来适应不同的网络环境和请求场景。

【5】认证设置

  • 认证设置:登陆网站是,弹出一个框,要求你输入用户名密码(与alter很类似),此时是无法获取html的
  • 但本质原理是拼接成请求头发送
    • r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
  • 一般的网站都不用默认的加密方式,都是自己写
  • 那么我们就需要按照网站的加密方式,自己写一个类似于_basic_auth_str的方法
  • 得到加密字符串后添加到请求头
    • r.headers['Authorization'] =func('.....')
  • 在使用requests模块进行认证设置时,可以通过HTTPBasicAuth类或简写方式来提供用户名和密码进行认证。

(1)使用HTTPBasicAuth类:

  • 可以使用requests.auth模块中的HTTPBasicAuth类来提供用户名和密码进行认证。
  • 示例代码如下:
import requests
from requests.auth import HTTPBasicAuth

# 使用HTTPBasicAuth类提供用户名和密码进行认证
r = requests.get('xxx', auth=HTTPBasicAuth('user', 'password'))
print(r.status_code)

(2)使用简写方式:

  • 可以直接在auth参数中以元组的形式提供用户名和密码进行认证,无需导入HTTPBasicAuth类。
  • 示例代码如下:
import requests

# 使用简写方式提供用户名和密码进行认证
r = requests.get('xxx', auth=('user', 'password'))
print(r.status_code)
  • 需要注意的是,上述示例代码中的'xxx'应替换为具体的URL地址,用于发送请求进行认证。
  • 默认情况下,这两种方式都会使用HTTP基本认证(Basic Authentication),即将用户名和密码以base64编码形式拼接到请求头的Authorization字段中发送给服务器。

  • 然而,大多数网站都会使用自定义的加密方式,所以需要根据网站的加密方式进行相应的处理,在认证之前生成正确的认证字符串,并将其添加到请求头的Authorization字段中。

  • 例如,如果网站使用了自定义的加密方式,可以按照该方式编写一个自定义函数,并将生成的认证字符串通过r.headers['Authorization'] = 函数名('...')的方式添加到请求头中。

  • 需要根据具体网站的加密方式和认证机制进行处理,可以参考相关网站的文档或联系网站开发人员获取详细的认证设置信息。

【6】异常处理

  • 在使用requests模块时,可以通过异常处理机制来捕获和处理可能出现的异常情况。
  • 通过对不同类型的异常进行处理,可以增加代码的健壮性和容错性。
  • 以下是关于requests模块的常见异常类型和异常处理的示例代码:
import requests
from requests.exceptions import *

try:
    r = requests.get('http://www.baidu.com', timeout=0.00001)
except ReadTimeout:
    print('ReadTimeout Exception')
except ConnectionError:
    print('ConnectionError Exception')
except Timeout:
    print('Timeout Exception')
except RequestException:
    print('RequestException')

  • 在上述示例代码中,我们使用try-except语句来捕获可能出现的异常。
    • 首先,“import requests”导入了requests模块
    • 然后通过“from requests.exceptions import *”导入了所有异常类型
    • 接下来,我们使用requests.get函数向百度发送一个请求,设置超时时间为0.00001秒
    • 在try块内,我们捕获了可能出现的异常,根据不同的异常类型进行相应的处理。
  • ReadTimeout:

    • 当请求超时时触发该异常。
  • ConnectionError:

    • 当请求的连接失败时触发该异常,可能是网络不通或DNS解析失败等情况。
  • Timeout:

    • 当请求超时时触发该异常,包括连接超时和读取超时。
  • RequestException:

    • 当请求发生其他异常时触发该异常,属于通用的请求异常类型。
  • 根据具体需求,可以根据实际情况捕获和处理不同的异常类型。
  • 在except块内,可以编写特定的处理逻辑,比如打印错误信息、记录日志或进行其他的操作。
  • 需要注意的是,异常处理应该根据实际情况进行细致的处理,以便及时处理潜在问题并避免程序异常退出。

【7】上传文件

  • 在使用requests模块进行文件上传时,可以通过向post请求中添加files参数来实现。
  • files参数是一个字典,其中键为表单中的字段名(比如中的name),值为打开的文件对象。
  • 以下是关于使用requests模块上传文件的示例代码:
import requests

files = {'file': open('a.jpg', 'rb')}
response = requests.post('http://httpbin.org/post', files=files)
print(response.status_code)

  • 在上述示例代码中,我们首先导入了requests模块。
    • 然后,定义了一个files字典,其中键为'file',值为使用open函数打开的文件对象。
    • 这里假设要上传的文件名为'a.jpg',使用二进制读取模式打开文件('rb')。
  • 接下来,使用requests.post函数发送post请求,传递了files参数,该参数指定了要上传的文件。
    • 这里的URL使用了一个测试服务器'http://httpbin.org/post',你可以将其替换为你要上传文件的目标服务器URL。
  • 最后,打印response的status_code属性,即可获取请求的响应状态码。
  • 需要注意的是,根据实际情况,可能需要添加其他参数来完成文件上传,比如身份验证、请求头部信息等。
  • 具体的参数设置可以根据目标服务器的要求进行调整。
  • 另外,还需要注意文件路径的正确性,确保上传的文件存在,并且有合适的权限进行读取。

【补充】bytes格式

  • 在计算机编程中,"bytes" 是一种数据类型,用于表示以字节为单位的数据序列。
  • Bytes 类型是不可变的,意味着一旦创建了 bytes 对象,就不能修改它。

【1】创建 bytes 对象:

  • 使用字面量表达式:可以使用 b 前缀来创建 bytes 对象。例如,b'Hello' 创建了包含 ASCII 编码的字符串 "Hello" 的 bytes 对象。
  • 使用 bytes() 构造函数:可以将字符串或整数序列转换为 bytes 对象。例如,bytes('Hello', 'utf-8') 将字符串 "Hello" 转换为 UTF-8 编码的 bytes 对象。

【2】访问和操作 bytes 对象:

  • 索引和切片:可以通过索引和切片操作访问 bytes 对象中的单个字节或字节序列。例如,my_bytes[0] 返回第一个字节的整数值,my_bytes[2:5] 返回从第三个到第五个字节的切片。
  • len() 函数:可以使用 len() 函数获取 bytes 对象的长度(字节数)。

【3】字节与字符串之间的转换:

  • 编码:可以使用 bytes 对象的 encode() 方法将其转换为字符串。需要指定字符编码类型。例如,my_bytes.decode('utf-8') 将使用 UTF-8 编码将 bytes 对象转换为字符串。
  • 解码:可以使用字符串的 encode() 方法将其转换为 bytes 对象。同样需要指定字符编码类型。例如,my_str.encode('utf-8') 将使用 UTF-8 编码将字符串转换为 bytes 对象。

【4】常见操作:

  • 连接:可以使用 + 运算符将两个 bytes 对象连接在一起,生成一个新的 bytes 对象。例如,bytes1 + bytes2 将返回将 bytes1 和 bytes2 连接起来的结果。
  • 替换:由于 bytes 对象是不可变的,不能直接修改它们的值。但是可以使用切片和连接操作创建一个新的 bytes 对象,以替换其中的部分内容。

【5】应用场景:

  • 处理二进制数据:bytes 类型常用于处理二进制文件、网络通信或加密算法中的字节流数据。
  • 字符串加密:将字符串转换为 bytes 对象后,可以对其进行加密操作,以保护敏感信息的传输和存储。
  • 请注意,bytes 对象在不同的编程语言和环境中可能有不同的表示方式和操作方法。
  • 因此,在不同的上下文中,bytes 可能具有稍微不同的行为和特性。

【补充】HTTP和HTTPS的区别

  • HTTP和HTTPS是两种常用的协议,用于在客户端和服务器之间传输数据。它们之间的主要区别在于安全性和加密方式。

【1】安全性:

  • HTTP(Hypertext Transfer Protocol)是一种不安全的协议,数据在网络传输过程中是明文传输的,容易被第三方窃取或篡改。
  • HTTPS(Hypertext Transfer Protocol Secure)通过使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议进行加密,确保传输的数据在网络上传输期间得到了安全保护。HTTPS提供了端到端的加密,可以防止数据的窃听和篡改。

【2】加密方式:

  • HTTP使用明文传输数据,不进行加密。
  • HTTPS使用SSL或TLS协议对通信进行加密,确保数据的机密性。这意味着只有发送方和接收方才能了解通信内容,其他人无法解读或篡改数据。

【3】端口号:

  • HTTP默认使用端口号80进行通信。
  • HTTPS默认使用端口号443进行通信。

【4】证书:

  • HTTPS需要使用数字证书来验证服务器的身份。数字证书由可信任的第三方机构颁发,用于证明服务器的真实性和完整性。通过验证证书,客户端可以确定自己正在与合法的服务器建立连接。

总结起来,HTTPS相比HTTP提供了更高的安全性和数据传输的保密性。在涉及敏感信息(例如个人信息、密码、信用卡号等)的通信过程中,建议使用HTTPS来保护数据的安全。[DONE]

【补充】代理池搭建

【1】引入

  • 代理池是一种用于获取可用代理服务器的工具,可以帮助用户在发送请求时隐藏真实IP地址并提高访问稳定性。
  • 开源的代理池核心原理:https://github.com/jhao104/proxy_pool
    • 1 使用爬虫技术,爬取网上免费的代理
    • 2 爬完回来做验证,如果能用,存到redis中
  • 启动调度程序,爬代理,验证,存到redis中
    • python proxyPool.py schedule

步骤1:选择合适的开源软件

  • 首先,我们需要选择一款适合搭建代理池的开源软件。
  • 目前市面上有很多可供选择的软件,比如Scrapy、ProxyPool等。
  • 根据自己的需求和技术能力,选择一款稳定可靠的软件进行搭建。

步骤2:安装依赖和配置环境

  • 根据所选软件的官方文档,安装相关依赖并配置环境。
  • 通常情况下,您需要安装Python以及相应的库和模块。

步骤3:编写代理池程序

根据软件的要求,编写代理池程序。程序主要包括以下几个部分:

  • 代理获取:从免费代理网站或付费代理提供商获取代理IP,并进行有效性验证。
  • 代理存储:将有效的代理IP存储在数据库或其他存储介质中,以供后续使用。
  • 代理检测与维护:定时检测已存储的代理IP的有效性,并根据需要进行维护,如删除无效的代理IP。

步骤4:运行代理池程序

  • 确认程序编写完成后,通过命令行或脚本运行代理池程序。
  • 程序会自动获取、存储和维护代理IP。

步骤5:从代理池中随机取出代理发起请求

  • 在需要使用代理的场景下,可以从代理池中随机选择一个可用的代理IP,并将其添加到请求头中。
  • 示例代码如下:
import random
import requests

def get_random_proxy():
    # 从数据库或其他存储介质中获取代理池中的代理IP列表
    proxy_list = ['ip1:port1', 'ip2:port2', 'ip3:port3', ...]
    
    # 随机选择一个代理IP
    random_proxy = random.choice(proxy_list)
    
    return random_proxy

def send_request(url):
    # 获取随机代理IP
    proxy = get_random_proxy()
    
    # 设置代理参数
    proxies = {
        'http': 'http://' + proxy,
        'https': 'https://' + proxy
    }
    
    try:
        # 发起请求
        response = requests.get(url, proxies=proxies, timeout=10)
        
        # 处理响应数据
        # ...
        
    except Exception as e:
        # 请求异常处理
        # ...

# 示例调用
url = "http://example.com"
send_request(url)
  • 通过以上步骤,您就可以搭建一个代理池,并从中随机选择代理IP发送请求。
  • 记得定期更新和维护代理池,以确保代理IP的有效性和可用性。

【2】拉取第三方镜像

  • pycharm
    • Git
    • Clone
    • Git from version control

https://github.com/jhao104/proxy_pool

【3】搭建虚拟环境

mkvirtualenv -p python39 proxypool

【4】安装依赖

pip install -r requirements.txt

【5】更新配置

# setting.py 为项目配置文件

# 配置API服务

HOST = "0.0.0.0"               # IP
PORT = 5000                    # 监听端口


# 配置数据库
# Redis数据库
DB_CONN = 'redis://127.0.0.1:8888/2'


# 配置 ProxyFetcher

PROXY_FETCHER = [
    "freeProxy01",      # 这里是启用的代理抓取方法名,所有fetch方法位于fetcher/proxyFetcher.py
    "freeProxy02",
    # ....
]

# 代理验证目标网站
HTTP_URL = "http://httpbin.org"

HTTPS_URL = "https://www.qq.com"

【6】启动项目

  • 如果已经具备运行条件, 可用通过proxyPool.py启动。
  • 程序分为: schedule 调度程序 和 server Api服务

(1)启动调度程序

  • 启动调度程序,爬代理,验证,存到redis中
python proxyPool.py schedule

(2)启动webApi服务

  • 使用flask启动服务,对外开放了几个接口,向某个接口发请求,就能随机获取一个代理
python proxyPool.py server

【7】使用

(1)Api

api method Description params
/ GET api介绍 None
/get GET 随机获取一个代理 可选参数: ?type=https 过滤支持https的代理
/pop GET 获取并删除一个代理 可选参数: ?type=https 过滤支持https的代理
/all GET 获取所有代理 可选参数: ?type=https 过滤支持https的代理
/count GET 查看代理数量 None
/delete GET 删除代理 ?proxy=host:ip

(2)爬虫使用

  • 如果要在爬虫代码中使用的话, 可以将此api封装成函数直接使用,例如
import requests

def get_proxy():
    return requests.get("http://127.0.0.1:5010/get/").json()

def delete_proxy(proxy):
    requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy))

# your spider code

def getHtml():
    # ....
    retry_count = 5
    proxy = get_proxy().get("proxy")
    while retry_count > 0:
        try:
            html = requests.get('http://www.example.com', proxies={"http": "http://{}".format(proxy)})
            # 使用代理访问
            return html
        except Exception:
            retry_count -= 1
    # 删除代理池中代理
    delete_proxy(proxy)
    return None
  • 简单示例
import requests
res = requests.get('http://192.168.1.252:5010/get/?type=http').json()['proxy']
proxies = {
    'http': res,
}
print(proxies)
# 我们是http 要使用http的代理
respone = requests.get('http://139.155.203.196:8080/', proxies=proxies)
print(respone.text)

【拓展】requests-html库

  • 作者致力于封装更好用的代码
    • 使用requests封装了python内置模块urllib
  • 经过重构又封装了 requests+lxml
    • requests+lxml 爬取 + 解析 合二为一
  • requests-html库是一个Python实现的网页解析库,它使用了Requests和BeautifulSoup库的组合,为用户提供了简单而强大的网页解析功能。

【1】安装:

  • 可以使用pip命令安装requests-html库,如下所示:

    pip install requests-html
    

【2】导入:

  • 导入requests-html库,并创建HTMLSession对象,如下所示:

    from requests_html import HTMLSession
    
    session = HTMLSession()
    

【3】发送请求:

  • 使用session对象发送HTTP请求,获取网页内容,如下所示:

    response = session.get('http://example.com')
    

【4】解析网页:

  • 可以使用.html属性将网页内容解析为BeautifulSoup对象,如下所示:

    soup = response.html
    

【5】查找元素:

  • 可以使用CSS选择器或XPath表达式在网页中查找元素,如下所示:

    # 使用CSS选择器
    elements = soup.find('div.classname')
    
    # 使用XPath表达式
    elements = soup.xpath('//div[@class="classname"]')
    

【6】提取数据:

  • 可以通过元素对象的属性或方法提取相应的数据,如下所示:

    # 提取文本内容
    text = element.text
    
    # 提取属性值
    attribute = element.attrs['attribute_name']
    

【7】案例及注解:

  • 下面是一个简单的示例,演示如何使用requests-html库发送请求、解析网页,并提取其中的数据:
from requests_html import HTMLSession

# 创建HTMLSession对象
session = HTMLSession()

# 发送请求,获取网页内容
response = session.get('http://example.com')

# 解析网页
soup = response.html

# 查找元素并提取数据
title_element = soup.find('h1')[0]  # 使用CSS选择器查找标题元素
title = title_element.text  # 提取标题文本内容

paragraph_elements = soup.find('p')  # 使用CSS选择器查找段落元素列表
paragraphs = [p.text for p in paragraph_elements]  # 提取段落文本内容列表

print(title)
print(paragraphs)

注解:

  • 通过HTMLSession创建一个会话对象,可以发送HTTP请求和获取网页内容。
  • 使用.get()方法发送GET请求,并传递网页URL作为参数,返回一个响应对象。
  • 响应对象的.html属性将网页文本解析为BeautifulSoup对象,可以通过.find().xpath()方法查找需要的元素。
  • 通过元素对象的.text属性可以获取元素的文本内容,通过.attrs属性可以获取元素的属性字典。
  • 请注意,此示例只是requests-html库的简单应用。
  • 该库还提供了许多其他功能,例如JavaScript渲染、表单提交、页面跳转等。
  • 您可以参考官方文档以深入了解其更多用法和功能
posted @ 2023-08-22 09:08  Chimengmeng  阅读(122)  评论(0编辑  收藏  举报