使用PHP刮擦:逐步教程
#教程 #php #webscraping #howto

网络刮擦已经变得越来越流行,现在是IT社区中的一个热门话题。结果,几个库可帮助您从网站上刮擦数据。在这里,您将学习如何使用最受欢迎的网络刮擦库之一在PHP中构建网络刮板。

在本教程中,您将学习php中网络刮擦的基础知识。然后,如何解决最受欢迎的反刮擦系统,学习更多高级技术和概念,例如平行刮擦和无头浏览器。

遵循本教程,并成为PHP网络刮擦的专家!让我们不要浪费更多的时间,并在php中建立我们的第一个刮刀。

先决条件

这是您需要使用的简单刮板所需的先决条件列表:

  • PHP >= 7.29.0
  • Composer >= 2 如果您在系统上没有安装这些,则可以按照上述链接下载。

然后,您还需要以下作曲家库:

  • koude0 >= 4.8 您可以使用以下命令将其添加到项目的依赖项中:
composer require voku/simple_html_dom

另外,您需要内置的cURL PHP库。 cURL带有curl-ext PHP扩展名,它在大多数PHP软件包中自动存在并启用。如果您安装的PHP软件包不包括curl-ext,则可以安装它,如this guide中所述。

现在让我们了解有关此处提到的依赖项的更多信息。

介绍

voku/simple_html_domSimple HTML DOM Parser项目的叉子,用DOMDocument和其他现代PHP类代替了字符串操纵。使用nearly two million installs voku/simple_html_dom是一个快速,可靠且简单的库,用于解析HTML文档并在PHP中执行网络刮擦。。

curl-ext是一个PHP扩展名,可在PHP中启用cURL HTTP client,它允许您在PHP中执行HTTP请求。

您可以在this GitHub repo中找到演示Web刮板的代码。克隆它并使用以下命令安装项目的依赖项:

git clone https://github.com/Tonel/simple-scraper-php 
cd simple-scraper-php 
composer update

遵循本教程,学习如何在PHP中构建Web Scraper应用程序!

PHP中的基本网络刮擦

在这里,您将看到如何在<https://scrapeme.live/shop/>上执行Web刮擦,这是一个设计为刮擦目标的网站。

详细说明,这是商店的样子:

ScrapeMe Homepage

您可以看到, scrapeme.live不过是口袋妖怪风格的产品的分页列表。让我们在PHP中构建一个简单的网络刮板,该网站爬网并从所有这些产品中刮擦数据。

首先,您需要下载要刮擦的页面的HTML 。您可以使用koude1中的PHP中下载HTML文档,如下所示:

// initialize the cURL request 
$curl = curl_init(); 
// set the URL to reach with a GET HTTP request 
curl_setopt($curl, CURLOPT_URL, "https://scrapeme.live/shop/"); 
// get the data returned by the cURL request as a string 
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
// make the cURL request follow eventual redirects, 
// and reach the final page of interest 
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 
// execute the cURL request and 
// get the HTML of the page as a string 
$html = curl_exec($curl); 
// release the cURL resources 
curl_close($curl);

您现在拥有https://scrapeme.live/shop/页面的HTML,存储在$html变量中。将其加载到带有str_get_html()函数的HtmlDomParser实例中:

require_once __DIR__ . "../../vendor/autoload.php"; 

use voku\helper\HtmlDomParser; 

$curl = curl_init(); 
curl_setopt($curl, CURLOPT_URL, "https://scrapeme.live/shop/"); 
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 
$html = curl_exec($curl); 
curl_close($curl); 

// initialize HtmlDomParser 
$htmlDomParser = HtmlDomParser::str_get_html($html);

您现在可以使用HtmlDomParser浏览HTML页面的DOM并启动数据提取。

现在,让我们检索所有分页链接的列表,以爬网 。右键单击分页号HTML元素,然后选择“ Inspect”选项。

DevTools' Inspect function

此时,浏览器应打开DevTools窗口或以DOM元素突出显示的部分,如下:

DevTools window

在WebTools窗口中,您可以看到page-numbers CSS类标识了分页HTML元素。 请注意,CSS类并不能唯一地识别HTML元素,并且许多节点可能具有相同的类。 恰恰是page-numbersscrapeme.live页中发生的事情。

因此,如果您想使用CSS selector在DOM中选择元素,则您应该将CSS类与其他选择器一起使用。特别是,您可以将HtmlDomParser.page-numbers a CSS选择器一起选择页面上的所有分页html元素。然后,遍历它们,以从href属性中提取所有必需的URL如下:

// retrieve the HTML pagination elements with 
// the ".page-numbers a" CSS selector 
$paginationElements = $htmlDomParser->find(".page-numbers a"); 
$paginationLinks = []; 
foreach ($paginationElements as $paginationElement) { 
    // populate the paginationLinks set with the URL 
    // extracted from the href attribute of the HTML pagination element 
    $paginationLink = $paginationElement->getAttribute("href"); 
    // avoid duplicates in the list of URLs 
    if (!in_array($paginationLink, $paginationLinks)) { 
        $paginationLinks[] = $paginationLink; 
    } 
} 

// print the paginationLinks array 
print_r($paginationLinks);

请注意,koude22功能允许您根据CSS选择器提取DOM元素。另外,考虑到分页元素被放置在网页上两次,您需要定义自定义逻辑,以避免重复元素$paginationLinks数组中。

如果执行,此脚本将返回:

Array ( 
    [0] => https://scrapeme.live/shop/page/2/ 
    [1] => https://scrapeme.live/shop/page/3/ 
    [2] => https://scrapeme.live/shop/page/4/ 
    [3] => https://scrapeme.live/shop/page/46/ 
    [4] => https://scrapeme.live/shop/page/47/ 
    [5] => https://scrapeme.live/shop/page/48/ 
)

如图所示,所有URL都遵循相同的结构,并以指定分页数的最终数字为特征。如果您想在所有页面上迭代,则只需要与最后一页关联的数字。检索如下:

// remove all non-numeric characters in the last element of 
// the $paginationLinks array to retrieve the highest pagination number 
$highestPaginationNumber = preg_replace("/\D/", "", end($paginationLinks));

$highestPaginationNumber将包含“ 48”。

现在,让我们检索与单个产品相关的数据。同样,右键单击产品,并使用“ Inspect”选项打开DevTools窗口。这就是您应该得到的:

DevTools window

您可以看到,产品由li.product HTML元素组成,其中包含 a URL,图像,名称和价格。该产品信息分别放置在amgh2span HTML元素中。您可以使用下面的HtmlDomParseras提取此数据:

$productDataLit = array(); 

// retrieve the list of products on the page 
$productElements = $htmlDomParser->find("li.product"); 
foreach ($productElements as $productElement) { 
    // extract the product data 
    $url = $productElement->findOne("a")->getAttribute("href"); 
    $image = $productElement->findOne("img")->getAttribute("src"); 
    $name = $productElement->findOne("h2")->text; 
    $price = $productElement->findOne(".price span")->text; 

    // transform the product data into an associative array 
    $productData = array( 
        "url" => $url, 
        "image" => $image, 
        "name" => $name, 
        "price" => $price 
    ); 

    $productDataList[] = $productData; 
}

此逻辑将所有产品数据提取在一个页面上,并将其保存在$productDataList数组中。

现在,您只需要在每个页面上迭代并应用上面定义的刮擦逻辑:

// iterate over all "/shop/page/X" pages and retrieve all product data 
for ($paginationNumber = 1; $paginationNumber <= $highestPaginationNumber; $paginationNumber++) { 
    $curl = curl_init(); 
    curl_setopt($curl, CURLOPT_URL, "https://scrapeme.live/shop/page/$paginationNumber/"); 
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 
    $pageHtml = curl_exec($curl); 
    curl_close($curl); 

    $paginationHtmlDomParser = HtmlDomParser::str_get_html($pageHtml); 

    // scraping logic... 
}

etvoilã!您刚刚学会了如何在PHP中构建简单的Web刮板!

如果您想查看脚本的整个代码,则可以找到here。运行它,您将检索以下数据:

[ 
    { 
        "url": "https://scrapeme.live/shop/Bulbasaur/", 
        "image": "https://scrapeme.live/wp-content/uploads/2018/08/001-350x350.png", 
        "name": "Bulbasaur", 
        "price": "£63.00" 
    }, 

    ... 

    { 
        "url": "https://scrapeme.live/shop/Blacephalon/", 
        "image": "https://scrapeme.live/wp-content/uploads/2018/08/806-350x350.png", 
        "name": "Blacephalon", 
        "price": "£149.00" 
    } 
]

恭喜!您只是自动提取所有产品数据!

避免被阻止

上面的示例使用了设计用于刮擦的网站。提取所有数据是小菜一碟,但不要被这个数据所迷惑!抓取网站并不总是那么容易,您的脚本可能会被拦截和阻止。找出如何防止这种情况发生!

有几种可能的防御机制可以防止脚本访问网站。这些技术试图根据其行为来识别来自非人类或恶意用户的请求,因此阻止了他们。

绕过所有这些反刮擦系统并不总是那么容易。但是,通常可以避免使用两种解决方案:常见 HTTP标头和Web代理。现在让我们仔细看看这两种方法。


沮丧的是,您的网络刮刀一次又一次被阻止?

Zenrows API为您处理旋转代理和无头浏览器。

Try for FREE


1.使用常见的HTTP标头模拟真实用户

许多网站阻止似乎不是来自真实用户的请求。另一方面,浏览器设置了一些HTTP标头。确切的标题从供应商变为供应商。因此,这些防剪crip胶系统希望这些标头存在。因此,您可以通过设置适当的HTTP标头来避免块。

具体来说,您应该始终设置的最关键的标头是User-Agent标头(此后,UA)。这是一个识别应用程序,操作系统,供应商和/或应用程序版本的字符串。

默认情况下,cURL发送了curl/XX.YY.ZZ ua标头,这使请求可识别为脚本。您可以用cURL手动将UA标头设置为:

curl_setopt($curl, CURLOPT_USERAGENT, "<USER_AGENT_STRING>");

示例:

curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")

此代码线设置了最新版本的Google Chrome当前使用的UA。它使卷曲的请求更难识别为来自脚本。

您可以在线找到有效,最新和受信任的UA标头的列表。在大多数情况下,设置HTTP UA标头足以避免被阻塞。如果这还不够,您可以使用cURL发送其他HTTP标头:

curl_setopt($curl, CURLOPT_HTTPHEADER, 
    array( 
        "<HTTP_HEADER_1>: <HEADER_VALUE>", 
        "<HTTP_HEADER_2>: <HEADER_VALUE>", 
        // ... 
    ) 
);

示例:

// set the Content-Language and Authorization HTTP headers 
curl_setopt($curl, CURLOPT_HTTPHEADER, 
    array( 
         "Content-Language: es", 
         "Authorization: 32b108le1HBuSYHMuAcCrIjW72UTO3p5X78iIzq1CLuiHKgJ8fB2VdfmcS", 
    ) 
);

找出Zenrows在setting custom headers时所能提供的。

2.使用Web代理隐藏您的IP

反刮擦系统倾向于阻止用户在短时间内访问许多页面。主要检查查看请求来自的IP。 如果相同的IP在短时间内提出了许多请求。换句话说,要防止IP上的块,您必须找到一种隐藏方法。

最好的方法之一是通过代理服务器。 Web代理是您的计算机和Internet上其余计算机之间的中介服务器。通过代理执行请求时,目标网站将看到代理服务器的IP地址而不是您的IP地址。

几个free proxies可在线获得,但大多数是短暂的,不可靠的,而且通常不可用。您可以使用它们进行测试。但是,您不应该依靠它们来制作脚本。

另一方面,付费的代理服务更可靠,并且通常带有IP旋转。这意味着代理服务器暴露的IP经常会随着时间或每个请求而变化。这使得服务提供的每个IP更难被禁止,即使发生这种情况,您也会很快获得新的IP。

Zenrows支持高级代理。了解有关如何将它们用于avoid blocks的更多信息。

您可以使用cURL设置Web代理如下:

curl_setopt($curl, CURLOPT_PROXY, "<PROXY_URL>"); 
curl_setopt($curl, CURLOPT_PROXYPORT, "<PROXY_PORT>"); 
curl_setopt($curl, CURLOPT_PROXYTYPE, "<PROXY_TYPE>");

示例:

curl_setopt($curl, CURLOPT_PROXY, "102.68.128.214"); 
curl_setopt($curl, CURLOPT_PROXYPORT, "8080"); 
curl_setopt($curl, CURLOPT_PROXY, CURLPROXY_HTTP);

使用大多数Web代理,在第一行中设置代理的URL就足够了。 CURLOPT_PROXYTYPE可以采用以下值:CURLPROXY_HTTP(默认),CURLPROXY_SOCKS4CURLPROXY_SOCKS5CURLPROXY_SOCKS4ACURLPROXY_SOCKS5_HOSTNAME

您刚刚学会了如何避免被阻止。现在让我们研究如何更快地使脚本!

平行刮擦

处理PHP中的多线程很复杂。几个库可以支持您,但是在PHP中执行并行刮擦的最有效解决方案不需要任何一个。

这种并行刮擦方法背后的想法是使刮擦脚本准备在多个实例上运行。使用HTTP获取参数可能。

考虑前面介绍的分页示例。 您可以修改脚本以在较小的块上工作,然后并行启动几个实例。

,您可以修改脚本以在较小的块上工作。

您要做的就是将一些参数传递给脚本以定义块的边界。

您可以通过引入两个获取参数如下:

$from = null; 
$to = null; 

if (isset($_GET["from"]) && is_numeric($_GET["from"])) { 
    $from = $_GET["from"]; 
} 

if (isset($_GET["to"]) && is_numeric($_GET["to"])) { 
    $to = $_GET["to"]; 
} 

if (is_null($from) || is_null($to) || $from > $to) { 
    die("Invalid from and to parameters!"); 
} 

// scrape only the pagination pages whose number goes 
// from "$from" to "$to" 
for ($paginationNumber = $from; $paginationNumber <= $to; $paginationNumber++) { 
    // scraping logic... 
} 

// write the data scraped to a database/file

现在,您可以通过在浏览器中打开这些链接来启动脚本的几个实例:

https://your-domain.com/scripts/scrapeme.live/scrape-products.php?from=1&to=5 
https://your-domain.com/scripts/scrapeme.live/scrape-products.php?from=6&to=10 
... 
https://your-domain.com/scripts/scrapeme.live/scrape-products.php?from=41&to=45 
https://your-domain.com/scripts/scrapeme.live/scrape-products.php?from=46&to=48

这些实例将并行运行,并同时刮擦网站。您可以找到此新版本的刮擦脚本here的整个代码。

,你有!您刚刚学会了通过网络刮擦并行从网站上提取数据。

能够并行刮擦网站已经是一个很大的改进,但是您可以在PHP Web Scraper中采用许多其他高级技术。让我们找出如何将您的网络刮擦脚本提升到一个新的级别。

高级技术

请记住,网页上并非所有感兴趣的数据都直接显示在浏览器中。 网页还包括元数据和隐藏元素。要访问此数据,右键单击网页的空部分,然后单击“查看页面源”。

ScrapeMe's source code

在这里,您可以看到网页的完整DOM,包括隐藏的元素。详细说明,您可以在koude43 中找到有关网页的元数据。此外,重要的隐藏数据可能存储在koude44 elements中。

同样,通过hidden HTML elements,页面上可能已经存在一些数据。仅当特定事件发生时,JavaScript才显示。即使您看不到页面上的数据,它仍然是DOM的一部分。因此,您可以像可见节点一样使用HtmlDomParser检索这些隐藏的HTML元素。

另外,请记住网页大于其源代码。网页可以在浏览器中向retrieve data asynchronously via AJAX提出请求,并相应地更新其DOM。这些Ajax调用通常提供有价值的数据,您可能需要从网络刮擦脚本中调用它们。

要嗅探这些电话,您需要使用浏览器的DevTools窗口。右键单击网站空白部分,选择“检查”,然后到达“网络”选项卡。在“ fetch/xhr”选项卡中,您可以看到网页执行的ajax调用列表,如下示例。

POST AJAX call

探索所选AJAX请求的所有内部选项卡,以了解如何执行AJAX调用。具体来说,您可以使用cURL复制此帖子Ajax调用如下:

$curl = curl_init($url); 
curl_setopt($curl, CURLOPT_URL, "https://www.w3schools.com/jquery/demo_test_post.asp"); 
// specify that the cURL request is a POST 
curl_setopt($curl, CURLOPT_POST, true); 
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 

// define the body of the request 
curl_setopt($curl, CURLOPT_POSTFIELDS, 
    // http_build_query is required to simulate 
    // a FormData request. Ignore it on a JSON request 
    http_build_query( 
        array( 
            "name" => "Donald Duck", 
            "city" => "Duckburg" 
        ) 
    ) 
); 

// define the body of the request 
curl_setopt($curl, CURLOPT_POSTFIELDS, 
    array( 
        "name" => "Donald Duck", 
        "city" => "Duckburg" 
    ) 
); 

// replicate the AJAX call 
$result = curl_exec($curl);

恭喜!您刚刚与cURL进行了帖子电话!

无头浏览器

嗅探和复制AJAX调用有助于通过用户互动而从加载的网站进行编程检索数据。此数据不是网页源代码的一部分,也无法在从标准get cURL请求获得的HTML元素中找到。

但是,复制所有可能的交互,嗅探Ajax调用并在脚本中调用它们是一种麻烦的方法。有时,您需要定义一个可以像人类用户一样通过JavaScript与页面交互的脚本。您可以通过headless browser实现此目标。

如果您不熟悉此概念,则无头浏览器是一个网络浏览器,没有图形用户界面,该浏览器通过代码提供了对网页的自动控件。提供无头浏览器功能的PHP中最受欢迎的库是koude49Selenium WebDriver。有关更多信息,请访问我们的Web Scraping with Selenium指南。

另外,Zenrows还提供Web浏览器功能。了解有关how to extract dynamically loaded data的更多信息。

其他库

您在网络刮擦方面可能会采用的其他有用的PHP库是:

  • Guzzle:一个高级HTTP客户端,可以发送HTTP请求,并且与Web Services集成是微不足道的。您可以将其用作cURL的替代方案。
  • Goutte:一个网络刮擦库,提供用于爬网网站并从其HTML网页中提取数据的高级API。由于它还包括HTTP客户端,因此您可以将其用作voku/simple_html_domcURL的替代方案。 ##结论{#conclusion} 在这里,您了解了有关在PHP中进行网络刮擦的所有知识,从基本爬行到高级技术。如上所示,在PHP中构建网络刮板,可以爬网并自动提取数据并不困难

您只需要正确的库,在这里我们查看了一些最受欢迎的库。

此外,您的Web刮板应该能够绕过反刮擦系统,并且可能必须检索隐藏的数据或像人类用户一样与网页进行交互。在本教程中,您也学会了如何完成所有这些。

如果您喜欢这个,请看一下JavaScript web scraping guide

感谢您的阅读!我们希望您对此教程有所帮助。随意提出问题,评论或建议。

您发现内容有帮助吗?在TwitterLinkedInFacebook上分享单词并在Twitter上共享。

本文最初发表在Zenrows上:Web Scraping with PHP: Step-By-Step Tutorial