Python入门学习三

一 异步编程

1. 协程

协程不是计算机提供的,是人员创造的;也可以称为微线程,是一种用户态上下文切换技术。简而言之,就是通过一个县程序实现代码相互切换执行。人为控制在一个函数之间来回切换

python 复制代码
def func1():
    print(1)
    ...
    print(2)

def func2():
    print(3)
    ...
    pirnt(4)
   

func1()
func2()

实现协程有以下几种方式

  • greenlet,早期模块
  • yield关键字
  • asyncio装饰器(py3.4)
  • async, await关键字(py3.5)

2.协程的实现方式

2.1 greenlet

python 复制代码
pip/pip3 install greenlet

实现

python 复制代码
form greenlet import greenlet

def func1():
    print(1)
    gr2.switch()  #切换到func2函数
    print(2)
    gr2.switch()  #切换到func2函数,从上一次执行位置继续向后执行

def func2():
    print(3)
    gr1.switch() #切换到func1函数,从上一次执行位置继续向后执行
    print(4)


gr1 = greenlet(func1)
gr2 = greenlet(func2)

gr1.switch()  #执行func1函数

#输出顺序 -》 1,3,2,4

2.2 yield 关键字

python 复制代码
def func1():
    yield 1
    yield from func2()
    yield 2

def func2():
    yield 3
    yield 4

f1 = func1()

for item in f1:
    print(item)

2.3 asyncio

python3.4 之后

python 复制代码
@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2) #遇到IO耗时操作,自动化切换到tasks的其他任务
    print(2)

@asyncio.coroutine
def func2():
    print(3)
    yield from asyncio.sleep(2) #遇到IO耗时操作,自动化切换到tasks的其他任务
    print(4)

tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2()),
]

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

#打印 出来也是1324

和前端js的宏任务,微任务一样,有点放在队列的意思

2.4 async await

python 3.5 及以后版本

python 复制代码
async def func1():
    print(1)
    await asyncio.sleep(2)  # IO耗时操作时,自动切换到其他任务
    print(2)


async def func2():
    print(3)
    await asyncio.sleep(2)  # IO耗时操作时,自动切换到其他任务
    print(4)


# 主协程:所有异步逻辑的入口(必须用 async def 定义)
async def main():
    # 1. 用 create_task() 替代 ensure_future()(3.13 推荐创建任务的方式)
    task1 = asyncio.create_task(func1())
    task2 = asyncio.create_task(func2())

    # 2. 等待所有任务完成(两种方式任选,效果一致)
    # 方式一:逐个 await(适合任务数量少的情况)
    await task1
    await task2

    # 方式二:用 gather() 批量等待(适合任务多的情况,更简洁)
    # await asyncio.gather(task1, task2)


if __name__ == "__main__":
    # 3. 用 asyncio.run() 启动主协程(3.13 唯一推荐的启动方式)
    # 自动创建/管理/关闭事件循环,无需手动操作 loop
    asyncio.run(main())

3. 协程的意义

在一个线程中,如果遇到了io等待的时间,线程不会等待,会利用空闲的时候再去干点其他的事情

案例: 下载图片

3.1 传统方式(同步)

python 复制代码
pip/pip3 install requests
python 复制代码
import requests
def dowload_images(url):
    print("开始下载", url)
    response = requests.get(url)
    print("下载完成")
    print(response)
    file_name = url.split("/")[-1]

    # 以2进制的方式写入文件,直接下载到当前文件夹
    with open(file_name, mode="wb") as file_object:
        file_object.write(response.content)

url_list = [
    'https://car2.autoimg.cn/cardfs/series/g31/M07/74/F8/400x300_autohomecar__ChxoHWYU1RCAPDx5AAjYvgS1ZC8852.png.png',
    'https://car3.autoimg.cn/cardfs/series/g33/M06/AD/95/400x300_autohomecar__ChxpVWkRnliACHALAC3azVUgZSs382.png.png'
]

for item in url_list:
    dowload_images(item)

# 按照写法,假设一张图片需要下载2分钟,那么2张图就是4分钟

按照协程的写法(异步)

python 复制代码
#aiohttp -Python 异步 HTTP 请求库,专门用于在异步程序中发送 HTTP/HTTPS 请求(对应同步的 requests 库);
# 核心用途:支持异步请求,不会因为等待一个请求的响应而阻塞其他任务(这是并发下载的关键
import aiohttp

#导入 Python 内置的异步编程核心库 ------ 负责管理事件循环、创建任务、调度协程等,是所有 Python 异步
#程序的 "发动机"。
import asyncio

# 返回一个协程对象
async def fetch(session, url):
        print("发送请求", url)
        async with session.get(url, verify_ssl=False) as response:
            print("请求成功")
            content = await response.content.read()
            file_name = url.split('_')[-1]
            with open(file_name, 'wb') as f:
                print("写入开始")
                f.write(content)
                print("写入成功")

async def main():
    async  with aiohttp.ClientSession() as session:
        url_list = [
            'https://car2.autoimg.cn/cardfs/series/g31/M07/74/F8/400x300_autohomecar__ChxoHWYU1RCAPDx5AAjYvgS1ZC8852.png.png',
            'https://car3.autoimg.cn/cardfs/series/g33/M06/AD/95/400x300_autohomecar__ChxpVWkRnliACHALAC3azVUgZSs382.png.png'
        ]
        tasks = [ asyncio.create_task(fetch(session, url)) for url in url_list]
        await  asyncio.wait(tasks)

if __name__ == '__main__':
    asyncio.run(main())

#可以看到打印,请求是一起发送的

4. 异步编程

4.1 事件循环

检测并执行某些代码

4.2 快速上手

协程函数: 定义函数的时候 async def 函数名

协程对象:执行协程函数得到的对象

python 复制代码
async def func():
    pass

result = func()

注意:创建协程函数创建协程对象,函数内部不会执行

python 复制代码
improt asyncio

async def func():
    pirnt("开始执行了")

result = func()

#获取事件循环,执行事件循环
# loop = asyncio.get_event_loop()
# loop.run_until_complete(result )

asyncio.run( result ) #pyhon3.7 后写法

4.3 await

await + 可等待对象(协程对象,Future, Task对象 -> IO等待)

python 复制代码
import asyncio 

async def func():
    print("执行1")
    respones = await asyncio.sleep(2)
    print("结束")

asyncio.run( func() )
python 复制代码
import asyncio

async def others():
    print("others1")
    await asyncio.sleep(2)
    print("others2")
    return "返回值"

async def func():
    print("我是func1-1")
    r = await others()
    print(r)

    r1 = await others()
    print(r1)
    print("我是func1-2")

asyncio.run(func())

4.4 Task对象

帮助在事件循环中添加多个任务

python 复制代码
import asyncio

async def func():
    print(1)
    await asyncio.sleep(1)
    print(2)
    return "返回值"

async def main():
    print("main开始")

    #创建task对象,将当前执行函数添加到事件循环中
    # task1 = asyncio.create_task(func())
    # task2 = asyncio.create_task(func())

    # ret1 = await task1
    # ret2 = await task2

    # name,加入队列的名称
    task_list = [
        asyncio.create_task(func(), name='task1'),
        asyncio.create_task(func(), name='task2')
    ]

    #timeout 是否延迟执行, 返回两种状态,done中是执行完成后的结果列表
    done ,pendding= await asyncio.wait(task_list, timeout=None)
    print(done)


# 创建事件循环队列,并将main添加在里面
asyncio.run(main())

4.5 asyncio.Future 对象

_state 维护了一个状态

Task继承了Future,Task内部await结果的处理基于Future对象来的

python 复制代码
import asyncio
async def main():
    # 获取到当前事件循环对象
    loop = asyncio.get_event_loop()
    # 创建一个任务(Future对象) 这个任务什么都不干
    fut = loop.create_future()

    await fut
asyncio.run(main())
python 复制代码
import asyncio

async def func(fut):
    print(1)
    await asyncio.sleep(1)
    fut.set_result(66)

async def main():
    # 获取到当前事件循环对象
    loop = asyncio.get_event_loop()
    # 创建一个任务(Future对象) 这个任务什么都不干
    fut = loop.create_future()
    #创建一个任务(task对象),绑定了func函数在1s后,会给fut赋值
    #即手动设置了future任务的最终结果
    await loop.create_task(func(fut))

    r = await fut
    print(r)

asyncio.run(main())

4.6 concurrent的Future对象

使用线程池,进程池实现异步操作时用到的对象

4.7 异步可迭代对象

可在async for 语句中被使用的对象。

创建一个异步迭代对象,循环生成 1~99 的数字,直到生成第 100 次时停止迭代

python 复制代码
import asyncio

class Reader(object):

    def __init__(self):
        self.count = 0
    
    async def readline(self):  
        self.count += 1
        if self.count == 100:
            return None
        return self.count

    def __aiter__(self):
        return self
    
    async def __anext__(self):
        val = await self.readline() 
        if val is None:  
            raise StopAsyncIteration 
        return val

async def func():
    obj = Reader()  
    async for item in obj:  
        print(item)

asyncio.run(func())  

5. 详细讲解 async await

5.1. 定义协程函数:async def

要创建一个 "能异步执行的函数",必须用 async def 开头(不能用普通 def):

python 复制代码
# 这是一个协程函数(注意 async def)
async def my_coroutine():
    print("协程开始执行")
    return "协程执行完成"

注意:协程函数不能直接调用!

如果像普通函数一样调用 my_coroutine(),不会执行函数内部代码,只会返回一个 "协程对象":

python 复制代码
result = my_coroutine()  # 不会打印任何内容
print(type(result))       # 输出:<class 'coroutine'>(是协程对象,不是返回值)

5.2. 运行协程:asyncio.run()

要真正执行协程函数,必须通过 asyncio.run()(Python 3.7+ 内置,最简单的启动方式):

python 复制代码
import asyncio  # 必须导入异步库

async def my_coroutine():
    print("协程开始执行")
    return "协程执行完成"

# 用 asyncio.run() 启动协程(这是异步程序的入口)
final_result = asyncio.run(my_coroutine())
print(final_result)  # 输出:协程执行完成

执行流程:

  1. asyncio.run() 会创建一个 "事件循环"(可以理解为 "任务调度中心",负责安排协程执行);
  2. 事件循环运行 my_coroutine 协程,执行函数内部代码;
  3. 协程执行完后,asyncio.run() 会返回协程的返回值;
  4. 最后关闭事件循环。

5.3. 暂停协程:await

await 是 "等待" 的意思,只能用在 协程函数内部,作用是:

  • 暂停当前协程的执行,去执行其他协程或异步操作;
  • 等被等待的操作完成后,再恢复当前协程的执行。

语法格式:

python 复制代码
async def 协程A():
    # 等待 协程B 执行完成,拿到返回值后再继续
    result = await 协程B()
    print(result)

示例:用 await 等待另一个协程

python 复制代码
import asyncio

# 定义一个需要"耗时"的协程(模拟 IO 操作,比如网络请求)
async def fake_io_operation():
    print("开始执行耗时操作(比如下载文件)")
    await asyncio.sleep(2)  # 模拟 2 秒耗时(注意:必须用 asyncio.sleep,不能用 time.sleep!)
    return "耗时操作完成"

# 主协程
async def main():
    print("主协程开始")
    # 等待 fake_io_operation 完成,拿到返回值
    result = await fake_io_operation()
    print("主协程继续执行,拿到结果:", result)

# 启动主协程
asyncio.run(main())

执行结果(会等待 2 秒):

plaintext

python 复制代码
主协程开始
开始执行耗时操作(比如下载文件)
# 此处暂停 2 秒(期间事件循环可以安排其他任务,但这个例子只有一个任务)
主协程继续执行,拿到结果: 耗时操作完成

关键提醒:

  • await 后面必须跟 "可等待对象" (常见的是:协程对象、asyncio.Taskasyncio.Future 等);
  • 绝对不能在普通 def 函数里用 await
  • 不能用 time.sleep(2) 模拟耗时!time.sleep 是 "同步阻塞",会让整个事件循环卡住;asyncio.sleep 是 "异步非阻塞",会让当前协程暂停,事件循环可以去做其他事。

5.4 案例

5.4.1 为什么要用异步

"为什么要用异步?同步不是挺好吗?" 看这个对比案例就懂了!

场景:模拟 3 个耗时任务(比如下载 3 个文件,每个任务耗时 2 秒)

1. 同步版本(普通 def + time.sleep
python 复制代码
import time

# 同步函数:每个任务耗时 2 秒
def sync_task(name):
    print(f"同步任务 {name} 开始")
    time.sleep(2)  # 同步阻塞,整个程序都要等
    print(f"同步任务 {name} 结束")

# 同步执行3个任务
def sync_main():
    start_time = time.time()
    sync_task("A")
    sync_task("B")
    sync_task("C")
    end_time = time.time()
    print(f"同步总耗时:{end_time - start_time:.2f} 秒")

sync_main()

执行结果(总耗时~6 秒):

复制代码
同步任务 A 开始
同步任务 A 结束
同步任务 B 开始
同步任务 B 结束
同步任务 C 开始
同步任务 C 结束
同步总耗时:6.01 秒

→ 问题:任务 A、B、C 按顺序执行,每个都要等前一个完成,总耗时是 2+2+2=6 秒。

2. 异步版本(async def + await + asyncio.sleep
python 复制代码
import asyncio
import time

# 异步任务:每个任务"耗时"2秒(非阻塞)
async def async_task(name):
    print(f"异步任务 {name} 开始")
    await asyncio.sleep(2)  # 异步暂停,事件循环可以去执行其他任务
    print(f"异步任务 {name} 结束")

# 异步执行3个任务
async def async_main():
    start_time = time.time()
    # 同时启动3个异步任务(关键:不是顺序执行,是并发执行)
    await asyncio.gather(
        async_task("A"),
        async_task("B"),
        async_task("C")
    )
    end_time = time.time()
    print(f"异步总耗时:{end_time - start_time:.2f} 秒")

# 启动异步主协程
asyncio.run(async_main())

执行结果(总耗时~2 秒):

复制代码
异步任务 A 开始
异步任务 B 开始
异步任务 C 开始
异步任务 A 结束
异步任务 B 结束
异步任务 C 结束
异步总耗时:2.01 秒

→ 奇迹:3 个任务 "同时执行",总耗时只有 2 秒!

关键解释:

  • asyncio.gather(task1, task2, task3):让多个协程 "并发执行"(同时启动,不用等前一个开始);
  • async_task(A) 执行到 await asyncio.sleep(2) 时,会暂停自己,事件循环立刻去执行 async_task(B)
  • async_task(B) 暂停后,再去执行 async_task(C)
  • 2 秒后,3 个任务的 sleep 都完成,各自恢复执行,打印 "结束"。

这就是异步的核心价值:在等待某个耗时操作时,不闲着,去处理其他任务,大幅节省总时间

5.4.2 异步获取网页

实际开发中,async/await 常用在 "网络请求""数据库操作" 等耗时 IO 场景。下面用 aiohttp(异步 HTTP 库)模拟批量获取网页内容:

步骤 1:安装依赖

复制代码
pip install aiohttp  # 异步 HTTP 库,支持 await

步骤 2:异步批量请求网页

python 复制代码
import asyncio
import aiohttp  # 异步 HTTP 客户端

# 要请求的网页地址列表
URLS = [
    "https://www.baidu.com",
    "https://www.taobao.com",
    "https://www.jd.com",
    "https://www.zhihu.com"
]

# 异步请求单个网页
async def fetch_url(session, url):
    async with session.get(url) as response:  # 异步发送 GET 请求
        return f"{url}:状态码 {response.status}"  # 返回响应状态码

# 主协程:批量请求所有网页
async def main():
    # 创建一个异步 HTTP 会话(类似 requests.Session,但支持异步)
    async with aiohttp.ClientSession() as session:
        # 创建所有要执行的异步任务
        tasks = [fetch_url(session, url) for url in URLS]
        # 并发执行所有任务,等待全部完成后返回结果列表
        results = await asyncio.gather(*tasks)  # * 是解包列表
    
    # 打印所有结果
    for result in results:
        print(result)

# 启动程序
asyncio.run(main())
执行效果:

4 个网页请求 "同时进行",总耗时≈单个网页的请求时间(比如 1~2 秒);如果用同步的 requests 库,总耗时≈4 个网页请求时间之和(比如 4~8 秒)。

常见误区

  1. 误区 1:用 time.sleep 模拟异步耗时 → 错!time.sleep 是同步阻塞,会让整个事件循环卡住,异步变同步。→ 正确:用 asyncio.sleep()

  2. 误区 2:在普通 def 函数里用 await → 错!await 只能在 async def 定义的协程函数内部使用。→ 正确:如果需要用 await,必须把函数改成 async def

  3. 误区 3:直接调用协程函数就能执行 → 错!async def 函数调用后返回协程对象,不会执行内部代码。→ 正确:用 asyncio.run() 或事件循环(loop.run_until_complete())启动。

  4. 误区 4:异步一定比同步快→ 错!只有在 "有耗时 IO 操作"(网络、文件、数据库)时,异步才快;如果是 CPU 密集型任务(比如计算),异步反而可能更慢(因为事件循环有调度开销)。

总结(核心知识点浓缩)

  1. async def:定义协程函数,返回协程对象(不能直接执行);
  2. asyncio.run(coro):启动协程的入口,自动管理事件循环;
  3. await:暂停当前协程,等待其他异步操作完成后恢复,只能用在协程函数内;
  4. 核心价值:IO 密集型场景下,通过 "并发执行" 减少等待时间,提高程序效率;
  5. 常用工具asyncio.gather()(并发执行多个协程)、aiohttp(异步网络请求)、asyncpg(异步操作 PostgreSQL)等。

二 python三大http请求库

1. requests

1. 基本介绍

requests的官方文档:https://requests.readthedocs.io/en/latest/user/quickstart/#make-a-request

w3c 学习文档:https://www.w3cschool.cn/requests2/requests2-fu433fja.html

Requests 是 Python 最流行的 HTTP 请求库,功能强大且用法简洁,能轻松实现网页爬取、接口调用、数据提交等需求。本教程从环境搭建到实战案例,循序渐进带你掌握核心用法,学完即可上手开发。

2. 安装

python 复制代码
pip|pip3 install requests

3. 核心基础:Requests 核心方法与语法

Requests 最常用的 5 个方法,对应 HTTP 协议的核心请求类型:

方法 作用 场景示例
requests.get() 发送 GET 请求(获取数据) 爬取网页、获取接口数据
requests.post() 发送 POST 请求(提交数据) 登录、表单提交、上传文件
requests.put() 发送 PUT 请求(全量更新数据) 接口更新资源(如修改用户信息)
requests.delete() 发送 DELETE 请求(删除数据) 接口删除资源(如删除订单)
requests.session() 创建会话(保持登录状态) 需登录后才能访问的页面 / 接口
3.1 GET 请求(获取数据)
3.1.1 简单请求
python 复制代码
import requests  # 导入 requests 库

# 1. 发送 GET 请求到目标 URL
response = requests.get("https://www.baidu.com")  # response 是响应对象,包含服务器返回的所有信息

# 2. 查看响应结果
print("响应状态码:", response.status_code)  # 状态码:200 表示成功,4xx 客户端错误,5xx 服务器错误
print("响应内容(前100字符):", response.text[:100])  # response.text:以字符串形式返回响应内容(网页源码)
3.1.2 核心响应对象属性
属性 / 方法 说明 示例输出
response.status_code 响应状态码(判断请求是否成功) 200(成功)、404(未找到)
response.text 以字符串返回响应内容(自动解码) 网页 HTML 源码、接口 JSON 字符串
response.content 以字节流返回响应内容(二进制数据) 图片、文件、未解码的文本
response.json() 自动解析 JSON 格式的响应内容(返回字典) {"name": "张三", "age": 20}
response.headers 响应头(服务器返回的 metadata) {'Content-Type': 'text/html'}
response.url 实际请求的 URL(处理重定向后) https://www.baidu.com/
3.1.3 GET 请求带参数

很多时候需要在 URL 后拼接参数(如 https://search.baidu.com/s?wd=pythonwd=python 是搜索参数),用 params 参数传递更简洁(无需手动拼接 URL):

python 复制代码
import requests

# 定义要传递的参数(字典格式)
params = {
    "wd": "Python Requests 教程",  # 搜索关键词
    "pn": 0  # 分页参数(第 0 页,即第一页)
}

# 发送带参数的 GET 请求(params 参数自动拼接 URL)
response = requests.get(
    url="https://search.baidu.com/s",  # 基础 URL(不含参数)
    params=params  # 传递参数
)

print("实际请求的 URL:", response.url)  # 输出拼接后的完整 URL
print("响应状态码:", response.status_code)
3.2 POST 请求

POST 请求用于向服务器提交数据(如登录、注册、提交表单),数据放在请求体中(而非 URL 上),更安全。

3.2.1 提交表单数据(data 参数)

适用于 Content-Type: application/x-www-form-urlencoded 格式(最常见的表单提交格式):

python 复制代码
import requests

# 1. 定义要提交的表单数据(字典格式)
form_data = {
    "username": "test_user",
    "password": "123456"
}

# 2. 发送 POST 请求(data 参数传递表单数据)
response = requests.post(
    url="https://example.com/login",  # 目标接口 URL(示例,需替换为实际接口)
    data=form_data  # 提交表单数据
)

# 3. 解析响应(假设接口返回 JSON 格式)
if response.status_code == 200:
    result = response.json()  # 自动解析 JSON
    print("登录结果:", result)
else:
    print("请求失败,状态码:", response.status_code)
3.2.2 提交 JSON 数据(json 参数)

适用于 Content-Type: application/json 格式(现代接口常用,如 RESTful API):

python 复制代码
import requests

# 1. 定义 JSON 数据(字典格式,requests 会自动序列化为 JSON 字符串)
json_data = {
    "name": "张三",
    "age": 25,
    "hobbies": ["编程", "阅读"]
}

# 2. 发送 POST 请求(json 参数传递 JSON 数据)
response = requests.post(
    url="https://example.com/api/user",
    json=json_data  # 自动设置 Content-Type: application/json
)

# 3. 处理响应
print("响应结果:", response.json())
3.3 配置:请求头、超时、代理
3.3.1 设置请求头(headers

模拟浏览器请求(避免被服务器识别为爬虫),最常用的是 User-Agent(标识客户端类型):

python 复制代码
import requests

# 定义请求头(模拟 Chrome 浏览器)
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}

# 发送带请求头的 GET 请求
response = requests.get(
    url="https://www.baidu.com",
    headers=headers
)

print("响应状态码:", response.status_code)
3.3.2 设置超时(timeout

避免请求因网络问题一直阻塞,timeout 表示最大等待时间(单位:秒):

python 复制代码
import requests

try:
    response = requests.get(
        url="https://www.baidu.com",
        timeout=5  # 5 秒内未响应则抛出超时异常
    )
except requests.exceptions.Timeout:
    print("请求超时,请检查网络!")
3.3.3 处理 SSL 证书验证(verify

访问 HTTPS 网站时,requests 会自动验证 SSL 证书,若证书无效(如自签名证书),可关闭验证:

python 复制代码
import requests

# verify=False 关闭 SSL 证书验证(仅用于测试/内部网站)
response = requests.get(
    url="https://xxx.com",  # 示例:证书无效的 HTTPS 网站
    verify=False
)
3.4 会话保持(requests.Session()

用于需要维持登录状态的场景(如登录后访问个人中心),会话会自动保存 cookies:

python 复制代码
import requests

# 1. 创建会话对象(自动保存 cookies)
session = requests.Session()

# 2. 第一步:发送登录请求(会话会保存登录后的 cookies)
login_data = {"username": "test", "password": "123"}
session.post("https://example.com/login", data=login_data)

# 3. 第二步:访问需要登录的页面(会话携带 cookies,无需再次登录)
response = session.get("https://example.com/user/center")

print("个人中心内容:", response.text[:200])  # 成功获取登录后的内容

4. 实战案例:

4.1:爬取网页内容(百度首页标题)

场景:获取百度首页的 HTML 源码,并提取页面标题(基础爬虫入门)。

python 复制代码
import requests
from bs4 import BeautifulSoup  # 用于解析 HTML(需安装:pip install beautifulsoup4)

def crawl_baidu_title():
    # 1. 目标 URL(百度首页)
    url = "https://www.baidu.com"
    
    # 2. 设置请求头(模拟浏览器,避免被反爬)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
    }
    
    try:
        # 3. 发送 GET 请求(设置超时 5 秒)
        response = requests.get(
            url=url,
            headers=headers,
            timeout=5
        )
        
        # 4. 检查请求是否成功(200 表示成功)
        if response.status_code == 200:
            # 5. 解析 HTML 源码(用 BeautifulSoup)
            soup = BeautifulSoup(response.text, "html.parser")  # html.parser 是 Python 内置解析器
            title = soup.find("title").text  # 查找 <title> 标签并获取文本内容
            
            print(f"百度首页标题:{title}")
            print(f"页面源码长度:{len(response.text)} 字符")
        else:
            print(f"请求失败,状态码:{response.status_code}")
    
    # 捕获常见异常
    except requests.exceptions.Timeout:
        print("请求超时,请检查网络连接!")
    except requests.exceptions.ConnectionError:
        print("连接错误,请检查 URL 是否正确!")
    except Exception as e:
        print(f"未知错误:{str(e)}")

# 执行函数
if __name__ == "__main__":
    crawl_baidu_title()
语法讲解:
  • BeautifulSoup(response.text, "html.parser"):将 HTML 字符串转为可操作的对象,html.parser 无需额外安装;
  • soup.find("title"):查找 HTML 中第一个 <title> 标签,text 属性获取标签内的文本;
  • 异常捕获:Timeout(超时)、ConnectionError(连接错误)是网络请求最常见的异常,避免程序崩溃。
4.2:调用公开 API(获取天气数据)

场景:调用第三方公开 API(聚合数据天气接口),获取指定城市的天气(接口调用入门)。

python 复制代码
import requests

def get_weather(city="北京"):
    # 1. 接口信息(聚合数据天气 API,需替换为自己的 key,免费申请地址:https://www.juhe.cn/docs/api/id/73)
    api_url = "http://v.juhe.cn/tianqi/index"
    api_key = "你的接口 key"  # 替换为自己申请的 key(免费)
    
    # 2. 接口参数(根据 API 文档传递)
    params = {
        "cityname": city,  # 城市名称
        "key": api_key,    # 接口密钥
        "format": 2        # 返回格式:2 表示 JSON(1 表示 XML)
    }
    
    try:
        # 3. 发送 GET 请求(带参数)
        response = requests.get(
            url=api_url,
            params=params,
            timeout=10
        )
        
        if response.status_code == 200:
            # 4. 解析 JSON 响应(接口返回 JSON 格式)
            result = response.json()
            
            # 5. 根据 API 返回的状态判断是否成功(不同 API 的状态字段可能不同)
            if result["error_code"] == 0:  # error_code=0 表示接口调用成功
                # 提取关键天气数据
                realtime_weather = result["result"]["realtime"]  # 实时天气
                today_weather = result["result"]["future"][0]    # 今日天气
                
                print(f"===== {city} 天气信息 =====")
                print(f"当前温度:{realtime_weather['temperature']}℃")
                print(f"天气状况:{realtime_weather['info']}")
                print(f"风力:{realtime_weather['direct']}{realtime_weather['power']}级")
                print(f"今日最低温:{today_weather['temperature'].split('-')[0]}℃")
                print(f"今日最高温:{today_weather['temperature'].split('-')[1]}℃")
                print(f"温馨提示:{result['result']['life']['info']['chuanyi'][0]}")
            else:
                print(f"接口调用失败:{result['reason']}(错误码:{result['error_code']})")
        else:
            print(f"请求失败,状态码:{response.status_code}")
    
    except requests.exceptions.Timeout:
        print("请求超时,请稍后重试!")
    except Exception as e:
        print(f"错误:{str(e)}")

# 执行函数(可传入其他城市,如 get_weather("上海"))
if __name__ == "__main__":
    get_weather("广州")
语法讲解:
  • 接口参数:params 传递 API 要求的参数(城市名、密钥),API 文档会明确说明必填参数;
  • response.json():自动将 JSON 格式的响应转为 Python 字典,方便提取数据;
  • 嵌套字典取值:result["result"]["realtime"] 表示从响应结果中逐层提取实时天气数据,需根据 API 返回结构调整。
4.3:文件下载(下载图片到本地)

场景:从网络下载图片(如产品详情图)并保存到本地(文件操作 + 请求结合)。

python 复制代码
import requests
import os

def download_image(image_url, save_dir="downloads"):
    # 1. 验证 URL 是否有效(简单判断是否为图片 URL)
    if not image_url.endswith((".jpg", ".png", ".jpeg", ".gif")):
        print("无效的图片 URL(需以 .jpg/.png 等结尾)")
        return
    
    # 2. 创建保存目录(若不存在则创建)
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)  # 递归创建目录
    
    # 3. 提取文件名(从 URL 中截取,如 "https://example.com/img.jpg" → "img.jpg")
    file_name = image_url.split("/")[-1]
    save_path = os.path.join(save_dir, file_name)  # 完整保存路径(目录+文件名)
    
    try:
        # 4. 发送 GET 请求(stream=True 表示流式下载,适合大文件)
        response = requests.get(
            url=image_url,
            stream=True,  # 流式下载,避免一次性加载大文件到内存
            timeout=10
        )
        
        if response.status_code == 200:
            # 5. 以二进制模式写入文件(图片是二进制数据,用 "wb" 模式)
            with open(save_path, "wb") as f:
                # 分块写入(每次写 1024 字节,适合大文件)
                for chunk in response.iter_content(chunk_size=1024):
                    if chunk:  # 过滤空块
                        f.write(chunk)
            
            print(f"图片下载成功!保存路径:{save_path}")
        else:
            print(f"下载失败,状态码:{response.status_code}")
    
    except requests.exceptions.Timeout:
        print("下载超时!")
    except requests.exceptions.ConnectionError:
        print("连接错误,图片 URL 不可访问!")
    except Exception as e:
        print(f"下载错误:{str(e)}")

# 执行函数(可替换为任意图片 URL)
if __name__ == "__main__":
    image_url = "https://picsum.photos/800/600"  # 测试用图片 URL(随机生成 800x600 图片)
    download_image(image_url)
语法讲解:
  • stream=True:流式下载,适用于图片、视频等大文件,避免占用过多内存;
  • response.iter_content(chunk_size=1024):分块读取响应内容,chunk_size 表示每次读取的字节数;
  • 文件操作:with open(save_path, "wb") 以二进制写入模式打开文件,with 语句会自动关闭文件,无需手动调用 f.close()
  • 目录创建:os.makedirs(save_dir) 递归创建目录(若父目录不存在也会创建)。

5. 常见问题与避坑指南

  1. 请求被拒绝(403 Forbidden)

    • 原因:服务器识别为爬虫,拒绝访问;
    • 解决:设置 User-Agent 模拟浏览器,必要时添加 Referer(请求来源)。
  2. JSON 解析失败(json.decoder.JSONDecodeError)

    • 原因:响应内容不是 JSON 格式(如 HTML 错误页面);
    • 解决:先打印 response.text 查看实际响应内容,确认接口返回格式是否正确。
  3. 大文件下载内存溢出

    • 原因:未使用流式下载,一次性将大文件加载到内存;
    • 解决:添加 stream=True,分块写入文件(如案例 3)。
  4. 接口调用失败(错误码非 0)

    • 原因:参数错误、密钥无效、接口限流;
    • 解决:查看 API 文档,检查参数是否完整,密钥是否有效,是否需要处理限流(如添加请求间隔)。

6. 总结(核心知识点浓缩)

  1. 核心方法get()(获取)、post()(提交)、session()(会话保持);
  2. 关键参数params(GET 参数)、data/json(POST 数据)、headers(请求头)、timeout(超时);
  3. 响应处理status_code(判断成功)、text(字符串)、json()(JSON 解析)、content(二进制);
  4. 实战场景:网页爬取、接口调用、文件下载,掌握这 3 类场景即可应对 80% 的需求;
  5. 避坑关键:设置请求头、处理异常、流式下载大文件、保持会话状态。

2. httpx

1. 基本介绍

httpx 是 requests 的「增强版」HTTP 请求库,核心优势是 兼容 requests 语法 + 支持同步 / 异步双模式 + 更强功能扩展(HTTP/2、自动重试等)。

2. 安装

复制代码
# 基础安装(仅支持同步+HTTP/1.1)
pip|pip3 install httpx

# 完整安装(推荐,支持异步+HTTP/2+代理等扩展功能)
pip|pip3 install httpx[http2]

验证安装成功

复制代码
python -c "import httpx; print(httpx.__version__)"
# 示例输出:0.25.2

3. 核心基础:httpx 同步模式(完全兼容 requests)

httpx 同步模式的 API 与 requests 几乎完全一致,相当于「无缝替换」,但增加了更多实用功能。如果你的项目正在用 requests,直接全局替换 requestshttpx 即可运行。

3.1 入门第一步:GET 请求(获取数据)
3.1.1 简单入门
python 复制代码
import httpx

# 1. 发送 GET 请求(和 requests.get() 语法完全一致)
response = httpx.get("https://www.baidu.com")

# 2. 查看响应核心属性(和 requests 一致)
print("响应状态码:", response.status_code)  # 200 表示成功
print("响应内容(前 100 字符):", response.text[:100])  # 字符串形式响应(自动解码)
print("响应内容(字节流):", response.content[:50])  # 二进制形式(用于图片/文件)
print("响应头:", response.headers)  # 服务器返回的元数据
print("实际请求 URL:", response.url)  # 处理重定向后的最终 URL
3.1.2 GET 请求带参数
python 复制代码
import httpx

def get_with_params():
    # 1. 定义参数(字典格式,键值对对应 URL 中的参数)
    params = {
        "wd": "httpx 教程",  # 搜索关键词
        "pn": 0,  # 分页参数(第 0 页=第一页)
        "rn": 10  # 每页显示 10 条结果
    }

    # 2. 发送带参数的 GET 请求
    response = httpx.get(
        url="https://search.baidu.com/s",  # 基础 URL(不含参数)
        params=params,  # 传递参数(自动拼接为完整 URL)
        timeout=5  # 超时时间(5 秒内未响应则抛出异常,和 requests 一致)
    )

    # 3. 验证结果
    print("拼接后的完整 URL:", response.url)
    # 输出示例:https://search.baidu.com/s?wd=httpx+%E6%95%99%E7%A8%8B&pn=0&rn=10

if __name__ == "__main__":
    get_with_params()
参数解析:
  • url:目标请求地址(基础 URL,不含参数);
  • params:字典类型,存储 URL 拼接参数,httpx 会自动编码特殊字符(如中文、空格);
  • timeout:整数 / 浮点数,单位秒,防止请求因网络问题无限阻塞。
3.2 POST 请求

POST 用于向服务器提交数据(登录、表单提交、接口调用),httpx 支持两种主流格式:表单数据(application/x-www-form-urlencoded)和 JSON 数据(application/json)。

3.2.1 提交表单数据(data 参数)
python 复制代码
import httpx

def post_form_data():
    # 1. 定义表单数据(字典格式)
    form_data = {
        "username": "test_user",
        "password": "123456"
    }

    # 2. 发送 POST 请求(用 data 参数传递表单数据)
    response = httpx.post(
        url="https://example.com/login",  # 模拟登录接口(实际需替换为真实接口)
        data=form_data,  # 表单数据(自动设置 Content-Type: application/x-www-form-urlencoded)
        timeout=5
    )

    # 3. 处理响应(假设接口返回 JSON)
    if response.status_code == 200:
        result = response.json()  # 自动解析 JSON(和 requests 一致)
        print("登录结果:", result)
    else:
        print("请求失败,状态码:", response.status_code)

if __name__ == "__main__":
    post_form_data()
3.2.2 提交 JSON 数据(json 参数)

适用于现代 API(如 RESTful API),httpx 会自动将字典序列化为 JSON 字符串,并设置请求头 Content-Type: application/json

python 复制代码
import httpx

def post_json_data():
    # 1. 定义 JSON 数据(字典格式,支持嵌套)
    json_data = {
        "name": "张三",
        "age": 25,
        "hobbies": ["编程", "阅读"],
        "address": {"city": "北京", "district": "朝阳区"}
    }

    # 2. 发送 POST 请求(用 json 参数传递 JSON 数据)
    response = httpx.post(
        url="https://example.com/api/user",  # 模拟用户注册接口
        json=json_data,  # 自动序列化为 JSON,无需手动 json.dumps()
        timeout=5
    )

    print("响应结果:", response.json())

if __name__ == "__main__":
    post_json_data()
3.3 进阶配置:请求头、代理、超时
3.3.1 设置请求头(模拟浏览器,避免反爬)

服务器可能通过 User-Agent 识别爬虫,需设置请求头模拟浏览器:

python 复制代码
import httpx

def get_with_headers():
    # 1. 定义请求头(字典格式,key 为请求头名称,value 为对应值)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Referer": "https://www.baidu.com"  # 模拟请求来源(部分网站会校验)
    }

    # 2. 发送带请求头的 GET 请求
    response = httpx.get(
        url="https://www.baidu.com",
        headers=headers,
        timeout=5
    )

    print("响应状态码:", response.status_code)  # 200 表示成功(未被反爬拦截)

if __name__ == "__main__":
    get_with_headers()

参数解析:

  • headers:字典类型,常用键包括 User-Agent(客户端标识)、Referer(请求来源)、Authorization(身份认证 token)等。
3.3.2 设置代理(突破 IP 限制)

爬虫或接口测试时,需用代理隐藏真实 IP,httpx 支持 HTTP/HTTPS/SOCKS 代理:

python 复制代码
import httpx

def get_with_proxy():
    # 1. 定义代理配置(字典格式,key 为协议,value 为代理地址)
    proxies = {
        "http://": "http://127.0.0.1:7890",  # HTTP 代理
        "https://": "http://127.0.0.1:7890",  # HTTPS 代理
        # "https://": "socks5://127.0.0.1:7890"  # SOCKS 代理(需完整安装 httpx[http2])
    }

    # 2. 发送带代理的请求
    response = httpx.get(
        url="https://httpbin.org/ip",  # 用于查看当前 IP 的测试接口
        proxies=proxies,
        timeout=10,
        verify=False  # 关闭 SSL 证书验证(部分代理可能需要)
    )

    print("当前 IP 信息:", response.json())

if __name__ == "__main__":
    get_with_proxy()
3.3.3 会话保持(httpx.Client

用于需要维持登录状态的场景(如登录后访问个人中心),会话会自动保存 cookies,类似 requests.Session:

python 复制代码
import httpx

def session_demo():
    # 1. 创建会话对象(自动保存 cookies,复用连接)
    with httpx.Client() as client:
        # 2. 第一步:发送登录请求(会话保存登录 cookies)
        login_data = {"username": "test", "password": "123456"}
        client.post("https://example.com/login", data=login_data)

        # 3. 第二步:访问需要登录的页面(会话携带 cookies,无需再次登录)
        response = client.get("https://example.com/user/center")

        print("个人中心内容(前 200 字符):", response.text[:200])

if __name__ == "__main__":
    session_demo()
核心优势:
  • 复用 TCP 连接,比每次单独请求更快;
  • 自动保存 cookies、请求头,无需手动传递。

4. httpx 异步模式(高并发必备)

httpx 支持异步请求(需完整安装 httpx[http2]),语法简单,并发性能远超同步模式,适合批量接口调用、爬取等场景。

4.1 异步基础语法(AsyncClient

异步模式的核心是 httpx.AsyncClient,配合 async/await 语法使用,步骤如下:

  1. 函数定义前加 async 关键字;
  2. async with httpx.AsyncClient() 管理异步客户端;
  3. 请求方法(get/post)前加 await 等待响应;
  4. asyncio.run() 启动异步事件循环。
4.1.1 异步 GET 请求示例
python 复制代码
import httpx
import asyncio  # Python 内置异步库,用于启动事件循环

# 1. 定义异步函数(加 async 关键字)
async def async_get_demo():
    # 2. 创建异步客户端(async with 自动管理连接)
    async with httpx.AsyncClient() as client:
        # 3. 异步请求(加 await 等待响应,非阻塞)
        response = await client.get(
            url="https://www.baidu.com",
            timeout=5
        )
        print("异步 GET 响应状态码:", response.status_code)
        return response.text[:100]

# 4. 启动异步事件循环(执行异步函数)
if __name__ == "__main__":
    result = asyncio.run(async_get_demo())
    print("异步 GET 响应内容(前 100 字符):", result)
4.1.2 异步并发执行多个任务(核心场景)

asyncio.gather() 同时启动多个异步任务,总耗时 = 最长单个任务耗时,效率大幅提升:

python 复制代码
import httpx
import asyncio
import time

# 异步任务:模拟耗时接口请求(如获取城市天气)
async def fetch_weather(client, city):
    """
    异步获取城市天气
    :param client: 异步客户端对象(复用,提升性能)
    :param city: 城市名称(字符串)
    :return: 天气信息字符串
    """
    params = {
        "cityname": city,
        "key": "你的接口 key",  # 替换为聚合数据天气 API 的 key(免费申请:https://www.juhe.cn/docs/api/id/73)
        "format": 2  # 返回 JSON 格式
    }
    # 异步发送 GET 请求
    response = await client.get(
        url="http://v.juhe.cn/tianqi/index",
        params=params,
        timeout=10
    )
    # 解析响应(JSON 格式)
    result = response.json()
    if result["error_code"] == 0:
        realtime = result["result"]["realtime"]
        return f"【{city}】天气:{realtime['info']},温度:{realtime['temperature']}℃"
    else:
        return f"【{city}】获取失败:{result['reason']}"

# 主异步函数:批量执行任务
async def main():
    # 定义需要查询的城市列表(10 个城市,并发执行)
    cities = ["北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "西安", "南京", "重庆"]
    
    # 创建异步客户端(复用连接,关键优化)
    async with httpx.AsyncClient() as client:
        # 生成所有异步任务(不立即执行,仅创建任务对象)
        tasks = [fetch_weather(client, city) for city in cities]
        
        # 并发执行所有任务,等待全部完成(总耗时≈最长单个任务耗时)
        results = await asyncio.gather(*tasks)  # * 用于解包任务列表
    
    # 打印所有结果
    for res in results:
        print(res)

if __name__ == "__main__":
    # 记录开始时间
    start_time = time.time()
    # 启动异步事件循环
    asyncio.run(main())
    # 计算总耗时
    end_time = time.time()
    print(f"\n总耗时:{end_time - start_time:.2f} 秒")

代码解析:

  1. 参数说明

    • client:异步客户端对象,必须复用(不能在每个任务中创建新客户端,否则会频繁建立连接,性能骤降);
    • city:字符串类型,传入需要查询的城市名称;
    • tasks:列表推导式生成所有异步任务,每个任务对应一个城市的天气查询。
  2. 执行效果:10 个城市的天气查询并发执行,总耗时≈1-2 秒(同步执行需 10-20 秒),体现异步高并发优势。

  3. 核心优化

    • 复用 AsyncClient:一个客户端管理所有请求,复用 TCP 连接;
    • asyncio.gather(*tasks):批量并发执行任务,自动调度,无需手动管理。

5.httpx 增强功能(requests 没有的实用特性)

httpx 相比 requests 增加了多个关键功能,无需额外依赖即可实现。

5.1 自动重试(避免网络波动导致失败)

网络不稳定时,自动重试失败的请求(如超时、连接错误),无需手动写重试逻辑:

python 复制代码
import httpx

def retry_demo():
    # 创建客户端时配置重试策略
    with httpx.Client(
        retries=3,  # 最大重试次数(3 次)
        retry_conditions=httpx.HTTPStatusRetryCondition(
            status_codes=[500, 502, 503, 504]  # 仅对 5xx 服务器错误重试
        )
    ) as client:
        response = client.get("https://example.com/api/data", timeout=5)
        print("响应状态码:", response.status_code)

if __name__ == "__main__":
    retry_demo()
5.2 支持 HTTP/2 协议

现代 API 越来越多支持 HTTP/2,httpx 完整安装后可自动适配,无需额外配置:

python 复制代码
import httpx

def http2_demo():
    # 创建支持 HTTP/2 的客户端
    with httpx.Client(http2=True) as client:
        response = client.get("https://http2.pro/api/v1", timeout=5)
        print("HTTP 协议版本:", response.http_version)  # 输出:HTTP/2
        print("响应结果:", response.json())

if __name__ == "__main__":
    http2_demo()
5.3 连接限制(防止并发过高)

批量请求时,限制最大连接数,避免给服务器造成过大压力:

python 复制代码
import httpx

def limit_connections_demo():
    # 配置最大连接数(全局最大 10 个,单主机最大 5 个)
    limits = httpx.Limits(
        max_connections=10,
        max_keepalive_connections=5
    )
    with httpx.Client(limits=limits) as client:
        for i in range(10):
            response = client.get("https://www.baidu.com", timeout=5)
            print(f"第 {i+1} 次请求状态码:", response.status_code)

if __name__ == "__main__":
    limit_connections_demo()

6. 实战案例:httpx 异步批量爬取网页

6.1 案例场景

批量爬取 5 个主流网站的标题,用异步模式实现高并发,对比同步模式的效率差异。

python 复制代码
import httpx
import asyncio
import time
from bs4 import BeautifulSoup  # 用于解析 HTML(需安装:pip install beautifulsoup4)

async def crawl_page(client, url, site_name):
    """
    异步爬取单个网页的标题
    :param client: 异步客户端对象(复用连接)
    :param url: 目标网页 URL(字符串)
    :param site_name: 网站名称(用于日志输出,字符串)
    :return: 爬取结果(成功返回标题,失败返回错误信息)
    """
    try:
        # 1. 异步发送 GET 请求(加 await,设置超时 10 秒)
        response = await client.get(
            url=url,
            headers={
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
            },
            timeout=httpx.Timeout(10.0)  # 显式配置超时(更灵活)
        )
        
        # 2. 检查请求是否成功(200 表示成功)
        response.raise_for_status()  # 若状态码为 4xx/5xx,会抛出异常
        
        # 3. 解析 HTML,提取标题(用 BeautifulSoup)
        soup = BeautifulSoup(response.text, "html.parser")  # html.parser 是 Python 内置解析器
        title = soup.find("title").text.strip()  # 查找 <title> 标签并获取文本
        
        return f"✅ 【{site_name}】爬取成功:标题={title},页面大小={len(response.text)} 字符"
    
    # 捕获常见异常
    except httpx.TimeoutException:
        return f"❌ 【{site_name}】爬取失败:请求超时"
    except httpx.HTTPStatusError as e:
        return f"❌ 【{site_name}】爬取失败:状态码={e.response.status_code}"
    except Exception as e:
        return f"❌ 【{site_name}】爬取失败:未知错误={str(e)}"

async def main():
    """主异步函数:批量启动爬取任务"""
    # 1. 定义要爬取的网站列表(URL + 网站名称)
    sites = [
        ("https://www.baidu.com", "百度"),
        ("https://www.taobao.com", "淘宝"),
        ("https://www.jd.com", "京东"),
        ("https://www.zhihu.com", "知乎"),
        ("https://www.bilibili.com", "B站")
    ]
    
    # 2. 创建异步客户端(复用连接,配置超时和连接限制)
    async with httpx.AsyncClient(
        timeout=httpx.Timeout(10.0),  # 全局超时配置
        limits=httpx.Limits(max_connections=10)  # 最大连接数限制
    ) as client:
        # 3. 生成所有异步任务(列表推导式,每个任务对应一个网站)
        tasks = [crawl_page(client, url, name) for url, name in sites]
        
        # 4. 并发执行所有任务,等待全部完成(返回结果列表)
        results = await asyncio.gather(*tasks)
    
    # 5. 打印所有爬取结果
    print("=" * 50)
    print("爬取结果汇总:")
    print("=" * 50)
    for result in results:
        print(result)

if __name__ == "__main__":
    # 记录开始时间
    start_time = time.time()
    
    # 启动异步事件循环(执行主函数)
    asyncio.run(main())
    
    # 计算总耗时
    end_time = time.time()
    print(f"\n总耗时:{end_time - start_time:.2f} 秒")

运行效果

复制代码
==================================================
爬取结果汇总:
==================================================
✅ 【百度】爬取成功:标题=百度一下,你就知道,页面大小=22732 字符
✅ 【京东】爬取成功:标题=京东(JD.COM)-正品低价、品质保障、配送及时、轻松购物!,页面大小=138567 字符
✅ 【淘宝】爬取成功:标题=淘宝网 - 淘!我喜欢,页面大小=104892 字符
✅ 【知乎】爬取成功:标题=知乎 - 有问题,就会有答案,页面大小=89765 字符
✅ 【B站】爬取成功:标题=哔哩哔哩 (゜-゜)つロ 干杯~-bilibili,页面大小=98743 字符

总耗时:1.86 秒

核心亮点解析

  1. 高并发:5 个网站同时爬取,总耗时≈1-2 秒(同步模式需 5-10 秒);
  2. 异常处理:捕获超时、状态码错误等常见问题,避免单个任务失败导致整体崩溃;
  3. 连接复用 :复用 AsyncClient,减少 TCP 连接建立 / 关闭的开销;
  4. 灵活配置:支持全局超时、连接限制,适配不同场景需求。

7. 常见问题与避坑指南

7.1. 异步模式报错「No module named 'httpx._async'」
  • 原因:未安装完整版本的 httpx,缺少异步依赖;
  • 解决:执行 pip install httpx[http2] 重新安装。
7.2. 响应解析 JSON 报错「JSONDecodeError」
  • 原因:响应内容不是 JSON 格式(如 HTML 错误页面);
  • 解决:先打印 response.text 查看实际响应内容,确认接口返回格式是否正确。
7.3. 异步任务中创建多个 AsyncClient 导致性能差
  • 错误:在循环 / 子函数中创建 async with httpx.AsyncClient()
  • 正确:复用一个 AsyncClient,传入所有子任务(如实战案例中那样)。
7.4. 代理配置后无法访问
  • 原因:代理地址错误、端口未开放,或 HTTPS 代理需要关闭 SSL 验证;
  • 解决:检查代理是否可用,添加 verify=False 参数(仅测试环境使用)。
7.5. 超时参数配置错误
  • 同步模式:timeout=5(直接传数字,单位秒);
  • 异步模式:支持两种写法:timeout=5timeout=httpx.Timeout(5)(更灵活)。

8. 总结

  1. 同步模式:完全兼容 requests,直接替换即可使用,新增自动重试、HTTP/2 等功能;
  2. 异步模式 :用 AsyncClient+async/await,高并发场景必备,语法简单;
  3. 核心 API
    • 同步:httpx.get()/httpx.post()/httpx.Client()
    • 异步:httpx.AsyncClient()+await client.get()
  4. 适用场景
    • 简单脚本、少量请求:用同步模式;
    • 批量接口调用、爬取、高并发:用异步模式;
    • 需要 HTTP/2、自动重试、代理池:优先选 httpx。
相关推荐
雪碧聊技术22 天前
requests入门
python·requests·请求头的user-agent
yivifu1 个月前
九成自动化批量备份知乎专栏文章
beautifulsoup·requests·playwright·知乎专栏备份
一晌小贪欢2 个月前
Python爬虫第3课:BeautifulSoup解析HTML与数据提取
爬虫·python·网络爬虫·beautifulsoup·python爬虫·python3·requests
介一安全2 个月前
资产信息收集与指纹识别:HTTPX联动工具实战指南
网络安全·安全性测试·httpx·安全工具
网硕互联的小客服3 个月前
SSL部署完成,https显示连接不安全如何处理?
ssl·httpx
万粉变现经纪人3 个月前
如何解决pip安装报错ModuleNotFoundError: No module named ‘python-dateutil’问题
开发语言·ide·python·pycharm·pandas·pip·httpx
伊玛目的门徒3 个月前
告别 OpenAI SDK:如何使用 Python requests 库调用大模型 API(例如百度的ernie-4.5-turbo)
python·openai·requests·大模型调用·ernie-4.5-turbo
万粉变现经纪人3 个月前
如何解决pip安装报错ModuleNotFoundError: No module named ‘websockets’问题
ide·pycharm·beautifulsoup·pandas·fastapi·pip·httpx
青衫客364 个月前
Portkey-AI gateway 的一次“假压缩头”翻车的完整排障记:由 httpx 解压异常引发的根因分析
大模型·llm·gateway·httpx