php爬虫上传文件

admin 101 0
PHP爬虫实现文件上传,核心是通过模拟浏览器行为,利用cURL库发送包含文件的POST请求,需先分析目标网站表单结构,明确action地址、enctype(需设为multipart/form-data)及字段名,使用curl_setopt设置CURLOPT_POST、CURLOPT_POSTFIELDS(文件需用@符号标记路径,如'@file.jpg'),并添加请求头模拟真实浏览器,同时需处理目标网站的验证机制(如token、session),确保请求合法,安全方面需注意文件类型过滤、路径验证,防止恶意文件上传,此技术常用于批量数据导入、自动化表单提交等场景,需结合异常处理确保稳定性。

PHP爬虫实战:手把手教你使用cURL与Guzzle模拟表单上传文件

在开发网络爬虫或自动化脚本时,我们不仅需要从服务器"抓取"数据,有时还需要模拟用户行为向服务器"提交"数据。模拟文件上传是一个比普通表单提交(键值对)稍微复杂的过程,因为它涉及到特殊的HTTP请求头格式(multipart/form-data)。

本文将深入探讨如何使用PHP原生的cURL扩展以及流行的Guzzle HTTP客户端来实现这一功能,并提供完整的代码示例和最佳实践。

核心原理:理解multipart/form-data

在普通的表单提交中(如登录),数据的格式通常是application/x-www-form-urlencoded,类似于URL参数字符串的形式。

当你需要上传文件时,浏览器会将请求头中的Content-Type设置为multipart/form-data,请求体会被分割成多个部分(boundary分隔),每个部分对应一个表单控件,文件部分不仅包含文件内容,还包含文件名、MIME类型等元信息。

在PHP爬虫中,我们的目标就是精确构造这种格式的HTTP请求。

使用PHP原生cURL

cURL是PHP内置的最强大的HTTP请求工具,在PHP 5.5及更高版本中,引入了CURLFile类,使得文件上传变得非常安全和规范(不再建议使用旧的前缀方法)。

代码示例:

假设目标网站有一个上传接口http://example.com/upload.php,其HTML表单如下:

<input type="file" name="user_avatar">

我们的PHP爬虫代码如下:

<?php
// 1. 目标接口URL
$url = 'http://example.com/upload.php';
// 2. 准备上传的文件路径(建议使用绝对路径)
$filePath = realpath('./images/my_photo.jpg');
// 3. 创建CURLFile对象
// 参数1: 文件路径, 参数2: MIME类型(可选), 参数3: 上传后的文件名(可选)
$mimeType = mime_content_type($filePath); // 获取真实MIME类型,如image/jpeg
$postName = 'user_avatar'; // 对应表单中的name属性
$fileData = new CURLFile($filePath, $mimeType, 'uploaded_photo.jpg');
// 4. 构建POST数据数组
// 除了文件,你还可以传递其他普通的表单字段
$postFields = [
    $postName => $fileData,
    'username' => 'crawler_bot',
    'description' => '这是一张通过爬虫上传的图片',
    'submit' => '上传'
];
// 5. 初始化cURL
$ch = curl_init();
// 6. 设置cURL选项
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); // 自动识别multipart/form-data
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 将响应存入变量而不直接输出
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 如果目标网站使用HTTPS且证书有问题,可以临时关闭验证
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// 模拟浏览器User-Agent
curl_setopt($ch, CURLOPT_USER_AGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
// 设置超时时间
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
// 添加必要的HTTP头
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8'
]);
// 7. 执行请求
$response = curl_exec($ch);
// 8. 检查错误
if (curl_errno($ch)) {
    echo 'Curl Error: ' . curl_error($ch);
} else {
    // 获取HTTP状态码
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    echo "HTTP Status Code: " . $httpCode . "\n";
    echo "Server Response: " . $response;
}
// 9. 关闭cURL资源
curl_close($ch);
?>

重要注意事项:

  1. 文件路径:确保文件路径正确且可读
  2. MIME类型:使用mime_content_type()获取真实的MIME类型,而不是硬编码
  3. 错误处理:总是检查cURL错误和HTTP状态码
  4. 安全性:关闭SSL验证仅用于开发环境,生产环境应妥善处理证书

使用Guzzle HTTP客户端

Guzzle是一个流行的PHP HTTP客户端,提供了更简洁的API和更强大的功能,使用Guzzle上传文件会更加优雅和直观。

安装Guzzle:

composer require guzzlehttp/guzzle

代码示例:

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Stream;
// 1. 创建Guzzle客户端
$client = new Client([
    'timeout'  => 30.0,
    'verify' => false, // 生产环境应设置为true或提供CA证书路径
    'headers' => [
        'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
    ]
]);
// 2. 准备文件
$filePath = './images/my_photo.jpg';
$fileName = 'uploaded_photo.jpg';
$mimeType = mime_content_type($filePath);
// 3. 创建multipart表单数据
$multipart = [
    [
        'name'     => 'user_avatar',
        'contents' => fopen($filePath, 'r'),
        'filename' => $fileName,
        'headers'  => [
            'Content-Type' => $mimeType
        ]
    ],
    [
        'name'     => 'username',
        'contents' => 'crawler_bot'
    ],
    [
        'name'     => 'description',
        'contents' => '这是一张通过Guzzle上传的图片'
    ],
    [
        'name'     => 'submit',
        'contents' => '上传'
    ]
];
// 4. 发送请求
try {
    $response = $client->post('http://example.com/upload.php', [
        'multipart' => $multipart
    ]);
    // 5. 处理响应
    $statusCode = $response->getStatusCode();
    $body = $response->getBody();
    echo "HTTP Status Code: " . $statusCode . "\n";
    echo "Server Response: " . $body . "\n";
} catch (GuzzleHttp\Exception\RequestException $e) {
    echo "Request failed: " . $e->getMessage() . "\n";
    if ($e->hasResponse()) {
        echo "Response: " . $e->getResponse()->getBody()->getContents() . "\n";
    }
}
?>

Guzzle的优势:

  1. 更简洁的API:代码更易读,维护性更好
  2. 更好的错误处理:提供详细的异常信息
  3. 流式处理:支持大文件上传,不会占用过多内存
  4. 中间件支持:可以添加各种中间件来处理请求/响应
  5. Promise支持:支持异步请求

高级技巧与最佳实践

处理大文件上传

对于大文件,建议使用流式处理:

// cURL方式
curl_setopt($ch, CURLOPT_INFILE, fopen($filePath, 'r'));
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
// Guzzle方式(默认就是流式)
$multipart = [
    [
        'name'     => 'file',
        'contents' => fopen($filePath, 'r'),
        'filename' => basename($filePath)
    ]
];

添加Cookie支持

// cURL方式
$cookieFile

标签: #php爬虫 #文件上传