загрузка в несколько потоков Как создать свой сайт > Вебмастеру > Создание своего сайта > Загрузка в несколько потоков с multi_curl и PHP

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

— Настоящий эстонец должен жить в Канаде! В Канаде, и больше нигде…
«Зона. Записки надзирателя», Сергей Довлатов.
26 октября 2007

    Когда нужно скачать сотню страниц, то можно обойтись моим компонентом 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
  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://www.seoded.ru
  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://www.seoded.ru
  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://www.seoded.ru
  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. ?>

Автор: Владимир Лучанинов.

Комментарии:


⇓ 

Поделись ссылкой на Seoded.ru с друзьями, знакомыми и собеседниками в соцсетях и на форумах! А сам сайт добавь в закладки! Так победим.

Поделиться ссылкой на эту страницу в:

Полезные ссылки:

Тайм-менеджмент помогает в работе Надомная работа мамам с детьми

Ещё материалы по этой теме:

Скажи кэшированию… иногда. Часть 2: Memcache Скажи кешированию… иногда :) Управление зависимостями в PHP-коде Инверсия зависимостей Registry вместо Singleton
основан в 2008 г. © Все права на материалы сайта Seoded.ru принадлежат Алексею Вострову.
Копирование (полное или частичное) любых материалов сайта возможно только с разрешения автора и при указании ссылки на источник.
Ослушавшихся находит и забирает Бабайка!