PHP 抓取结果存入数据库

wen PHP项目 4

本文目录导读:

PHP 抓取结果存入数据库

  1. 数据库准备
  2. 基础抓取类
  3. 使用示例
  4. 配置文件示例 (config.php)
  5. 增强功能(可选)
  6. 注意事项

我来为您提供一个完整的PHP抓取网页内容并存入数据库的解决方案:

数据库准备

CREATE TABLE IF NOT EXISTS `scraped_data` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `url` VARCHAR(500) NOT NULL, VARCHAR(500),
  `content` TEXT,
  `images` TEXT,
  `links` TEXT,
  `scraped_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX `idx_url` (`url`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

基础抓取类

<?php
class WebScraper {
    private $pdo;
    private $timeout = 30;
    private $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    /**
     * 抓取网页内容
     * @param string $url 目标URL
     * @return array 抓取的数据
     */
    public function scrape($url) {
        try {
            // 获取网页内容
            $html = $this->fetchContent($url);
            if (!$html) {
                throw new Exception('无法获取网页内容');
            }
            // 解析网页内容
            $data = $this->parseContent($html, $url);
            // 保存到数据库
            $this->saveToDatabase($data);
            return $data;
        } catch (Exception $e) {
            error_log('抓取失败: ' . $e->getMessage());
            return false;
        }
    }
    /**
     * 获取网页内容
     */
    private function fetchContent($url) {
        $ch = curl_init();
        $options = [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS => 5,
            CURLOPT_TIMEOUT => $this->timeout,
            CURLOPT_CONNECTTIMEOUT => 10,
            CURLOPT_USERAGENT => $this->userAgent,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_ENCODING => '', // 支持压缩
            CURLOPT_HTTPHEADER => [
                'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8',
            ],
        ];
        curl_setopt_array($ch, $options);
        $content = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if (curl_errno($ch)) {
            error_log('CURL错误: ' . curl_error($ch));
            return false;
        }
        curl_close($ch);
        if ($httpCode !== 200) {
            error_log("HTTP状态码: $httpCode");
            return false;
        }
        return $content;
    }
    /**
     * 解析网页内容
     */
    private function parseContent($html, $url) {
        $dom = new DOMDocument();
        libxml_use_internal_errors(true);
        $dom->loadHTML('<?xml encoding="UTF-8">' . $html);
        libxml_clear_errors();
        $xpath = new DOMXPath($dom);
        // 提取标题
        $titleNode = $xpath->query('//title');
        $title = $titleNode->length > 0 ? trim($titleNode->item(0)->textContent) : '';
        // 提取主体内容(可根据需要调整选择器)
        $contentNode = $xpath->query('//article | //main | //div[@class="content"] | //div[@id="content"]');
        $content = '';
        if ($contentNode->length > 0) {
            $content = $this->cleanContent($contentNode->item(0)->textContent);
        } else {
            // 回退到body内容
            $bodyNode = $dom->getElementsByTagName('body');
            if ($bodyNode->length > 0) {
                $content = $this->cleanContent($bodyNode->item(0)->textContent);
            }
        }
        // 提取图片
        $images = [];
        $imgNodes = $dom->getElementsByTagName('img');
        foreach ($imgNodes as $img) {
            $src = $img->getAttribute('src');
            if ($src && filter_var($src, FILTER_VALIDATE_URL) === false) {
                $src = $this->resolveUrl($url, $src);
            }
            if ($src) {
                $images[] = $src;
            }
        }
        // 提取链接
        $links = [];
        $linkNodes = $dom->getElementsByTagName('a');
        foreach ($linkNodes as $link) {
            $href = $link->getAttribute('href');
            if ($href && !preg_match('/^(javascript|mailto|tel):/i', $href)) {
                if (filter_var($href, FILTER_VALIDATE_URL) === false) {
                    $href = $this->resolveUrl($url, $href);
                }
                $links[] = [
                    'text' => trim($link->textContent),
                    'href' => $href
                ];
            }
        }
        return [
            'url' => $url,
            'title' => $title,
            'content' => $content,
            'images' => json_encode($images),
            'links' => json_encode($links),
            'scraped_at' => date('Y-m-d H:i:s')
        ];
    }
    /**
     * 清理内容
     */
    private function cleanContent($content) {
        // 去除多余空白
        $content = preg_replace('/\s+/', ' ', $content);
        // 去除特殊字符
        $content = trim($content);
        return $content;
    }
    /**
     * 解析相对URL为绝对URL
     */
    private function resolveUrl($baseUrl, $relativeUrl) {
        // 解析基础URL
        $parsedBase = parse_url($baseUrl);
        // 如果是完整的URL则直接返回
        if (filter_var($relativeUrl, FILTER_VALIDATE_URL)) {
            return $relativeUrl;
        }
        // 处理相对URL
        if (strpos($relativeUrl, '//') === 0) {
            return $parsedBase['scheme'] . ':' . $relativeUrl;
        }
        if (strpos($relativeUrl, '/') === 0) {
            return $parsedBase['scheme'] . '://' . $parsedBase['host'] . $relativeUrl;
        }
        $path = isset($parsedBase['path']) ? $parsedBase['path'] : '/';
        $path = preg_replace('/[^\/]*$/', '', $path);
        return $parsedBase['scheme'] . '://' . $parsedBase['host'] . $path . $relativeUrl;
    }
    /**
     * 保存到数据库
     */
    private function saveToDatabase($data) {
        try {
            $stmt = $this->pdo->prepare(
                "INSERT INTO scraped_data (url, title, content, images, links, scraped_at) 
                 VALUES (:url, :title, :content, :images, :links, :scraped_at)"
            );
            return $stmt->execute($data);
        } catch (PDOException $e) {
            error_log('数据库保存失败: ' . $e->getMessage());
            return false;
        }
    }
    /**
     * 检查URL是否已存在
     */
    public function isUrlExists($url) {
        $stmt = $this->pdo->prepare("SELECT COUNT(*) FROM scraped_data WHERE url = :url");
        $stmt->execute([':url' => $url]);
        return $stmt->fetchColumn() > 0;
    }
}
?>

使用示例

<?php
// 加载数据库配置
require_once 'config.php';
// 创建PDO连接
try {
    $pdo = new PDO(
        "mysql:host=$db_host;dbname=$db_name;charset=utf8mb4",
        $db_user,
        $db_pass,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]
    );
} catch (PDOException $e) {
    die("数据库连接失败: " . $e->getMessage());
}
$scraper = new WebScraper($pdo);
// 要抓取的URL列表
$urls = [
    'https://example.com/page1',
    'https://example.com/page2',
    'https://example.com/page3',
];
foreach ($urls as $url) {
    // 检查是否已存在
    if ($scraper->isUrlExists($url)) {
        echo "URL已存在,跳过: $url\n";
        continue;
    }
    echo "开始抓取: $url\n";
    $result = $scraper->scrape($url);
    if ($result) {
        echo "成功抓取并保存: {$result['title']}\n";
    } else {
        echo "抓取失败: $url\n";
    }
    // 避免请求过快
    usleep(rand(500000, 1000000)); // 0.5-1秒延迟
}
?>

配置文件示例 (config.php)

<?php
$db_host = 'localhost';
$db_name = 'web_scraper';
$db_user = 'root';
$db_pass = 'your_password';
// 可选:代理配置
$proxy_config = [
    'proxy_type' => CURLPROXY_HTTP,
    'proxy_host' => '127.0.0.1',
    'proxy_port' => 8080,
    'proxy_user' => '',
    'proxy_pass' => '',
];
// 可选:多线程配置(需要pcntl扩展)
$threads = 5;
?>

增强功能(可选)

支持Cookie和会话

private function fetchContentWithCookies($url, $cookies) {
    $cookieFile = tempnam('/tmp', 'cookie_');
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_COOKIE, $cookies);
    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieFile);
    curl_setopt($ch, CURLOPT_COOKIEFILE, $cookieFile);
    // ... 其他设置
}

支持JavaScript渲染

// 使用无头浏览器(需要安装Puppeteer)
public function scrapeWithHeadlessBrowser($url) {
    $html = shell_exec("node render.js \"$url\"");
    // 然后解析HTML
}

注意事项

  1. 遵守robots.txt和网站服务条款
  2. 设置适当的请求延迟,避免给目标网站造成压力
  3. 处理编码问题,确保正确解析UTF-8、GBK等编码
  4. 错误处理日志记录很重要
  5. 数据库连接使用PDO以增强安全性
  6. 定期清理不需要的数据

这个解决方案已经包含常用的网页抓取功能,您可以根据具体需求进行调整和扩展。

抱歉,评论功能暂时关闭!