网络刮擦已经变得越来越流行,现在是IT社区中的一个热门话题。结果,几个库可帮助您从网站上刮擦数据。在这里,您将学习如何使用最受欢迎的网络刮擦库之一在PHP中构建网络刮板。
在本教程中,您将学习php中网络刮擦的基础知识。然后,如何解决最受欢迎的反刮擦系统,学习更多高级技术和概念,例如平行刮擦和无头浏览器。
遵循本教程,并成为PHP网络刮擦的专家!让我们不要浪费更多的时间,并在php中建立我们的第一个刮刀。
先决条件
这是您需要使用的简单刮板所需的先决条件列表:
然后,您还需要以下作曲家库:
- koude0 >= 4.8 您可以使用以下命令将其添加到项目的依赖项中:
composer require voku/simple_html_dom
另外,您需要内置的cURL
PHP库。 cURL
带有curl-ext
PHP扩展名,它在大多数PHP软件包中自动存在并启用。如果您安装的PHP软件包不包括curl-ext
,则可以安装它,如this guide中所述。
现在让我们了解有关此处提到的依赖项的更多信息。
介绍
voku/simple_html_dom
是Simple 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.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窗口或以DOM元素突出显示的部分,如下:
在WebTools窗口中,您可以看到page-numbers
CSS类标识了分页HTML元素。 请注意,CSS类并不能唯一地识别HTML元素,并且许多节点可能具有相同的类。 恰恰是page-numbers
在scrapeme.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窗口。这就是您应该得到的:
您可以看到,产品由li.product
HTML元素组成,其中包含 a URL,图像,名称和价格。该产品信息分别放置在a
,mg
,h2
和span
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为您处理旋转代理和无头浏览器。
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_SOCKS4
,CURLPROXY_SOCKS5
,CURLPROXY_SOCKS4A
或CURLPROXY_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中采用许多其他高级技术。让我们找出如何将您的网络刮擦脚本提升到一个新的级别。
高级技术
请记住,网页上并非所有感兴趣的数据都直接显示在浏览器中。 网页还包括元数据和隐藏元素。要访问此数据,右键单击网页的空部分,然后单击“查看页面源”。
。在这里,您可以看到网页的完整DOM,包括隐藏的元素。详细说明,您可以在koude43 中找到有关网页的元数据。此外,重要的隐藏数据可能存储在koude44 elements中。
同样,通过hidden HTML elements,页面上可能已经存在一些数据。仅当特定事件发生时,JavaScript才显示。即使您看不到页面上的数据,它仍然是DOM的一部分。因此,您可以像可见节点一样使用HtmlDomParser
检索这些隐藏的HTML元素。
另外,请记住网页大于其源代码。网页可以在浏览器中向retrieve data asynchronously via AJAX提出请求,并相应地更新其DOM。这些Ajax调用通常提供有价值的数据,您可能需要从网络刮擦脚本中调用它们。
要嗅探这些电话,您需要使用浏览器的DevTools窗口。右键单击网站空白部分,选择“检查”,然后到达“网络”选项卡。在“ fetch/xhr”选项卡中,您可以看到网页执行的ajax调用列表,如下示例。
探索所选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中最受欢迎的库是koude49和Selenium 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_dom
和cURL
的替代方案。 ##结论{#conclusion} 在这里,您了解了有关在PHP中进行网络刮擦的所有知识,从基本爬行到高级技术。如上所示,在PHP中构建网络刮板,可以爬网并自动提取数据并不困难。
您只需要正确的库,在这里我们查看了一些最受欢迎的库。
此外,您的Web刮板应该能够绕过反刮擦系统,并且可能必须检索隐藏的数据或像人类用户一样与网页进行交互。在本教程中,您也学会了如何完成所有这些。
如果您喜欢这个,请看一下JavaScript web scraping guide。
感谢您的阅读!我们希望您对此教程有所帮助。随意提出问题,评论或建议。
您发现内容有帮助吗?在Twitter,LinkedIn和Facebook上分享单词并在Twitter上共享。
本文最初发表在Zenrows上:Web Scraping with PHP: Step-By-Step Tutorial