Загрузка в несколько потоков с multi_curl и PHP

Когда нужно скачать сотню страниц, то можно обойтись моим компонентом Browser. Но недавно мне надо было скачать столько информации, что нужно было ждать неделю. Проблема в том, что компонент загружает страницы по очереди. Пришлось придумывать как заставить его качать в несколько потоков.

Оказалось, что самое простое решение - это multi_curl. Но его на форумах очень критикуют за то, что он ест слишком много памяти, глючит и вообще безобразничает. Есть ещё вариант - неблокирующие сокеты, но мне они показались более сложными. Ну, а самое правильное, решение - это pkg_delete php, pkg_add perl :) Потому что Perl для этого задуман, а PHP - нет. Но ради единоразовой операции, хоть и длительной, пересиливать свою необъяснимую нелюбовь к Perl я не стал. Тем более пишут, что если ставить немного потоков (до 100), то multi_curl будет нормально работать.

К счастью, я не сторонник выдумывания велосипедов, поэтому взял библиотеку Вадима Тимофеева для работы с multi_curl. Чтобы она работала с компонентом Browser, скачайте последнюю версию, разархивируйте файл MultiCurl.class.php в папку vendors вашего CakePHP-проекта и переименуйте его в multi_curl.php.

Папка vendors в CakePHP позволяет использовать сторонние разработки, которые не созданы специально для CakePHP. Эти файлы подключаются с помощью функции vendor()

Класс MultiCurl довольно интересно реализован. Он является абстрактным классом. Чтобы использовать его в своей программе, надо создать его наследника, в котором переопределить событие, которое происходит при загрузке. Вот что пришлось дописать в начало компонента Browser.

PHP:
  1. vendor('multi_curl');
  2. class BrowserComponentMultiCurl extends MultiCurl {
  3.     var $Browser = null;
  4.  
  5.     protected function onLoad($url, $content, $info) {
  6.         $s = (serialize($info) . "\r\n\r\n" . $content);
  7.         file_put_contents($this->Browser->_getCacheFilename($url), $s);
  8.     }
  9. }

Тут необходимо пояснить принцип работы multi_curl. При скачивании в несколько потоков multi_curl ставит в очередь закачки несколько страниц и возращает управление основной программе только после того как закачает все страницы. Но после закачки каждого файла выполняется callback-функция.

Я решил упростить себе задачу и сделал вместо метода getMulti($urls), метод cacheMulti($urls). Поэтому не забудьте созадать папку для кеширования - APP/tmp/cache/browser

PHP:
  1. /**
  2. * Downloads URLs in multiple threads
  3. *
  4. * @param array $urls a(url1, url2, ...)
  5. * @return boolean
  6. */
  7. function cacheMulti($urls) {
  8.     try {
  9.         $mc = new BrowserComponentMultiCurl();
  10.         $mc->Browser = $this;
  11.         $mc->setMaxSessions(5); // limit 5 parallel sessions (by default 10)
  12.         //$mc->setMaxSize(10240); // limit 10 Kb per session (by default 10 Mb)
  13.  
  14.         foreach ($urls as $url) {
  15.             if (!$this->_isCached($url)) {
  16.                 $mc->addUrl($url);
  17.             }
  18.         }
  19.         $mc->wait();
  20.     } catch (Exception $e) {
  21.         // dirty style, but good enough for my tasks
  22.         echo('<h3 style="color:red">'.$e->getMessage().'</h3>');
  23.         @flush();ob_flush();
  24.     }
  25.  
  26.     return true;
  27. }

Из основной программы этот метод я хотел вызвать сначала так:

PHP:
  1. $this->Browser->cacheMulti($urls);
  2. foreach ($urls as $url) {
  3.     $html = $this->Browser->get($url); // уже закешировано, поэтому выдаст сразу
  4.  
  5.     // process $html
  6. }

Но не тут то было. Так как URLов действительно очень много, то ни Firefox, ни Lynx (я сервисные скрипты запускаю часто через него) не могли дождаться ответа от сервера и писалось 408 Request Timeout. Тогда я добавил в httpd.conf настройку Timeout 300000. Ошибка 408 Request Timeout всё равно показывалась, но насколько я понял, Apache продолжал выполнять скрипт ещё 300000 секунд. Я не нашёл правильного решения этой проблемы, поэтому немного подкорректировал скрипт, разбив список URLов на части.

PHP:
  1. $queueLength = 30;
  2. for ($i=0; $i<count($urls); $i+=$queueLength) {
  3.     $queue = array_slice($urls, $i, $queueLength);
  4.     $this->Browser->cacheMulti($queue);
  5.  
  6.     foreach ($queue as $url) {
  7.         $html = $this->Browser->get($url); // уже закешировано, поэтому выдаст сразу
  8.  
  9.         // process $html
  10.     }
  11.        
  12.     echo(((!empty($i)?', ':'')) . $i); // показываем обработанный номер
  13.     @flush();ob_flush(); // принудительное отображение. по одной работать не хотят. собака в начале строки - неизбежное зло
  14.     usleep(500); // нечего пригружать сильно чужие сервера - забанят
  15. }

Можно было бы поставить обработку информации в onLoad, но мне так удобнее :)

Вот полная версия обновлённого компонента Browser

PHP:
  1. <?
  2.  
  3. vendor('multi_curl');
  4. class BrowserComponentMultiCurl extends MultiCurl {
  5.     var $Browser = null;
  6.  
  7.     protected function onLoad($url, $content, $info) {
  8.         $s = (serialize($info) . "\r\n\r\n" . $content);
  9.         file_put_contents($this->Browser->_getCacheFilename($url), $s);
  10.     }
  11. }
  12.  
  13. /**
  14. * Emulation of browser
  15. *
  16. * @version 1.3 (24 Oct 2007)
  17. * @author Vladimir Luchaninov - http://php.southpark.com.ua
  18. *
  19. */
  20. class BrowserComponent extends Object {
  21.     var $handle;
  22.     var $header;
  23.     var $body;
  24.  
  25.     /**
  26.      * Name of browser you want to emulate. If 'random' then it will select from the large list.
  27.      *
  28.      * @var string
  29.      */
  30.     var $userAgent = 'random';
  31.  
  32.     // if you need http auth
  33.     var $username = null;
  34.     var $password = null;
  35.  
  36.     var $proxy = ''; // 'ip:port'
  37.     var $referer = 'http://www.google.com/';
  38.     var $timeout = 30;
  39.  
  40.     /**
  41.      * if you want to cache your requests you need to create folder APP/tmp/cache/browser
  42.      *
  43.      * @var string
  44.      */
  45.     var $cacheFolder = null;
  46.  
  47.     var $symbolsNotFile = array( '~''!''@''#''http://', '/'"\\", ':''*''?''"''<''>''|');
  48.     var $symbolsFile = array('~~', '!!', '@@', '##', '#~',      '~!', '~@', '~#', '!~', '!@', '!#', '@~', '@!', '@#'); // still reserved '#!', '#@'
  49.  
  50.     /**
  51.      * Init handle for connection
  52.      *
  53.      * @param AppController $controller
  54.      */
  55.     function startup(&$controller) {
  56.         $cacheFolder = APP . 'tmp' . DS . 'cache' . DS . 'browser' . DS;
  57.         if (is_dir($cacheFolder)) {
  58.             $this->cacheFolder = $cacheFolder;
  59.         }
  60.  
  61.         $this->_initUserAgent();
  62.  
  63.         $this->handle = curl_init();
  64.     }
  65.  
  66.     /**
  67.      * Convert URL to the filename for caching
  68.      *
  69.      * @param string $url Like http://php.southpark.com.ua
  70.      * @return string Filename of the cache file (withour full path)
  71.      */
  72.     function urlToFilename($url) {
  73.         return r($this->symbolsNotFile, $this->symbolsFile, $url).'.txt';
  74.     }
  75.  
  76.     /**
  77.      * Convert filename from cache to URL
  78.      *
  79.      * @param string $filename Filename of cached file (without full path)
  80.      * @return string URL
  81.      */
  82.     function filenameToUrl($filename) {
  83.         return r($this->symbolsFile, $this->symbolsNotFile, substr($filename, 0, strlen($filename)-4));
  84.     }
  85.  
  86.     /**
  87.      * Extract header and body from response to $this->header and $this->body
  88.      *
  89.      * @param string $response
  90.      * @return string
  91.      */
  92.     function _setHeaderBody($response) {
  93.         // You should see responses from some strange web-services
  94.         // Check for \r\n\r\n is really not enough
  95.         $regex = '/(.*?)\n[\r\n]*?\n+(.*)/sm';
  96.  
  97.         $this->header = '';
  98.         if (!preg_match($regex, $response, $m)) {
  99.             $this->body = $response;
  100.         } else {
  101.             $this->header = $m[1];
  102.             $this->body = ltrim($m[2], "\r");
  103.  
  104.             // sometimes there are several headers
  105.             while (strpos($this->body, 'HTTP/')===0 && preg_match($regex, $this->body, $m)) {
  106.                 $this->header .= "\n\n" . $m[1];
  107.                 $this->body = ltrim($m[2], "\r");
  108.             }
  109.         }
  110.  
  111.         return true;
  112.     }
  113.  
  114.     /**
  115.      * Get cache file filename for $url if possible. Otherwise null
  116.      *
  117.      * @param string $url Like http://php.southpark.com.ua
  118.      * @return string Cache file filename with full path
  119.      */
  120.     function _getCacheFilename($url) {
  121.         if (!empty($this->cacheFolder) && empty($postvars)) {
  122.             return $this->cacheFolder . $this->urlToFilename($url);
  123.         } else {
  124.             return null;
  125.         }
  126.     }
  127.  
  128.     /**
  129.      * Check if $url is already downloaded and saved to cache file
  130.      *
  131.      * @param string $url Like http://php.southpark.com.ua
  132.      * @return boolean True if $url exist in cache
  133.      */
  134.     function _isCached($url) {
  135.         $cacheFile = $this->_getCacheFilename($url);
  136.  
  137.         return (!empty($cacheFile) && file_exists($cacheFile));
  138.     }
  139.  
  140.     /**
  141.      * List all cached URLs
  142.      *
  143.      * @return array a(url1, url2, ...)
  144.      */
  145.     function getCachedUrls() {
  146.         $folder = new Folder($this->cacheFolder);
  147.         $files = $folder->find('.*\.txt');
  148.  
  149.         $urls = array();
  150.         foreach ($files as $filename) {
  151.             $urls[] = $this->filenameToUrl($filename);
  152.         }
  153.  
  154.         return $urls;
  155.     }
  156.  
  157.     /**
  158.      * Main function
  159.      *
  160.      * @param string $url
  161.      * @param array $postvars
  162.      * @return string body
  163.      * after execution $this->header is accessible if needed
  164.      */
  165.     function get($url, $postvars=null) {
  166.         $cacheFile = $this->_getCacheFilename($url);
  167.  
  168.         if ($this->_isCached($url)) {
  169.             $response = file_get_contents($cacheFile);
  170.         } else {
  171.             $this->prepare($url, $postvars);
  172.             $response = curl_exec($this->handle);
  173.             if (!empty($cacheFile)) {
  174.                 file_put_contents($cacheFile, $response);
  175.             }
  176.         }
  177.         $this->referer = $url;
  178.  
  179.         $this->_setHeaderBody($response);
  180.  
  181.         return $this->body;
  182.     }
  183.  
  184.     /**
  185.      * Downloads URLs in multiple threads
  186.      *
  187.      * @param array $urls a(url1, url2, ...)
  188.      * @return boolean
  189.      */
  190.     function cacheMulti($urls) {
  191.         try {
  192.             $mc = new BrowserComponentMultiCurl();
  193.             $mc->Browser = $this;
  194.             $mc->setMaxSessions(5); // limit 5 parallel sessions (by default 10)
  195.             //$mc->setMaxSize(10240); // limit 10 Kb per session (by default 10 Mb)
  196.  
  197.             foreach ($urls as $url) {
  198.                 if (!$this->_isCached($url)) {
  199.                     $mc->addUrl($url);
  200.                 }
  201.             }
  202.             $mc->wait();
  203.         } catch (Exception $e) {
  204.             // dirty style, but good enough for my tasks
  205.             echo('<h3 style="color:red">'.$e->getMessage().'</h3>');
  206.             @flush();ob_flush();
  207.         }
  208.  
  209.         return true;
  210.     }
  211.  
  212.     /**
  213.      * Set default options of curl
  214.      *
  215.      * @param string $url
  216.      * @param array $postvars
  217.      */
  218.     function prepare($url, $postvars=false){
  219.         curl_setopt($this->handle, CURLOPT_PROXY, $this->proxy);
  220.         curl_setopt($this->handle, CURLOPT_REFERER, $this->referer);
  221.         curl_setopt($this->handle, CURLOPT_USERAGENT, $this->userAgent);
  222.         curl_setopt($this->handle, CURLOPT_URL, str_replace('&amp;','&',$url));
  223.         curl_setopt($this->handle, CURLOPT_HEADER, 1);
  224.         curl_setopt($this->handle, CURLOPT_FOLLOWLOCATION,1);
  225.         curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1);
  226.         curl_setopt($this->handle, CURLOPT_TIMEOUT, $this->timeout);
  227.         curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, false);
  228.         curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST,  2);
  229.  
  230.         curl_setopt($this->handle, CURLOPT_COOKIEJAR, APP.'tmp/cookie.txt');
  231.         curl_setopt($this->handle, CURLOPT_COOKIEFILE, APP.'tmp/cookie.txt');
  232.  
  233.         if (!empty($postvars)){
  234.             curl_setopt($this->handle, CURLOPT_POST, 1);
  235.             curl_setopt($this->handle, CURLOPT_POSTFIELDS, $postvars);
  236.         }
  237.  
  238.         if (!empty($this->username)) {
  239.             curl_setopt($this->handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  240.             curl_setopt($this->handle, CURLOPT_USERPWD, $this->username.':'.$this->password); // $auth should be [username]:[password]
  241.         }
  242.  
  243.         return true;
  244.     }
  245.  
  246.     /**
  247.      * Close current connection
  248.      *
  249.      */
  250.     function close() {
  251.         curl_close($this->handle);
  252.         return true;
  253.     }
  254.  
  255.     /**
  256.      * Clears cache
  257.      */
  258.     function clearCache() {
  259.         if (empty($this->cacheFolder)) return false;
  260.  
  261.         $dir = dir($this->cacheFolder);
  262.         while (($file = $dir->read()) !== false) {
  263.             if (in_array($file, array('', '.', '..'))) continue;
  264.  
  265.             unlink($dir->path . $file);
  266.         }
  267.  
  268.         return true;
  269.     }
  270.  
  271.     /**
  272.      * What browser should be emulated
  273.      *
  274.      * @return string browser name
  275.      */
  276.     function _initUserAgent() {
  277.         if ($this->userAgent!='random') return true;
  278.  
  279.         $browsers = array(
  280.             'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)',
  281.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)',
  282.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
  283.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)',
  284.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 1.1.4322)',
  285.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Avant Browser; .NET CLR 2.0.50727)',
  286.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.10',
  287.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; FunWebProducts)',
  288.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; MRA 4.8 (build 01709); Maxthon; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
  289.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 8.50',
  290.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; ru) Opera 8.54',
  291.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)',
  292.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705)',
  293.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)',
  294.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)',
  295.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)',
  296.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; MAXTHON 2.0)',
  297.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)',
  298.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; InfoPath.1)',
  299.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)',
  300.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; InfoPath.2; .NET CLR 1.1.4322; MAXTHON 2.0)',
  301.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)',
  302.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.2)',
  303.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MRA 4.7 (build 01670); .NET CLR 1.1.4322)',
  304.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MRA 4.7 (build 01670); InfoPath.1)',
  305.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MRA 4.8 (build 01709))',
  306.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MRA 4.8 (build 01709); .NET CLR 1.1.4322)',
  307.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MRA 4.8 (build 01709); .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30)',
  308.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MRA 4.8 (build 01709); Maxthon; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30)',
  309.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MyIE2; InfoPath.1)',
  310.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MyIE2; MRA 4.8 (build 01709); .NET CLR 1.1.4322; InfoPath.1)',
  311.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322)',
  312.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)',
  313.             'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
  314.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
  315.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322)',
  316.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)',
  317.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)',
  318.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; InfoPath.1)',
  319.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser; Avant Browser; .NET CLR 1.1.4322)',
  320.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon)',
  321.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon; Avant Browser; InfoPath.2)',
  322.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon; MyIE2; .NET CLR 1.0.3705; .NET CLR 2.0.50727; InfoPath.2)',
  323.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MRA 4.6 (build 01425); InfoPath.1)',
  324.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MRA 4.8 (build 01709))',
  325.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MRA 4.8 (build 01709); .NET CLR 1.1.4322; InfoPath.1)',
  326.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MRA 4.8 (build 01709); Avant Browser)',
  327.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MRA 4.9 (build 01863); .NET CLR 2.0.50727)',
  328.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MyIE2)',
  329.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; MyIE2; .NET CLR 2.0.50727; InfoPath.1; .NET CLR 1.1.4322; MEGAUPLOAD 1.0)',
  330.             'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506; InfoPath.2; .NET CLR 1.1.4322)',
  331.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; bg; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3',
  332.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11',
  333.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3',
  334.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4',
  335.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3',
  336.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3',
  337.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4',
  338.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11',
  339.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3',
  340.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.4) Gecko/20070515 Firefox/2.0.0.4',
  341.             'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.12) Gecko/20050919 Firefox/1.0.7',
  342.             'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.1) Gecko/20060313 Fedora/1.5.0.1-9 Firefox/1.5.0.1 pango-text',
  343.             'Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.8) Gecko/20060112 ASPLinux/1.5-1.2am Firefox/1.5',
  344.             'Opera/8.54 (Windows NT 5.1; U; en)',
  345.             'Opera/9.00 (Windows NT 5.1; U; ru)',
  346.             'Opera/9.01 (Windows NT 5.1; U; ru)',
  347.             'Opera/9.02 (Windows NT 5.0; U; ru)',
  348.             'Opera/9.02 (Windows NT 5.1; U; ru)',
  349.             'Opera/9.02 (Windows NT 5.2; U; en)',
  350.             'Opera/9.10 (Windows NT 5.1; U; ru)',
  351.             'Opera/9.20 (Windows NT 5.1; U; en)',
  352.             'Opera/9.20 (Windows NT 5.1; U; ru)',
  353.             'Opera/9.21 (Windows NT 5.1; U; ru)',
  354.             );
  355.  
  356.         $this->userAgent = $browsers[array_rand($browsers)];
  357.         return true;
  358.     }
  359. }
  360.  
  361. ?>


Понравилось?

  1. Подпишись через RSS
  2. Расскажи о http://php.southpark.com.ua друзьям.
    Все способы хороши: ICQ, E-mail, свой блог, комментарий в чужом блоге или сообщение на форуме
  3. Добавь статью на news2.ru, Хабрахабр или в закладки

Огромное спасибо!

И не стесняйтесь комментировать - у меня стоит плагин, который убирает rel="nofollow" у людей, которые написали больше 5 комментариев.

RSS feed | Trackback URI

3 комментария »

2008-02-04 13:16:25

[...] файлов с нескольких серверов. Для загрузки обхожусь multi_curlом, для закачки можно написать алгоритм с неблокирующими [...]

 
Comment by Imho
2009-06-06 08:39:30

По моему такое количество User-Agent избыточно, и ведет к не нужному захламлению кода.
[quota]
Тогда я добавил в httpd.conf настройку Timeout 300000. Ошибка 408 Request Timeout всё равно показывалась, но насколько я понял, Apache продолжал выполнять скрипт ещё 300000 секунд.
[/quota]
Вполне очевидно что процесс с очень большим колиством url займет продолжительное время и вполне естественно либо зделать set_time_limit(0) и если не делаеться вывод в браузер закрывать окно(ну или ignore_user_abort и закрывать), не понятно зачем в httpd.conf нужно было менять Timeout? А вообше такие веши на php неделаються, не такие задачи перед ним ставяться.

 
Pingback by Особое программирование » Post Topic » CakePHP action из командной строки
2010-05-26 07:56:40

[...] файлов с нескольких серверов. Для загрузки обхожусь multi_curlом, для закачки можно написать алгоритм с неблокирующими [...]

 
Имя (required)
E-mail (required - never shown publicly)
URL
Текст комментария
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.