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

2023-12-01

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

如果我将 php5-cli 脚本分成 10 个单独的块,我会在很大程度上提高总运行时间,因此我知道我不受 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 has Charles建议,但您仍然需要一种更快有效的方法来将该请求拉入您的workers

解决方案1

Use curl_multi_init...卷曲实际上更快,并且使用多重卷曲可以让您并行处理

来自 PHP 文档

卷曲_多重_初始化允许并行处理多个 cURL 句柄。

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

这里有一些有趣的实现

  • php - 检查许多域(1000 以上)中文本是否存在的最快方法
  • php 从 url 获取所有宽度和高度 >=200 的图像更快
  • PHP中Curl请求时如何防止服务器过载

解决方案2

您可以使用pthreads使用多线程PHP

Example

// 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"));

Output

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);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 pcntl_fork() 提高 HTML 抓取效率 的相关文章

  • 在 Codeigniter 中显示来自连接的数据

    我正在使用简单的联接从两个数据库中提取数据 这是模型中的连接 function com control this gt db gt select this gt db gt from comments this gt db gt join
  • R 语言与 php 集成以获取 R 的结果

    我有以下 R 脚本 assign data path data path lt C Users Owner Desktop R work assign valus to the following three percent train p
  • Paypal Rest API - 来自批准 URL 的令牌生命周期

    我使用 Paypal Rest API 我的问题是 有多长token来自批准 URL 有效吗 我想将此令牌 也包含我的令牌 存储到数据库并生成带有我的令牌的链接 稍后 如果我单击此链接 将我的令牌替换为 paypal 令牌 我想重定向到 p
  • PHP mysql 选择连接

    我有这个功能 以某种形式显示自动建议 function searchbyId params input strtolower params input len strlen input limit isset params limit in
  • 如何在codeigniter中将数据写入.txt文件

    我的资产中有一个文件夹名称是login 我的疑问是如何设置路径 data id expense type amount exp date br todate date Y m d echo todate if write file asse
  • Laravel 无法连接 Oracle

    我在用着耶吉拉 laravel oci8 https github com yajra laravel oci8用于 Oracle 与 Laravel 的连接 但我无法从客户端 PC 连接到 Oracle 服务器 showing this
  • 如何将 php 变量传递给 ajax

    我有一个自动加载页面 我需要能够基于变量检索数据 因为该变量必须返回特定值 下面的代码基于检索所有数据 但我只需要基于 list 的少数数据 Page php
  • 通过 preg_match_all PHP 函数从 html 代码字符串中提取 img 标签

    我有一些 html 代码并提取了img src来自它的属性 html 字符串中有一些像这样的 img img src http www pecso it wp content uploads 2016 12 10 WRAS png 我尝试使
  • Paypal PDT交易ID有效期

    当我尝试使用交易 ID 检索付款信息时 我从 paypal PDT 收到错误 4003 虽然我这里有一个类似的线程 贝宝 PDT 错误 4003 https stackoverflow com questions 8521800 paypa
  • Laravel/PHP - 从子类返回/重定向

    这是我的子控制器 class VolunteersController extends BaseController public function index this gt checkForRoles admin list some s
  • 调用另一个 PHP 脚本并在其他脚本完成之前将控制权返回给用户

    我实际上正在尝试将其应用于发送电子邮件的脚本 发送电子邮件部分需要几秒钟 这太长了 我想要的是第一个脚本执行其操作并触发另一个脚本 发送电子邮件 但我希望第一个脚本将控制权返回给用户 而无需等待第二个脚本发送电子邮件 我考虑过的选项 计划任
  • Javascript crc32 函数和 PHP crc32 与 UTF8 不匹配

    我一直在尝试从 PHP 获取 crc32 函数来匹配 javascript 生成的结果 我已经使用了 4 个不同的 javascript crc32 库 1 http www webtoolkit info javascript crc32
  • 使用 Composer CLI 将数据添加到额外属性

    根据文档extra的财产composer json 架构 https getcomposer org doc 04 schema md extra 允许设置 供脚本使用的任意额外数据 出于脚本目的 如果可以将数据添加到extra通过命令行属
  • PHP crypt() Blowfish 函数不工作

    这是我第一次在 PHP 中使用 crypt 函数 我不明白为什么它不起作用 我的代码基于这篇文章 http www techrepublic com blog australia secure passwords with blowfish
  • 从数据库结果生成多维数组的递归函数

    我正在编写一个函数 它接受页面 类别数组 来自平面数据库结果 并根据父 ID 生成嵌套页面 类别项目数组 我想递归地执行此操作 以便可以完成任何级别的嵌套 例如 我在一个查询中获取所有页面 这就是数据库表的样子 id parent id t
  • 如何将 ZF2 单元/应用程序模块测试合并到单个调用中?

    我遵循将测试存储在模块中的 ZF2 约定 并且当从每个模块内运行测试时一切正常 我想做的是有一个根级别的 phpunit xml 来调用各个模块测试并将它们合并以生成代码覆盖率数据和其他指标 问题是每个单独的测试套件都是在模块化 phpun
  • PHP json_encode json_decode UTF-8

    如何将包含国际字符的 json 编码字符串保存到数据库 然后在浏览器中解析解码后的字符串
  • PHP 指针与引用

    在 PHP 中 使用指针有什么区别 例如 function foo var var 3 a 0 foo a 以及参考 function foo var var 3 a 0 foo a 它们都修改了原始变量的值 但是它们内部的表示方式不同吗
  • 致命错误:找不到类“App\PDO”

    当我尝试使用命名空间时出现此错误 I have namespace App 顶部 班级看起来像 class database function construct try this gt db new PDO lt here the err
  • PHP MySQLi 多次插入

    我想知道准备好的语句是否与具有多个值的普通 mysql query 一样工作 INSERT INTO table a b VALUES a b c d VS sql db gt prepare INSERT INTO table a b V

随机推荐

  • ggplot2 PDF 输出中的 Unicode 字符

    如何在使用 ggplot2 创建的 PDF 绘图中使用 Unicode 字符作为标签 标题和类似内容 考虑以下示例 library ggplot2 qplot Sepal Length Petal Length data iris main
  • Python PyQT:如何从工作线程调用 GUI 函数?

    我有一个 pyqt gui 并调用一个长进程 ffmpeg 我将其放在一个单独的线程上以不阻塞 gui 然后 我想在较长命令列表中的一个命令完成时更新进度栏 问题是 我无法在工作线程之外调用 gui 线程中的函数 所以我让在工作线程中运行一
  • JCAPS 地狱小盒子

    Has anyone out there in SO land had to deal with this when you go to build a new deployment profile in JCAPS you have qu
  • avcodec_encode_video2 时出现分段错误

    我在尝试将 AVFrame 编码到数据包时遇到一些问题 在阅读整个代码之前 输入的东西正在工作 我测试了它 输出内容来自示例here 我认为是有问题的 但分段错误发生在接近末尾的循环中 这是我的简化代码 void nmain input s
  • 如何从列表中查找对象

    我使用以下程序创建从网站获取的城市列表 现在我想从我创建的列表中查找城市名称 参数 我怎么做 换句话说 如何从列表中找到对象 我试过 listOfCities find city 我收到错误 因为找不到属性 find def weather
  • 依赖于 package.json 中使用 git URL 的分支或标签?

    假设我已经分叉了一个带有错误修复程序的节点模块 并且我想在功能分支上使用我的修复版本 直到错误修复程序合并并发布 我如何在中引用我的固定版本dependencies of my package json 解决方案1 来自npm docs 使
  • 无法在 javafx 8 中加载 css 文件

    Scene listenMenu new Scene root 250 272 listenMenu getStylesheets add styles css 这对于我加载 css 文件总是有效 但是在一次小的 IntelliJ 更新之后
  • Parallel.ForEach - 访问修改后的闭包是否适用?

    我读过许多有关访问修改的闭包的其他问题 所以我了解基本原理 尽管如此 我还是无法判断 是吗 Parallel ForEach有同样的问题吗 以下面的代码片段为例 我重新计算了上周用户的使用情况统计数据 var startTime DateT
  • 我怎样才能修复反应中的路线错误

    我收到此警告 除了 App js 中包含 默认页面内容 的 之外 我看不到我的路线 我如何重写包含其他路线的代码 位置 admin 处的匹配叶路由没有元素 这意味着默认情况下它将呈现一个空值 从而导致 空 页面 我在用着 反应 17 0 2
  • for-each 内的方案延续

    我目前正在大学学习一门课程的计划 在查看一些练习时 我陷入了这个特定的练习中 教授尚未回复我之前的邮件 因此我有更多机会更快地收到答复 鉴于此代码 define list iter cc lst call cc lambda return
  • cin 的 isdigit 问题

    我有一个有符号整数变量 当我在 main 中执行此操作时 它也会给我整数值 错误 int main unsigned int a while cin gt gt a if isdigit a cout lt lt Error lt
  • ios NSError 类型

    自从我添加了这个async请求 我收到 xcode 错误Sending NSError const strong to parameter of type NSError autoreleasing changes retain relea
  • Rshinydashboard-根据用户输入显示/隐藏多个菜单项

    这个想法是有一个用户输入 访问代码 基于该输入可以访问不同的菜单项 所以基本上我们可以根据用户的要求提供自定义版本的应用程序 3 个菜单项的工作示例如下 library shiny library shinydashboard librar
  • 无法在 Java 应用程序中连接到 SQLServer 数据库

    我已在尝试开发的 JSF Web 应用程序中连接到我的数据库 使用 Windows 7 NetBeans 6 9 1 SQLServer 2008 Glassfish 3 3 我正在使用sqljdbc4 jar我从 XP 系统复制了驱动程序
  • 使用 Java 访问嵌套 JSON 对象的最佳方法

    我是使用 JSON 的新手 我想知道是否有更好的方法来完成我在下面的代码中所做的事情 您会注意到 要访问嵌套的 JSON 对象 我必须先创建子 JSON 对象 数组 然后才能访问 JSON 数组元素 leagues 有没有更快或更简单的方法
  • 使用 volatile long 作为原子

    如果我有这样的事情 volatile long something global 0 long some public func return something global 当使用多个线程访问时 期望此代码不会中断 竞争条件 是否合理
  • 为什么 pyautogui 点击实际上没有点击

    我尝试使用 Pyautogui 的单击功能 但实际单击没有发生 或者至少页面没有变化 尽管它将鼠标移动到正确的位置 该窗口处于焦点位置 我认为 因为该程序与其他页面配合得很好 我只能找到一个相关问题 单击程序时遇到问题 pyautogui
  • 当应用程序在 Xcode 中停止时自动关闭 iOS 模拟器

    每当 Xcode 中的应用程序停止时 是否可以让 iOS 模拟器关闭 退出 我无法在 Xcode 或模拟器中找到执行此操作的设置 如果存在的话 它将有助于加快开发进程 要在构建停止时终止模拟器 您需要编译一个可执行文件 包括以下内容 bin
  • 如何在Windows Phone中对在线内容进行数据虚拟化?

    我有一个从互联网获取内容的应用程序 这些信息太大了 无法容纳在内存中 LongListSelector does UI Virtualization出色地 遗迹Data Virtualization 我认为解决方案是先将数据保存在数据库中然
  • 使用 pcntl_fork() 提高 HTML 抓取效率

    在前两个问题的帮助下 我现在有了一个可以工作的 HTML 抓取工具 可以将产品信息输入数据库 我现在想做的是通过让我的刮刀工作来提高效率pcntl fork 如果我将 php5 cli 脚本分成 10 个单独的块 我会在很大程度上提高总运行