gpt4 book ai didi

php - 使用 pcntl_fork() 提高 HTML 抓取效率

转载 作者:行者123 更新时间:2023-12-03 19:22:06 27 4
gpt4 key购买 nike

在前两个问题的帮助下,我现在有了一个可以工作的 HTML 抓取工具,可以将产品信息输入数据库。我现在想做的是通过让我的刮刀与 pcntl_fork 一起工作来提高效率。 .

如果我将 php5-cli 脚本分成 10 个单独的 block ,我会在很大程度上提高总运行时间,因此我知道我不受 i/o 或 cpu 限制,而只是受到抓取函数的线性性质的限制。

使用我从多个来源拼凑而成的代码,我进行了以下工作测试:

<?php
libxml_use_internal_errors(true);
ini_set('max_execution_time', 0);
ini_set('max_input_time', 0);
set_time_limit(0);

$hrefArray = array("http://slashdot.org", "http://slashdot.org", "http://slashdot.org", "http://slashdot.org");

function doDomStuff($singleHref,$childPid) {
$html = new DOMDocument();
$html->loadHtmlFile($singleHref);

$xPath = new DOMXPath($html);

$domQuery = '//div[@id="slogan"]/h2';
$domReturn = $xPath->query($domQuery);

foreach($domReturn as $return) {
$slogan = $return->nodeValue;
echo "Child PID #" . $childPid . " says: " . $slogan . "\n";
}
}

$pids = array();
foreach ($hrefArray as $singleHref) {
$pid = pcntl_fork();

if ($pid == -1) {
die("Couldn't fork, error!");
} elseif ($pid > 0) {
// We are the parent
$pids[] = $pid;
} else {
// We are the child
$childPid = posix_getpid();
doDomStuff($singleHref,$childPid);
exit(0);
}
}

foreach ($pids as $pid) {
pcntl_waitpid($pid, $status);
}

// Clear the libxml buffer so it doesn't fill up
libxml_clear_errors();

这提出了以下问题:

1) 鉴于我的 hrefArray 包含 4 个 url - 如果该数组包含 1,000 个产品 url,此代码将生成 1,000 个子进程?如果是这样,将进程数量限制为 10 个的最佳方法是什么,再次以 1,000 个 url 为例,将子工作负载拆分为每个子 100 个产品 (10 x 100)。

2)我了解到 pcntl_fork 创建了流程以及所有变量、类等的副本。我想做的是将我的 hrefArray 变量替换为 DOMDocument 查询,该查询构建要抓取的产品列表,并且然后将它们提供给子进程进行处理 - 将负载分散到 10 个子进程中。

我的大脑告诉我需要做如下的事情(显然这不起作用,所以不要运行它):

<?php
libxml_use_internal_errors(true);
ini_set('max_execution_time', 0);
ini_set('max_input_time', 0);
set_time_limit(0);
$maxChildWorkers = 10;

$html = new DOMDocument();
$html->loadHtmlFile('http://xxxx');
$xPath = new DOMXPath($html);

$domQuery = '//div[@id=productDetail]/a';
$domReturn = $xPath->query($domQuery);

$hrefsArray[] = $domReturn->getAttribute('href');

function doDomStuff($singleHref) {
// Do stuff here with each product
}

// To figure out: Split href array into $maxChilderWorks # of workArray1, workArray2 ... workArray10.
$pids = array();
foreach ($workArray(1,2,3 ... 10) as $singleHref) {
$pid = pcntl_fork();

if ($pid == -1) {
die("Couldn't fork, error!");
} elseif ($pid > 0) {
// We are the parent
$pids[] = $pid;
} else {
// We are the child
$childPid = posix_getpid();
doDomStuff($singleHref);
exit(0);
}
}


foreach ($pids as $pid) {
pcntl_waitpid($pid, $status);
}

// Clear the libxml buffer so it doesn't fill up
libxml_clear_errors();

但是我不知道如何仅在主/父进程中构建我的 hrefsArray[] 并将其提供给子进程。目前我尝试过的所有操作都会导致子进程中出现循环。 IE。我的 hrefsArray 是在主进程以及每个后续子进程中构建的。

我确信我所做的一切都是完全错误的,所以非常感谢在正确的方向上进行一般性的插入。

最佳答案

简介

pcntl_fork()并不是提高性能的唯一方法HTML scraper虽然使用 Message Queue 可能是个好主意有Charles建议,但您仍然需要一种更快有效的方法来将该请求拉到您的 workers

解决方案1

使用curl_multi_init ... curl 实际上更快,并且使用多重 curl 可以并行处理

来自 PHP 文档

curl_multi_init Allows the processing of multiple cURL handles in parallel.

所以不要使用 $html->loadHtmlFile('http://xxxx');要多次加载文件,您可以使用 curl_multi_init同时加载多个url

这里有一些有趣的实现

解决方案2

您可以使用pthreadsPHP 中使用多线程

示例

// Number of threads you want
$threads = 10;

// Treads storage
$ts = array();

// Your list of URLS // range just for demo
$urls = range(1, 50);

// Group Urls
$urlsGroup = array_chunk($urls, floor(count($urls) / $threads));

printf("%s:PROCESS #load\n", date("g:i:s"));

$name = range("A", "Z");
$i = 0;
foreach ( $urlsGroup as $group ) {
$ts[] = new AsyncScraper($group, $name[$i ++]);
}

printf("%s:PROCESS #join\n", date("g:i:s"));

// wait for all Threads to complete
foreach ( $ts as $t ) {
$t->join();
}

printf("%s:PROCESS #finish\n", date("g:i:s"));

输出

9:18:00:PROCESS  #load
9:18:00:START #5592 A
9:18:00:START #9620 B
9:18:00:START #11684 C
9:18:00:START #11156 D
9:18:00:START #11216 E
9:18:00:START #11568 F
9:18:00:START #2920 G
9:18:00:START #10296 H
9:18:00:START #11696 I
9:18:00:PROCESS #join
9:18:00:START #6692 J
9:18:01:END #9620 B
9:18:01:END #11216 E
9:18:01:END #10296 H
9:18:02:END #2920 G
9:18:02:END #11696 I
9:18:04:END #5592 A
9:18:04:END #11568 F
9:18:04:END #6692 J
9:18:05:END #11684 C
9:18:05:END #11156 D
9:18:05:PROCESS #finish

使用的类

class AsyncScraper extends Thread {

public function __construct(array $urls, $name) {
$this->urls = $urls;
$this->name = $name;
$this->start();
}

public function run() {
printf("%s:START #%lu \t %s \n", date("g:i:s"), $this->getThreadId(), $this->name);
if ($this->urls) {
// Load with CURL
// Parse with DOM
// Do some work

sleep(mt_rand(1, 5));
}
printf("%s:END #%lu \t %s \n", date("g:i:s"), $this->getThreadId(), $this->name);
}
}

关于php - 使用 pcntl_fork() 提高 HTML 抓取效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2861969/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com