网络请求是爬虫工程师采集数据的重要手段之一。在PC端爬虫中,网络请求通常使用HTTP协议进行通信,通过发送HTTP请求获取目标网站的数据。
爬虫工程师需要掌握HTTP协议的基本知识,包括HTTP请求和响应的格式、常见的HTTP请求方法(如GET、POST等)、HTTP请求头和响应头的常见字段等。
在进行网络请求时,爬虫工程师通常使用HTTP客户端库,如Python中的requests库、Java中的HttpClient等。这些库封装了HTTP协议的细节,提供了简单易用的API,方便爬虫工程师进行网络请求。
爬虫工程师还需要了解一些反爬虫技术,如User-Agent伪装、IP代理等,以应对目标网站的反爬虫策略。
一丶requests
1.requests源码解析
对于爬虫工程师来说,网络请求是常用的数据采集方式之一。而Python的requests库,作为一个高效且易用的HTTP请求库,被爬虫工程师广泛使用。在深入学习requests库前,建议先了解下其中的源码实现。
requests库是基于urllib3库封装的,所以在使用requests库时需要先安装对应的依赖库urllib3。
接下来,我们通过分析requests库的源代码,来了解其中的一些实现细节。
首先是发送请求的实现,即requests库中的Request类。Request类用于封装发送请求的参数,并通过一个Session对象发送请求并返回响应。以下是Request类的核心代码:
class Request:
@staticmethod
def send(session, method, url, **kwargs):
# ...
resp = session.request(method=method, url=url, **kwargs)
return resp
我们可以看到,Request类中的send方法调用了Session对象的request方法,这个方法是整个库中负责发送请求和返回响应的核心方法。以下是Session类中request方法的核心代码:
class Session:
def request(self, method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None,
timeout=None, allow_redirects=True, proxies=None, hooks=None, stream=None, verify=None, cert=None,
json=None):
# ...
return self.send(prep, **send_kwargs)
我们可以看到,Session对象的request方法的参数和关键字参数与HTTP请求的相关部分一一对应,其中最重要的是prep
参数(即经过预处理的Request
对象),它包含了请求的相关信息,如请求方法,请求头,请求体等。Session对象的request方法最终调用了self.send
方法,即发送HTTP请求并返回响应。
requests库实现了带有各种HTTP请求方法的函数接口,如requests.get()
、requests.post()
等,这些接口在内部会自动创建一个Session对象,然后调用Session对象的request方法,从而返回请求响应。
总体来说,requests是一个功能强大的HTTP请求库,它的源代码实现清晰、易于阅读和理解,掌握其中的实现细节可以帮助我们更好的使用这个库。
2.requests常用方法
requests是一个Python第三方库,用于发送HTTP请求。以下是requests常用方法:
requests.get(url, params=None, **kwargs)
:发送GET请求,url为请求的URL地址,params
为请求参数,kwargs
为其他可选参数。requests.post(url, data=None, json=None, **kwargs)
:发送POST请求,url
为请求的URL地址,data
为请求数据,json
为请求的JSON
数据,**kwargs
为其他可选参数。requests.put(url, data=None, **kwargs)
:发送PUT
请求,url
为请求的URL地址,data
为请求数据,**kwargs
为其他可选参数。requests.delete(url, **kwargs)
:发送DELETE
请求,url
为请求的URL
地址,**kwargs
为其他可选参数。requests.head(url, **kwargs)
:发送HEAD
请求,url
为请求的URL地址,**kwargs
为其他可选参数。requests.options(url, **kwargs)
:发送OPTIONS
请求,url
为请求的URL
地址,**kwargs
为其他可选参数。requests.request(method, url, **kwargs)
:发送自定义请求,method
为请求方法,url
为请求的URL
地址,**kwargs
为其他可选参数。requests.session()
:创建一个Session
对象,用于保持会话状态。requests.get(url, headers=headers)
:发送GET请求,并设置请求头。requests.get(url, cookies=cookies)
:发送GET请求,并设置请求的Cookies
。requests.get(url, proxies=proxies)
:发送GET请求,并设置代理服务器。requests.get(url, timeout=timeout)
:发送GET请求,并设置超时时间。requests.get(url, verify=verify)
:发送GET请求,并设置SSL证书验证。requests.get(url, allow_redirects=allow_redirects)
:发送GET请求,并设置是否允许重定向。requests.get(url, stream=stream)
:发送GET请求,并设置是否使用流式传输
3.data/json/param参数传递
在使用requests库发送网络请求时,我们可以通过传递不同的参数来实现不同的请求方式和数据传递方式。常用的参数包括data、json和params。
1.data参数
data参数用于传递表单数据,通常用于POST
请求。它可以是一个字典,也可以是一个字符串。如果是字典,requests
会自动将其转换为表单形式;如果是字符串,则需要手动指定Content-Type
为application/x-www-form-urlencoded
。
示例代码:
import requests
data = {
'username': 'admin',
'password': '123456'
}
response = requests.post('http://www.example.com/login', data=data)
2.json参数
json
参数用于传递JSON
格式的数据,通常用于POST
请求。它可以是一个字典,也可以是一个字符串。如果是字典,requests
会自动将其转换为JSON
格式;如果是字符串,则需要手动指定Content-Type
为application/json
。
示例代码:
import requests
data = {
'username': 'admin',
'password': '123456'
}
response = requests.post('http://www.example.com/login', json=data)
3.params参数
params
参数用于传递URL
参数,通常用于GET
请求。它可以是一个字典,也可以是一个字符串。如果是字典,requests
会自动将其转换为URL
参数;如果是字符串,则需要手动拼接URL
。
示例代码:
import requests
params = {
'page': 1,
'size': 10
}
response = requests.get('http://www.example.com/articles', params=params)
4.隧道代理使用
隧道代理是一种通过隧道连接到代理服务器的方式来进行网络请求的方法。这种方式可以帮助我们隐藏真实的IP地址,提高爬虫的稳定性和安全性。
使用隧道代理需要先购买代理服务,然后在代码中设置代理服务器的IP地址和端口号。以下是一个使用隧道代理的示例代码:
import requests
proxy = {
'http': 'http://代理服务器IP地址:端口号',
'https': 'https://代理服务器IP地址:端口号'
}
url = 'https://www.example.com'
response = requests.get(url, proxies=proxy)
print(response.text)
在上面的代码中,我们首先定义了一个代理字典,包含了http和https两种协议的代理服务器地址和端口号。然后使用requests
库的get
方法发送请求时,将代理字典作为proxies
参数传入即可。
需要注意的是,使用隧道代理可能会降低请求速度,而且代理服务的质量也会影响到爬虫的效果。因此,在选择代理服务时需要谨慎,建议选择稳定可靠的服务商。
5.证书异常处理
在进行网络请求时,有些网站会进行证书认证以确保数据的安全。如果requests库在进行SSL证书验证时遇到了问题,会抛出“证书验证异常(Certificate Verification Error)”的异常。这个异常通常是由于请求响应的SSL证书无效或不受信任导致的。
以下是requests库中处理证书异常的方法:
1.忽略证书验证
在使用requests库进行网络请求时,可以通过设置verify
参数为False
来忽略SSL证书验证。这个方法会禁止requests库对证书进行验证,而是采用不安全的方式进行通信,因此在进行敏感操作时应慎重使用。
例如:
response = requests.get('https://example.com', verify=False)
2.设置证书文件
可以通过设置cert
参数来指定一个证书文件,在请求时使用该证书进行验证。这个方法需要事先获得一个有效的证书文件,如果无法提供有效证书则无法进行安全通信。
例如:
response = requests.get('https://example.com', cert=('path/to/cert.crt', 'path/to/key'))
3.添加自定义证书
可以通过requests库提供的certifi
库在运行时初始化一个自定义证书,从而进行证书验证。这种方式需要提供证书的SHA256指纹值,并将其添加到requests库已有的证书列表中。
例如:
import certifi
cert = certifi.where()
fingerprints = {'example.com': 'A1:B2:C3:...', ...}
with open(cert, 'a') as f:
for host, fingerprint in fingerprints.items():
f.write(f'{host} {fingerprint}\n')
response = requests.get('https://example.com', verify=True)
在以上代码中,certifi.where()
用于获取当前Python环境中的证书路径,然后将每个需要验证的主机和其证书的SHA256指纹添加到证书文件中。
综上,要避免证书异常需要注意常见的安全规则,如设置SSL证书验证、使用CA颁发的证书、对外不开放不安全的通信端口等。需要快速扫描设备,确保组件升级到最新版本,在安全上下文中测试企业所依赖的所有服务并采用有力的加密技术以支持加密通信。
二丶httpx
1.httpx源码解析
httpx是一个Python异步HTTP客户端库,它提供了简单易用的API,支持异步和同步请求,支持HTTP/1.1和HTTP/2协议,支持代理、SSL/TLS、Cookie等功能。下面我们来看一下httpx的源码解析。
httpx的核心代码在client.py文件中,其中最重要的是Client类。Client类是httpx的主要接口,它提供了发送HTTP请求的方法,如get、post、put、delete等。下面是Client类的定义:
class Client:
def __init__(
self,
timeout=UNSET,
follow_redirects=UNSET,
max_redirects=UNSET,
verify=UNSET,
cert=UNSET,
trust_env=UNSET,
http2=UNSET,
backend=UNSET,
default_headers=UNSET,
base_url=UNSET,
app=UNSET,
auth=UNSET,
cookies=UNSET,
allow_redirects=UNSET,
proxies=UNSET,
dispatch=UNSET,
limits=UNSET,
pool_limits=UNSET,
retry=UNSET,
trust_env_proxies=UNSET,
headers=UNSET,
**extra_options,
):
...
Client类的构造函数接受很多参数,这些参数可以用来配置httpx的行为。其中比较重要的参数包括:
- timeout:请求超时时间。
- follow_redirects:是否自动跟随重定向。
- max_redirects:最大重定向次数。
- verify:是否验证SSL证书。
- cert:客户端证书。
- trust_env:是否信任环境变量。
- http2:是否启用HTTP/2协议。
- backend:HTTP客户端后端。
- default_headers:默认请求头。
- base_url:基础URL。
- app:ASGI应用程序。
- auth:HTTP认证。
- cookies:请求Cookie。
- allow_redirects:是否允许重定向。
- proxies:代理服务器。
- dispatch:请求分发器。
- limits:请求限制。
- pool_limits:连接池限制。
- retry:请求重试。
- trust_env_proxies:是否信任环境变量中的代理服务器。
- headers:请求头。
Client类的方法包括:
- request:发送HTTP请求。
- get:发送GET请求。
- post:发送POST请求。
- put:发送PUT请求。
- delete:发送DELETE请求。
- head:发送HEAD请求。
- options:发送OPTIONS请求。
- patch:发送PATCH请求。
这些方法都是基于request方法实现的,只是参数不同。下面是request方法的定义:
async def request(
self,
method,
url,
*,
params=UNSET,
data=UNSET,
json=UNSET,
headers=UNSET,
cookies=UNSET,
files=UNSET,
auth=UNSET,
timeout=UNSET,
allow_redirects=UNSET,
cert=UNSET,
verify=UNSET,
stream=UNSET,
trust_env=UNSET,
max_redirects=UNSET,
http2=UNSET,
backend=UNSET,
dispatch=UNSET,
limits=UNSET,
pool_limits=UNSET,
retry=UNSET,
trust_env_proxies=UNSET,
**options,
):
...
request方法接受很多参数,包括HTTP请求方法、URL、请求参数、请求体、请求头、请求Cookie、文件、HTTP认证、请求超时时间、是否允许重定向、客户端证书、是否验证SSL证书、是否使用流式传输、是否信任环境变量、最大重定向次数、是否启用HTTP/2协议、HTTP客户端后端、请求分发器、请求限制、连接池限制、请求重试、是否信任环境变量中的代理服务器等。
httpx的源码比较清晰,代码结构清晰,注释详细,易于阅读和理解。如果你想深入了解httpx的实现原理,可以阅读httpx的源码。
2.httpx常用方法
httpx是一个Python的异步HTTP客户端库,它提供了许多常用的方法来发送HTTP请求和处理响应。以下是httpx常用的方法:
get(url, params=None, **kwargs)
: 发送GET请求,url为请求的URL,params为请求参数,kwargs为其他可选参数,如headers、timeout等。post(url, data=None, json=None, **kwargs)
: 发送POST请求,url为请求的URL,data为请求数据,json为请求的JSON数据,kwargs为其他可选参数,如headers、timeout等。put(url, data=None, **kwargs)
: 发送PUT请求,url为请求的URL,data为请求数据,kwargs为其他可选参数,如headers、timeout等。delete(url, **kwargs)
: 发送DELETE请求,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。head(url, **kwargs)
: 发送HEAD请求,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。options(url, **kwargs)
: 发送OPTIONS请求,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。request(method, url, **kwargs)
: 发送自定义请求,method为请求方法,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。close()
: 关闭httpx客户端。request_stream(method, url, **kwargs)
: 发送流式请求,method为请求方法,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。request_raw(method, url, **kwargs)
: 发送原始请求,method为请求方法,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。request_bytes(method, url, **kwargs)
: 发送字节请求,method为请求方法,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。request_json(method, url, **kwargs)
: 发送JSON请求,method为请求方法,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。request_text(method, url, **kwargs)
: 发送文本请求,method为请求方法,url为请求的URL,kwargs为其他可选参数,如headers、timeout等。request_files(method, url, files=None, **kwargs)
: 发送文件请求,method为请求方法,url为请求的URL,files为上传的文件,kwargs为其他可选参数,如headers、timeout等。request_multipart(method, url, data=None, files=None, **kwargs)
: 发送多部分请求,method为请求方法,url为请求的URL,data为请求数据,files为上传的文件,kwargs为其他可选参数,如headers、timeout等。
3.httpx上下文处理
在httpx中,上下文处理是指在一个请求中,将一些共同的参数或配置信息保存在一个上下文对象中,以便在后续的请求中使用。这样可以避免在每个请求中都重复设置相同的参数或配置信息,提高代码的可读性和可维护性。
httpx中的上下文对象是一个字典,可以通过创建一个httpx.Context对象来获取。在创建Context对象时,可以传入一些默认的参数或配置信息,这些信息会被保存在Context对象中,以便在后续的请求中使用。
下面是一些常用的httpx上下文处理方法:
1.创建Context对象
import httpx
context = httpx.Context()
2.设置默认的请求头
context.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
})
3.设置代理
context.proxies = {
'http': 'http://127.0.0.1:8888',
'https': 'http://127.0.0.1:8888'
}
4.设置超时时间
context.timeout = httpx.Timeout(10.0, read=20.0)
5.设置SSL验证
context.verify = False
6.设置cookie
context.cookies['name'] = 'value'
7.设置认证信息
context.auth = httpx.BasicAuth('username', 'password')
8.设置重试次数
context.retry = httpx.Retry(total=3, backoff_factor=0.3)
9.设置连接池
context.http2 = True
context.max_keepalive_connections = 100
context.max_connections = 100
10.在请求中使用Context对象
import httpx
context = httpx.Context()
context.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
})
with httpx.Client(context=context) as client:
response = client.get('https://www.example.com')
4.httpx异步请求
在进行网络请求时,有时候需要进行异步请求,以提高效率和性能。httpx
是一个支持异步请求的Python HTTP客户端库,可以使用async/await
语法进行异步请求。
下面是一个使用httpx进行异步请求的示例:
import httpx
import asyncio
async def fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
async def main():
urls = [
'https://www.baidu.com',
'https://www.google.com',
'https://www.bing.com'
]
tasks = [asyncio.create_task(fetch(url)) for url in urls]
results = await asyncio.gather(*tasks)
print(results)
if __name__ == '__main__':
asyncio.run(main())
在上面的示例中,我们定义了一个fetch函数,用于异步请求指定的URL,并返回响应内容。然后在main函数中,我们定义了三个URL,使用asyncio.create_task
创建了三个异步任务,并使用asyncio.gather
等待所有任务完成,并打印结果。
需要注意的是,在使用httpx
进行异步请求时,需要使用AsyncClient
类,而不是普通的Client类。此外,需要使用async/await
语法进行异步请求。