PHP 的一个网页抓取器,用于轻松从网页中提取数据 -> laurentvw 的一部分

v2.0.1 2024-03-17 06:57 UTC

This package is not auto-updated.

Last update: 2024-09-29 09:44:09 UTC


README

Scrapher 是一个 PHP 库,用于轻松从网页中抓取数据。

入门

安装

将包添加到您的 composer.json 中并运行 composer update

{
    "require": {
        "laurentvw/scrapher": "2.*"
    }
}

对于仍在使用 v1.0 ("LavaCrawler") 的人,您可以在以下位置找到文档: https://github.com/Laurentvw/scrapher/tree/v1.0.2

基本用法

为了开始抓取,您需要设置要抓取的 URL(s) 或 HTML,以及要使用的选择器类型(例如正则表达式选择器,以及您希望匹配的数据)。

use \Laurentvw\Scrapher\Scrapher;
use \Laurentvw\Scrapher\Selectors\RegexSelector;

$url = 'https://www.google.com/';
$scrapher = new Scrapher($url);

// Match all links on a page
$regex = '/<a.*?href=(?:"(.*?)").*?>(.*?)<\/a>/ms';

$matchConfig = array(
    array(
        'name' => 'url',
        'id' => 1, // the first match (.*?) from the regex
    ),
    array(
        'name' => 'title',
        'id' => 2, // the second match (.*?) from the regex
    ),
);

$matches = $scrapher->with(new RegexSelector($regex, $matchConfig));

$results = $matches->get();

这将返回一个基于设置的匹配配置的数组列表。

array(29) {
  [0] =>
  array(2) {
    'url' =>
    string(34) "https://www.google.com/webhp?tab=ww"
    'title' =>
    string(6) "Search"
  }
  ...
}

文档

实例化

当创建 Scrapher 实例时,您可以可选地传递一个或多个 URL。

传递多个 URL 在您想要在不同页面上抓取相同数据时很有用。例如,当内容通过分页分开时。

$scrapher = new Scrapher($url);
$scrapher = new Scrapher(array($url, $url2));

如果您更喜欢使用专门的客户端/库自行抓取页面,您也可以简单地传递页面的实际内容。如果您想抓取除网页之外的其他内容(例如本地文件),这也很方便。

$scrapher = new Scrapher($content);
$scrapher = new Scrapher(array($content, $content2));

在某些情况下,您可能需要在运行时动态添加(即附加)URL 或内容。

$scrapher->addUrl($url);
$scrapher->addUrls(array($url, $url2));
$scrapher->addContent($content);
$scrapher->addContents(array($content, $content2));

使用选择器匹配数据

在检索或排序匹配的数据之前,您需要选择一个选择器来匹配您想要的数据。

目前,Scrapher 提供了一个内置选择器 RegexSelector,它允许您使用正则表达式选择数据。

选择器接受一个表达式和一个匹配配置作为其参数。

例如,要匹配所有链接及其链接名称,您可以这样做

$regExpression = '/<a.*?href=(?:"(.*?)").*?>(.*?)<\/a>/ms';

$matchConfig = array(
    array(
        // The "name" key let's you name the data you're looking for,
        // and will be used when retrieving the matched data
        'name' => 'url',
        // The "id" key is an identifier used during the regular expression search.
        // The id 1 corresponds to the first match in the regular expression, matching the URL.
        'id' => 1,
    ),
    array(
        'name' => 'title',
        'id' => 2,
    ),
);

$matches = $scrapher->with(new RegexSelector($regExpression, $matchConfig));

请注意,传递给 "id" 键的值可能因您使用的选择器而异,实际上可以是任何东西。您可以将 "id" 键视为给定表达式与其选择器之间的粘合剂。

RegexSelector 在内部使用 https://php.ac.cn/manual/en/function.preg-match-all.php

为了方便起见,当使用正则表达式时,带有 'id' => 0 的匹配将返回被爬取页面的 URL。

检索 & 排序

一旦您使用 with 方法指定了选择器,您就可以开始检索和/或排序数据。

检索

// Return all matches
$results = $matches->get();

// Return all matches with a subset of the data (either use multiple arguments or an array for more than one column)
$results = $matches->get('title');

// Return the first match
$result = $matches->first();

// Return the last match
$result = $matches->last();

// Count the number of matches
$numberOfMatches = $matches->count();

偏移量 & 限制

// Take the first N matches
$results = $matches->take(5)->get();

// Skip the first N matches
$results = $matches->skip(1)->get();

// Take 5 matches starting from the second one.
$results = $matches->skip(1)->take(5)->get();

排序

// Order by title
$results = $matches->orderBy('title')->get();

// Order by title, then by URL
$results = $matches->orderBy('title')->orderBy('url', 'desc')->get();

// Custom sorting: For values that do not lend well with sorting, e.g. dates*.
$results = $matches->orderBy('date', 'desc', 'date_create')->get();

// Simply reverse the order of the results
$results = $matches->reverse()->get();

过滤

您可以对匹配的数据进行过滤,以细化您的结果集。返回 true 以保留匹配项,返回 false 以过滤它。

$matches->filter(function($match) {
    // Return only matches that contain 'Google' in the link title.
    return stristr($match['title'], 'Google') ? true : false;
});

变异

为了处理不一致性或格式问题,您可以将匹配的值更改为更理想的值。变异发生在过滤和排序结果集之前。您可以通过在匹配配置数组中使用带有 2 个参数的闭包的 apply 索引来完成此操作:匹配的值和被爬取页面的 URL。

$matchConfig = array(
    array(
        'name' => 'url',
        'id' => 1,
        // Add domain to relative URLs
        'apply' => function($match, $sourceUrl)
        {
            if (!stristr($match, 'http')) {
                return $sourceUrl . trim($match, '/');
            }
            return $match;
        },
    ),
    array(
        'name' => 'title',
        'id' => 2,
        // Remove all html tags inside the link title
        'apply' => function($match) {
            return strip_tags($match);
        },
    ),
    ...
);

验证

您可以通过验证匹配的数据来确保结果集始终包含所需的结果。验证发生在可选地使用 apply 修改数据集之后。要添加应用于数据的验证规则,请使用匹配配置数组中的 validate 索引,并传递一个接受两个参数的闭包:匹配的值和爬取的网页的URL。如果验证成功,闭包应返回 true;如果验证失败,返回 false。验证失败的匹配将从中移除。

$matchConfig = array(
    array(
        'name' => 'url',
        'id' => 1,
        // Make sure it is a valid url
        'validate' => function($match) {
            return filter_var($match, FILTER_VALIDATE_URL);
        },
    ),
    array(
        'name' => 'title',
        'id' => 2,
        // We only want titles that are between 1 and 50 characters long.
        'validate' => function($match) {
            return strlen($match) >= 1 && strlen($match) <= 50;
        },
    ),
    ...
);

日志记录

如果您想查看被过滤掉的匹配项或由于验证失败而被移除的匹配项,可以使用 getLogs 方法,该方法返回一个包含消息日志的数组。

$logs = $matches->getLogs();

你知道吗?

所有方法都是可链式的

$scrapher = new Scrapher();
$scrapher->addUrl($url)->with($regexSelector)->filter(...)->orderBy('title')->skip(1)->take(5)->get();

只有 getfirstlastcountgetLogs 方法会导致链式调用结束,因为它们都返回一个特定的结果。

您可以从一个页面抓取不同的数据

假设您正在抓取一个页面,并想获取所有H2标题以及页面上的所有链接。您可以通过不重新实例化Scrapher来实现这一点。

$scrapher = new Scrapher($url);
$h2Titles = $scrapher->with($h2RegexSelector)->get();
$links = $scrapher->with($linksRegexSelector)->get();

关于

作者

Laurent Van Winckel - http://www.laurentvw.com - http://twitter.com/Laurentvw

许可协议

Scrapher遵循MIT许可协议 - 详细信息请参阅LICENSE文件。

贡献

我们欢迎对Laurentvw\Scrapher的贡献。通过通过 GitHub pull requests 发送您的贡献,您会使我们的工作变得更轻松。

您还可以 创建一个issue 来报告错误或请求新功能。