Модераторы: skyboy, MoLeX, Aliance, ksnk

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как отпарсить страницу 
:(
    Опции темы
DimaSiK
Дата 29.1.2007, 14:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 390
Регистрация: 20.8.2006

Репутация: нет
Всего: нет



Есть задача: написать парсер нескольких сайтов.Загвоздка состоит в том, что можно и нужно придумать для того, чтобы парсер не зациклился на ссылках одной страницы, тоесть знал когда ему нужно прикратить парсить данный сайт и переходить на другой?. Может ввести лимиты на найденные ссылки, или же проверять новую ссылку на предмет того, что она уже была найдена?


--------------------
Мы не стараемся быть первыми, мы стараемся быть лучшими.

PM MAIL   Вверх
Alex13
Дата 29.1.2007, 17:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 280
Регистрация: 30.12.2005
Где: Новосибирск

Репутация: нет
Всего: 1



DimaSiK, вы сами назвали выходы, причем, ИМХО, последний - самый разумный. В чем же проблема?


--------------------
Самая страшная ошибка - это ошибка, не имеющая явных причин и конкретных последствий.
Антисоциальный блог.
PM WWW ICQ Jabber   Вверх
DimaSiK
Дата 29.1.2007, 17:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 390
Регистрация: 20.8.2006

Репутация: нет
Всего: нет



Я понимаю что они верные, но помоему последняя кажется мне очень трудоемкой для скрипта, постоянно делать прохождение по найденным, это же сколько вермени необходимо будет smile. Может есть оптимальнее способы? 


--------------------
Мы не стараемся быть первыми, мы стараемся быть лучшими.

PM MAIL   Вверх
mishaSL
Дата 29.1.2007, 19:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1046
Регистрация: 10.1.2007
Где: Санкт-Петербург

Репутация: 19
Всего: 54



Вот что первое приходит на ум:
1. Заходишь на главную страницу (находишь все ссылки в html-коде), кладешь ссылки во временную таблицу БД или файл куда удобнее с пометкой что они не прочитаны.
2. Парсишь эту страницу(главную), читаешь текст, ну и делаешь что ты задумал с контентом. Помечаешь страницу во временном хранилище как прочитанную.
3. Далее заходишь на непрочитанные страницы из временного хранилища, и выполняешь пункт 1 затем пункт 2.

Единственное что:
1. Аккуратно распознавай ссылки (т.е. не забудь преобразовывать url-ы к единому стилю), я имею ввиду относительные ссылки и абсолютные.
2. Лучше сделать проверку на максимально возможное кол-во страниц, т.к. написать нормальное распознование сходства довольно сложно, а на некоторых сайта генерируются различные адреса ссылок на одну и туже страницу.
3. Я считаю самое существенное, данный скрипт написанный на php вполне может отвалиться из-за ограничения времени на выполнения скрипта.




--------------------
Лучший способ научиться программированию - это посмотреть как это делают другие...
PM MAIL   Вверх
DimaSiK
Дата 29.1.2007, 21:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 390
Регистрация: 20.8.2006

Репутация: нет
Всего: нет



Про ограничение было понятно сразу, но это пока меня не волнует, главное сделать задание. Как думаешь орагнизовать логику лучше будет с БД или же просто хранить в переменных все данные?. Еще вопрос, лучше сразу все ссылки собрать, а потом начать парсить или собирать и парсить?


--------------------
Мы не стараемся быть первыми, мы стараемся быть лучшими.

PM MAIL   Вверх
mishaSL
Дата 30.1.2007, 17:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1046
Регистрация: 10.1.2007
Где: Санкт-Петербург

Репутация: 19
Всего: 54



1. Я думаю лучше будет собирать и парсить
2. Думаю лучше будет с БД организовать, чтобы можно было разделять индексацию сайта на этапы.


--------------------
Лучший способ научиться программированию - это посмотреть как это делают другие...
PM MAIL   Вверх
-=Ustas=-
Дата 31.1.2007, 12:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



Цитата(DimaSiK @  29.1.2007,  21:22 Найти цитируемый пост)
Как думаешь орагнизовать логику лучше будет с БД или же просто хранить в переменных все данные?

Лучше в БД, потому что если сайт будет достаточно большим, то твой скрипт вывалится на memory_limit. 
Цитата(mishaSL @  29.1.2007,  19:49 Найти цитируемый пост)
данный скрипт написанный на php вполне может отвалиться из-за ограничения времени на выполнения скрипта.

Я писал себе такого паука, просто set_time_limit( 0 ) ставим и все. Он у меня сайт с более 50-ти тыс. страниц обрабатывал часа за полтора-два. Правда он у меня работает как консольное приложение. Могу подкинуть ;)


--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
mishaSL
Дата 31.1.2007, 12:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1046
Регистрация: 10.1.2007
Где: Санкт-Петербург

Репутация: 19
Всего: 54



Цитата(-=Ustas=- @  31.1.2007,  12:48 Найти цитируемый пост)
Могу подкинуть

Было бы интересно посмотреть smile 


Цитата(-=Ustas=- @  31.1.2007,  12:48 Найти цитируемый пост)
Правда он у меня работает как консольное приложение.

Если как консольное приложение, то это нормально... А если даже через крон запускать, то довольно запарно будет...


--------------------
Лучший способ научиться программированию - это посмотреть как это делают другие...
PM MAIL   Вверх
DimaSiK
Дата 31.1.2007, 18:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 390
Регистрация: 20.8.2006

Репутация: нет
Всего: нет



Цитата(mishaSL @ 31.1.2007,  12:57)
Цитата(-=Ustas=- @  31.1.2007,  12:48 Найти цитируемый пост)
Могу подкинуть

Было бы интересно посмотреть smile 


Цитата(-=Ustas=- @  31.1.2007,  12:48 Найти цитируемый пост)
Правда он у меня работает как консольное приложение.

Если как консольное приложение, то это нормально... А если даже через крон запускать, то довольно запарно будет...

можешь и мне кинуть на [email protected]

Это сообщение отредактировал(а) DimaSiK - 31.1.2007, 18:30


--------------------
Мы не стараемся быть первыми, мы стараемся быть лучшими.

PM MAIL   Вверх
-=Ustas=-
Дата 2.2.2007, 20:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



Вот освободился, как и обещал, выложу свого паука. Опишу его запуск.
1. Создаем следующие таблицы:

Код

DROP TABLE IF EXISTS `search_host`;
CREATE TABLE `search_host` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `host` VARCHAR(255) NOT NULL DEFAULT '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `host` (`host`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

DROP TABLE IF EXISTS `search_disabled_url`;
CREATE TABLE `search_disabled_url` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `host` INT(10) NOT NULL DEFAULT '0',
  `url` VARCHAR(255) NOT NULL DEFAULT '',
  `sid` VARCHAR(40) NOT NULL DEFAULT '',
  `active` ENUM('1','0') NOT NULL DEFAULT '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `url` (`url`, `host`),
  KEY `sid` (`sid`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

DROP TABLE IF EXISTS `search_found_url`;
CREATE TABLE `search_found_url` (
  `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `host` INT(10) NOT NULL DEFAULT '0',
  `url` VARCHAR(255) NOT NULL DEFAULT '',
  `sid` VARCHAR(40) NOT NULL DEFAULT '',
  `active` ENUM('1','0') NOT NULL DEFAULT '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `url` (`url`, `host`),
  KEY `sid` (`sid`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

DROP TABLE IF EXISTS `search_content`;
CREATE TABLE `search_content` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `uid` varchar(255) NOT NULL default '0',
  `sid` varchar(60) NOT NULL default '',
  `url` varchar(255) NOT NULL default '0',
  `title` varchar(255) NOT NULL default '',
  `content` text NOT NULL,
  `crc32` int(20) NOT NULL default '0',
  `bytes` int(20) NOT NULL default '0',
  `encode` varchar(50) NOT NULL default '',
  `modified` int(10) NOT NULL default '0',
  `date` int(10) NOT NULL default '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `uid` (`uid`),
  KEY `sid` (`sid`),
  KEY `url` (`url`),
  KEY `crc32` (`crc32`),
  KEY `bytes` (`bytes`),
  KEY `encode` (`encode`),
  KEY `modified` (`modified`),
  KEY `date` (`date`)
) ENGINE=MyISAM;

DROP TABLE IF EXISTS `search_index`;
CREATE TABLE `search_index` (
  `pid` int(10) unsigned NOT NULL default '0',
  `title_index` varchar(255) NOT NULL default '',
  `index` text NOT NULL,
  UNIQUE KEY `pid` (`pid`),
  FULLTEXT KEY `title_index` (`title_index`),
  FULLTEXT KEY `index` (`index`),
  FULLTEXT KEY `all` (`title_index`,`index`)
) ENGINE=MyISAM;


Далее создаем файлик, например sidx.robot.class.php, запихиваем в него данный класс:

Это сообщение отредактировал(а) -=Ustas=- - 2.2.2007, 22:01


--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
-=Ustas=-
Дата 2.2.2007, 22:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



Далее создаем файлик sidx.robot.class.php, запихиваем в него данный класс:

Первая часть класса (в одном посте не позволяет выложить весь) :
Код

<?php
/*
+---------------------------------------------------------------------------
|   Индексатор сайта / Паук
|   =============================================
|   by Bogutskiy Vladimir Aleksandrovich aka Ustas
|   (c) 2006 - 2007 Ustas.
|   http://www.ustix.ru
|   =============================================
|   Web: http://www.ustix.ru
|   Author: Ustas
|   Time: 
|   Release version: 1.5.0
|   Licence Info: http://www.ustix.ru/license/
+---------------------------------------------------------------------------
|
|   > sidx.robot.class.php
|   > Main executable wrapper.
|   > Date started: 
|
+---------------------------------------------------------------------------
*/

/**
 * Публичный интерфейс
 *
 */
interface IIndex 
{
    public function set_lenght_bytes( $b );
    public function set_lenght_index( $i );
    public function set_report_file( $file );
    public function set_page_limit( $limit );
    
    public function debug_mode( $mode );
    public function display_mode( $mode );
    public function interactive_mode( $mode );
    
    public function run_search();
    public function run_index();
}

final class Index implements IIndex
{
    const VERSION = '1.5.0';
    
    /**
     * URL-адрес
     *
     * @var string
     */
    private $site_url = NULL;
    
    /**
     * Идентификатор текщего запуска
     *
     * @access private
     * @var string sha1
     */
    private $sid       = NULL;
    
    private $curr_date = NULL;
    private $curr_time = NULL;
    
    /**
     * Величина байт, при которых индексируемая страница будет
     * считаться валидной, и добавлена в базу
     *
     * @var integer
     */
    private $bytes_length = 200;
    
    /**
     * Величина символов в слове, которое с меньшим
     * количеством не будет приниматься в индекс
     *
     * @access private
     * @var integer
     */
    private $index_lentgh = 4;
    
    /**
     * Файл для отчета о проделанной работе
     *
     * @access private
     * @var string
     */
    private $report_file = NULL;
    
    /**
     * Строка рег.выров для запрещенных сегментов URL-адреса
     * 
     * @access private
     * @var array
     * @example array( 'mycart', 'authorize' )
     */
    private $disable_urls   = array();
    private $disable_string = '';
    
    /**
     * Флаг режима работы. Т.е. выводить/не выводить инфу о процессе работы.
     *
     * @access private
     * @var boolean
     * @example 0 / 1
     */
    private $display_mode = 1;
    
    /**
     * Флаг интерактивного режима работы.
     *
     * @access private
     * @var boolean
     * @example 0 / 1
     */
    private $interactive_mode = 1;
    
    /**
     * Флаг поиска и отбора ссылок
     *
     * @access private
     * @var boolean
     */
    private $is_search = FALSE;
    
    /**
     * Лимит страниц на индексирование в текущем сеансе
     *
     * @access private
     * @var integer
     */
    private $page_limit = 0;
    
    /**
     * Ресурс базы данных
     *
     * @access private
     * @var resource
     */
    private $db = NULL;
    
    /**
     * SQL-запрос
     *
     * @access private
     * @var string
     */
    private $query = NULL;
    
    /**
     * Строка с ошибкой
     *
     * @access private
     * @var string
     */
    private $error = FALSE;
    
    /**
     * Свойства для статистики
     *
     * @var integer and real
     */
    private $count_url    = 0;
    private $count_index  = 0;
    private $update_index = 0;
    private $old_index    = 0;
    private $start_time   = 0;
    private $end_time     = 0;
    private $total_time   = 0;
    private $total_byte   = 0;
    
    /**
     * Текущий хост
     *
     * @access private
     * @var string
     */
    private $host = '';
    
    /**
     * ID хоста
     *
     * @access private
     * @var integer
     */
    private $host_id = '';
    
    /**
     * Конструктор
     * @param string: $start_url
     */
    public function __construct( $start_url )
    {
        if ( empty( $start_url ) || ( strpos( $start_url, 'http' ) === FALSE ) ) {
            
            $this->exceptions( 'Начальный адрес имеет неправильный формат' );
        }
        
        $this->site_url = $start_url;
        
        $this->host = parse_url( $this->site_url );
        $this->host = $this->host['host'];
        
        $this->sid       = sha1( mktime() );
        $this->curr_date = date( 'Y-m-d' );
        $this->curr_time = date( 'H:i:s' );
        
        if ( is_null( $this->report_file ) ) {
            $this->report_file = 'report-index-'.$this->curr_date.'.log';
        }
        
        $this->write_log( 'Initial parameter values for a current session' );
        
        $this->db = mysql_connect( DB_HOST, DB_USER, DB_PASS );
        $this->query( 'SET NAMES cp1251' );
        mysql_select_db( DB_NAME, $this->db );
        
        $res = $this->query( 'SELECT 
                                *
                             FROM 
                                `search_host` 
                             WHERE 
                                `host` = \''.mysql_real_escape_string( $this->host ).'\' 
                             LIMIT 1' );
        
        if ( $obj = $this->fetch( $res ) ) {
            
            $this->host_id = $obj->id;
            
        } else {
                    
            $this->query( 'INSERT INTO 
                                `search_host` (`host`) 
                           VALUES 
                                (\''.mysql_real_escape_string( $this->host ).'\')' );
                
            $this->host_id = mysql_insert_id( $this->db );
        }
    }
    
    /**
     * Вывод информационных сообщений
     *
     * @access private
     * @param string: $m
     * @return void()
     */
    private function std( $m )
    {
        if ( $this->display_mode ) {
            
            fputs( STDOUT, $m."\n" );
            $this->flush();
        }
    }
    
    /**
     * Вывод пользовательских сообщений
     *
     * @access private
     * @param string: $m
     * @return void()
     */
    private function info( $m, $exit = false )
    {
        fputs( STDOUT, $m."\n" );           
        $this->flush();
        
        if ( $exit ) {
            exit();
        }
    }
    
    /**
     * Сброс буфера
     * 
     * @access private
     * @return void()
     */
    private function flush()
    {
        flush();
    }
    
    /**
     * Проверка на целочисленные типы
     *
     * @access private
     * @param integer: $int
     * @return integer or FALSE
     */
    private function is_integer( $int )
    {
        $int = preg_replace( '#[^\d]#', '', $int );
        
        if ( empty( $int ) ) {
            
            return FALSE;
        }
        
        return (integer) $int;
    }
    
    /**
     * Оболочка для выполнения запросов SQL
     *
     * @access private
     * @param string: $sql
     * @return Exception в случае неудачи
     */
    private function query( $sql )
    {
        if ( empty( $sql ) ) {
            
            $this->exceptions_sql( 'Попытка выполнения пустого запроса' );
        }
        $this->query = $sql;
        
        if ( ! $res = mysql_query( $this->query, $this->db ) ) {
        
            $this->exceptions_sql( mysql_error()."\n SQL: ".$this->query );
        }
        
        return $res;
    }
    
    /**
     * Оболочка для получения результата запросов SQL
     *
     * @access private
     * @param resource: $res
     * @return Результатирующий объект
     */
    private function fetch( $res )
    {
        if ( $obj = mysql_fetch_object( $res ) ) {
            
            return (object) $obj;
        }
        
        return FALSE;
    }
    
    /**
     * Для некоторых пауз
     *
     * @access private
     * @param integer: $int
     * @return void()
     */
    private function sleeptime( $int )
    {
        sleep( $int );
    }
    
    /**
     * Диалог с пользователем. Запрос вопроса
     *
     * @access private
     * @param string: $message
     * @return boolean
     */
    private function confirm( $message )
    {
        if ( ! $this->interactive_mode ) {
            
            return TRUE;
        }
        
        fputs( STDOUT, "\n\n::> $ ".$message." (y / n) : " );
        
        $reply = trim( fgets( STDIN ) );
        
        if ( strtolower( $reply ) == 'y' ) {
            
            return TRUE;
        }
        
        return FALSE;
    }
    
    /**
     * Диалог с пользователем. Запрос ответа
     *
     * @access private
     * @param string: $message
     * @return string
     */
    private function request( $message )
    {   
        fputs( STDOUT, "\n::> $ ".$message." : " );
        
        $reply = trim( fgets( STDIN ) );
        
        return (string) $reply;
    }
    
    /**
     * Проверка на допустимый лимит
     *
     * @access private
     * @return void()
     */
    private function is_limit_enabled()
    {
        if ( $this->page_limit > 0 ) {
            
            if ( $this->count_url >= $this->page_limit ) {
            
                return FALSE;
            }
        }
        
        return TRUE;
    }
    
    /**
     * Собирание уникальных URL-ов с сайта
     * 
     * @access private
     * @return void()
     */
    private function get_url( $url )
    {
        static $iteration = 0;
        
        $this->std( "\nПолучение текущей ссылки: ".$url );
        
        // Получаем страницу
        if ( $content = $this->get_page( $url ) ) {
            
            // Собираем с нее ссылки
            preg_match_all('#href\s*=\s*\"((?:http:\/\/|www\.)(?:[A-Za-z0-9\-\.]+)\.(?:com|ru|info|org|ua))?([A-Za-z0-9\-\_\/\~\%\.]+(?:\.html|\.htm|\/))\"#isU', $content, $matches);
            unset($content);
            
            $size = sizeof( $matches[2] );
            
            for ( $i = 0; $i < $size; $i++ ) {
                // Проверяем, если эта страница с другого хоста, то нах...
                // нам нужен только этот сайт, чтобы себе в базу весь инет не впихнуть ))
                if ( ! empty( $matches[1][ $i ] ) ) {
                    
                    if ( ! preg_match( '#'.$this->host.'#is', $matches[1][ $i ] ) ) {
                        
                        unset( $matches[2][ $i ] );
                        continue;
                    }
                }
            }
            
            $items = array_values( $matches[2] );
            
            return (array) $items;
        }
    }
    
    /**
     * Сверка массива ссылок со стеком очереди (таблицей)
     *
     * @access private
     * @param array: $array
     * @return void()
     */
    private function parse_links_db( $array )
    {
        $size = sizeof( $array );
        
        // Делаем первую вставку в стек ссылок (таблицу)
        for ( $i = 0; $i < $size; $i++ ) {
            
            if ( ! $this->is_limit_enabled() ) { 
                
                return( 0 ); 
            }
            
            $item = $array[ $i ];
        
            // Если ссылка не имеет запрещенных сегментов
            if ( !  $this->is_item_enabled( $item ) ) {
                
                echo ' .'; // Это типа прогресс бара ))
                $this->flush();
                continue;
            }
            
            // Проверка на уникальность ссылки
            if ( $this->is_item_exists( $item ) ) {
                
                echo ' .';
                $this->flush();
                continue;
            }
            
            // Если ссылка разрешенная и не имеется в стеке, то пишем ее в очередь
            $this->std( "\nНайдена новая ссылка: ".$item.'...' );
            
            if ( $this->add_item( $item ) ) {
                            
                $this->std( "\nНовая ссылка была добавлена! OK! " );
                $this->count_url++;                            
            }
        }
    }
    
    /**
     * Индексатор ссылок со страниц
     * получает один раз на вход массив начальных ссылок (ссылки)
     * и далее полез, полез, полез... если нет установленного 
     * limit_page, то обшарит весь сайт до последней ссылки
     *
     * @access private 
     * @param array $begin_links
     * @return void()
     */
    private function search_links( $begin_links )
    {
        
        // Заполняем начальный стек очереди
        $this->parse_links_db( $begin_links );
        
        // Note:
        // Первая очередь ссылок в стеке готова, начинаем лазить по сайту.
        // Так сделано, чтоб небыло рекурсии и чтоб программа не вылетала 
        // при большой иерархии вложенности (обычно 819 стеков и PHP в ауте :) )
        
        // Флаг того, что программа распарсила весь сайт (если не указан лимит страниц)
        $stop_flag = FALSE; 

        while ( $stop_flag !== TRUE ) {
            
            if ( $item = $this->get_item_link() ) {
                
                $this->set_item_active( $item->id );
                
                $links = $this->get_url( 'http://'.$this->host.$item->url );
                
                // Добавляем элементы в стек очереди, пока есть
                $this->parse_links_db( $links );

                if ( ! $this->is_limit_enabled() ) { 
                    
                    $stop_flag = TRUE;
                }
        
                unset( $links );
                
            } else {
            
                $stop_flag = TRUE;
            }
        }
    }


Это сообщение отредактировал(а) -=Ustas=- - 3.2.2007, 12:57


--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
-=Ustas=-
Дата 2.2.2007, 22:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



Вторая чать класса (соединить в первой, опять не пропускает):
Код

    /**
     * Получение непройденной ссылки из стека очереди (таблицы)
     *
     * @access private
     * @return object
     */
    private function get_item_link()
    {
        $res = $this->query( 'SELECT * FROM `search_found_url` WHERE `active` = \'0\' ORDER BY `id` ASC LIMIT 1' );
    
        if ( $obj = $this->fetch( $res ) ) {
            
            return (object) $obj;
        }
        
        return FALSE;
    }
    
    /**
     * Установление флага пометки на элемент (ссылку) в стеке очереди, 
     * о том, что он был уже использован
     *
     * @access private
     * @param integer: $id
     * @return void()
     */
    private function set_item_active( $id )
    {
        $this->query( 'UPDATE `search_found_url` SET `active` = \'1\' WHERE `id` = '.intval( $id ) );
    }
    
    /**
     * Получить контент страницы
     *
     * @access private
     * @param string: $url
     * @return string: content page
     */
    private function get_page( $url )
    {
        $purl    = parse_url( $url );
        $host    = $purl['host'];
        $page    = '';
        $headers = '';
    
        if ( $socket = fsockopen( $host, 80, $errno, $errstr, 30 ) ) {
            
            $purl['path'] = ( empty( $purl['path'] ) ) ? '/' : $purl['path'];
            
            $headers .= "GET ".$purl['path']." HTTP/1.0\r\n";
            $headers .= "Accept: */*\r\n";
            $headers .= "Accept-Language: ru\r\n";
            $headers .= "Referer: Ustix-Bot\r\n";
            $headers .= "Host: ".$purl['host']."\r\n";
            $headers .= "User-Agent: Ustix-Spider-Bot/1.5.0\r\n";
            $headers .= "Connection: Close\r\n\r\n";

            fputs( $socket, $headers );

            while ( ! feof( $socket ) ) {
                $page .= fgets( $socket );
            }
            fclose( $socket );

            $page = substr( $page, ( strpos( $page, "\r\n\r\n" ) + 4 ), strlen( $page ) );

            return (string) $page;
        }
        
        return FALSE;
    }
    
    /**
     * Проверка ссылки на существование
     *
     * @access public
     * @param string: $item
     * @return boolean
     */
    private function is_item_exists( $item )
    {

        $sql = 'SELECT 
                    `u`.`id`, 
                    `u`.`url`,
                    `h`.`id` AS `host_id`
                FROM
                    `search_found_url` AS `u`
                INNER JOIN
                    `search_host` AS `h`
                ON
                    `u`.`host` = `h`.`id`
                WHERE
                    `u`.`url` = \''.mysql_real_escape_string( $item ).'\'
                AND
                    `h`.`id` = '.$this->host_id.'
                LIMIT 1';
        
        $res = $this->query( $sql );
        
        if ( $obj = $this->fetch( $res ) ) {
        
            $sql = 'UPDATE `search_found_url` SET `sid` = \''.$this->sid.'\' WHERE `id` = '.$obj->id.' LIMIT 1';
            $this->query( $sql );
            
            return TRUE;
            
        } else {
            
            return FALSE;
        }
    }
    
    /**
     * Проверка ссылки на исключение
     *
     * @access private
     * @param string $item
     * @return boolean
     */
    private function is_item_enabled( $item )
    {
        if ( ! empty( $this->disable_string ) ) {
            if ( preg_match( '#('.$this->disable_string.')#is', $item ) ) {
        
                return FALSE;
            }
        }
        
        return TRUE;
    }
    
    private function add_item( $item )
    {   
        $sql = 'INSERT INTO
                    `search_found_url`
                VALUES
                    (\'\',
                    '.$this->host_id.',
                    \''.mysql_real_escape_string( $item ).'\',
                    \''.$this->sid.'\',
                    \'0\')';
        
        $this->query( $sql );
        
        return TRUE;
    }
    
    /**
     * Оболочка для возбуждений исключений
     *
     * @param string: $message
     * @param integer: $code
     */
    private function exceptions( $message, $code = FALSE )
    {
        throw new Exception( $message, $code );
    }
    
    /**
     * Фатальные исключения СУБД
     *
     * @param string: $message
     * @param integer: $code
     * @return Exception
     */
    private function exceptions_sql( $message )
    {
        $this->exceptions( 'Ошибка SQL! Причина: '.$message );
    }
    
    /**
     * Логирование
     *
     * @access private
     * @param string: $message
     * @return void()
     */
    private function write_log( $message )
    {
        $fp = fopen( $this->report_file, 'a+' );
        
        flock( $fp, LOCK_EX );
        fputs( $fp, date( 'Y.m.d - H:i:s' )."\t".$message."\n" );
        flock( $fp, LOCK_UN );
        fclose( $fp );
    }
    
    private function get_page_params( $url )
    {
        $params = get_headers( $url );
        
        return (array) $params;
    }
    
    // ============================================================================================================
    // Публика
    // ============================================================================================================
    
    /**
     * Установить значение величины байт для принятия валидной страницы
     *
     * @access public
     * @param integer: $b
     * @return void()
     */
    public function set_lenght_bytes( $b )
    {
        if ( ! $b = $this->is_integer( $b ) ) {
            
            $this->exceptions( 'Величина байт должна быть целочисленной!' );
        }
        
        $this->bytes_length = $b;
        $this->write_log( 'Set value for length bytes: '.$this->bytes_length );
    }
    
    /**
     * Установить значение величины символов для индексируемого слова
     *
     * @access public
     * @param integer: $i
     * @return void()
     */
    public function set_lenght_index( $i ) 
    {
        if ( ! $i = $this->is_integer( $i ) ) {
            
            $this->exceptions( 'Величина символов должна быть целочисленной!' );
        }
        
        $this->index_lentgh = $i;
        $this->write_log( 'Set value for index length symbols: '.$this->index_lentgh );
    }
    
    /**
     * Изменить значение по умолчанию для файла отчетов
     *
     * @access public
     * @param string: $file
     * @return void()
     */
    public function set_report_file( $file )
    {
        $this->report_file = $file;
        $this->report_file = str_replace( '\\', '/', $this->report_file );
        
        if ( ! file_exists( $this->report_file ) ) {
            
            $fp = fopen( $this->report_file, 'w' );
            fclose( $fp );
        }
        if ( ! is_writable( $this->report_file ) ) {
            
            $this->exceptions( 'Файл для отчета недоступен для записи!' );
        }
        $this->write_log( 'Set value for report file : '.$this->report_file );
    }
    
    /**
     * Установить лимит страниц для текущего запуска
     *
     * @access public
     * @param integer: $limit
     * @return void()
     */
    public function set_page_limit( $limit )
    {
        $this->page_limit = intval( $limit );
        $this->write_log( 'Set value for page limit: '.$this->page_limit );
    }
    
    /**
     * Режим отладки, вывод сообщение об ошибках
     *
     * @access public
     * @param boolen: $mode
     * @return void()
     */
    public function debug_mode( $mode )
    {
        ini_set( 'display_errors', intval( $mode ) );
        $this->write_log( 'Set value for debug mode: '.intval( $mode ) );
    }
    
    /**
     * Устанавливает информационный режим работы. Т.е. показывать процесс или не показывать
     *
     * @access public
     * @param boolean: $mode
     * @return void()
     * 
     * @example Index::display_mode( 0 / 1 );
     */
    public function display_mode( $mode )
    {
        $this->display_mode = intval( $mode );
        $this->write_log( 'Set value for display mode: '.$this->display_mode );
    }



--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
-=Ustas=-
Дата 2.2.2007, 22:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



И последняя часть ( присоединить ко второй ):
Код

    /**
     * Устанавливает запрещенные URL-адреса, которые индексироваться не будут
     *
     * @access public
     * @param array: $urls
     * @return void()
     * 
     * @example Index::disable_urls( array( 'mycart', 'authorize' ) );
     */
    public function disable_urls( $urls )
    {
        if ( is_array( $urls ) ) {
            $this->disable_urls = $urls;
        }
        $this->write_log( 'Set value for disabled urls: '.implode( ', ', $this->disable_urls ) );
    }
    
    /**
     * Включить / Выключить интерактивный режим работы
     * Обращения и запросы пользователю и т.д.
     *
     * @access public
     * @param boolean: $mode
     * @return void()
     */
    public function interactive_mode( $mode )
    {
        $this->interactive_mode = intval( $mode );
        $this->write_log( 'Set value for interactive mode: '.$this->interactive_mode );
    }
    
    // Дружественное общение :)
    public function frendly()
    {
        $this->std( "\n\nHi! :)" );
        
        if ( $this->confirm( 'Хотите изменить настройки по умолчанию?' ) ) {
            
            if ( $this->confirm( 'Хотите указать адрес сайта? ( по умолчанию '.$this->site_url.' )' ) ) {
            
                $reply = FALSE;
                $count = 0;
                while ( $reply !== TRUE ) {
                
                    if ( $count == 0 ) {
                        $message = 'Введите адрес сайта или начальную страницу для индексации (без http://)';
                    } else {
                        $message = 'Ошибка! Повторите ввод адреса для старта индексации (без http://)';
                    }
                
                    $url = $this->request( $message );
                                
                    if ( ! empty( $url ) && ( strpos( $url, 'http://' ) === FALSE ) ) {
                        $this->site_url = $url;
                        $reply = TRUE;
                    }
                    $count++;
                }
            }
            
            if ( $this->confirm( 'Изменить файл для отчета по умолчанию? ( по умолчанию '.$this->report_file.' )' ) ) {
            
                $reply = FALSE;
                $count = 0;
                $file = '';
                while ( $reply !== TRUE ) {
                    
                    if ( $count == 0 ) {
                        $message = 'Введите новое имя файла для отчета';
                    } else {
                        $message = 'Ошибка! Файл "'.$file.'" не доступен для записи. Введите новое имя';
                    }
                
                    $file = $this->request( $message );
                    
                    $file = str_replace( '\\', '/', $file );
        
                    if ( ! file_exists( $file ) ) {
            
                        $fp = fopen( $file, 'w' );
                        fclose( $fp );
                    }
                    if ( is_writable( $file ) ) {
            
                        $this->set_report_file( $file );
                        $reply = TRUE;
                    }
                
                    $count++;
                }
            }
            
            if ( $this->confirm( 'Установить режим отладки?' ) ) {
            
                $debug = $this->request( 'Выберите режим отладки: 0 - не выводить отладочную инфу, 1 - выводить' );
                $this->debug_mode( intval( $debug ) );
            }
            
            if ( $this->confirm( 'Изменить размер для валидной индексируемой страницы? ( по умолчанию '.$this->bytes_length.' )' ) ) {
            
                $bytes = $this->request( 'Введите размер страницы в байтах' );
                $this->set_lenght_bytes( intval( $bytes ) );
            }
            
            if ( $this->confirm( 'Изменить количество символов для индекса слов ? ( по умолчанию '.$this->index_lentgh.' )' ) ) {
            
                $index = $this->request( 'Введите количество символов' );
                $this->set_lenght_index( intval( $index ) );
            }
            
            if ( $this->confirm( 'Хотите установить лимит страниц на текущий процесс ? ' ) ) {
            
                $pages = $this->request( 'Введите количество страниц ( по умолчанию '.$this->page_limit.', 0 - без ограничений )' );
                $this->set_page_limit( intval( $pages ) );
            }
            
            if ( $this->confirm( 'Хотите добавить запрещенные URL-сегменты?' ) ) {
            
                $reply = FALSE;
                $count = 0;
                $segment = '';
                while ( $reply !== TRUE ) {
                    
                    if ( $count == 0 ) {
                        $message = 'Введите обозначение URL-сегмента';
                    } else {
                        $message = 'Хотите еще добавить? ( y / n )';
                    }
                
                    $segment = $this->request( $message );
                    
                    if ( $segment == 'y' ) {
                        
                        $count = 0;
                        continue;
                        
                    } else if ( $segment == 'n' ) {
                        
                        $reply = TRUE;
                    }
                    
                    $segment = preg_replace( '#[^A-Za-z0-9\-\_\.\~]#is', '', $segment );
                    
                    if ( ! empty( $segment ) ) {
                        array_push( $this->disable_urls, $segment );
                    }
                
                    $count++;
                }
            }
            
            $this->write_log( 'The configuration has been successfully installed' );
            $this->write_log( 'Start new indexing' );
            
            $this->__construct( 'http://'.$this->site_url );
            $this->run_index();            
            
        } else {
            
            $this->write_log( 'The configuration was stopped' );
            $this->info( "\n\n".'Работа закончена' );
            $this->write_log( 'The successfully completed! Bye.' );
            $this->info( '-----------------------', TRUE );
            $this->info( 'Пользователь отказался от выполнения работы', TRUE );
        }
        
    }
    
    /**
     * Запуск поиска уникальных ссылок на сайте
     *
     * @access public
     * @return void()
     */
    public function run_index() 
    {
        $this->is_search = 1;
        
        $this->write_log( 'Start search links for '.$this->host );
        
        if ( sizeof( $this->disable_urls ) > 0 ) {
            
            $this->disable_string = implode( '|', $this->disable_urls );
        }
        
        $this->info( "\n\n".'=======================================' );
        $this->info( '||  Ustix Spider-Bot, version '.self::VERSION .'  ||' );
        $this->info( '||                                   ||' );
        $this->info( '||           Author: Ustas           ||' );
        $this->info( '||                                   ||' );
        $this->info( '||             2006-2007             ||' );
        $this->info( '======================================='."\n\n" );
        
        $this->sleeptime( 2 );
        
        if ( $this->confirm( 'Вы хотите начать новое индексирование?' ) ) {
            
            $this->write_log( 'Start new indexing' );
            
            $this->info( 'Очистка старых ссылок...' );
            
            $sql = 'DELETE FROM `search_found_url` WHERE `host` = '.$this->host_id.' AND `sid` <> \''.$this->sid.'\'';
            $this->query( $sql );
            $this->info( 'Очистка выполнена' );
            
            $this->sleeptime( 1 );
            
            $this->info( 'Очистка запрещенных ссылок...' );
            
            $sql = 'DELETE FROM `search_disabled_url` WHERE `host` = '.$this->host_id.' AND `sid` <> \''.$this->sid.'\'';
            $this->query( $sql );
            $this->info( 'Очистка выполнена' );
            
            $this->sleeptime( 1 );
            
            $links = $this->get_url( 'http://'.$this->host );
            
            $this->search_links( $links );
            
            // Закончили сбор ссылок, начинаем обработку страниц
            
            $this->info( "\n\nСбор ссылок закончен." );
            $this->info( 'На сайте '.$this->host.' найдено '.$this->count_url.' ссылок.' );
            $this->write_log( 'Search links for '.$this->host.' succeeds' );
            $this->write_log( 'Found links: '.$this->count_url );
            $this->sleeptime( 2 );
            
            if ( $this->confirm( 'Начать индексирование страниц? [ вы можете отложить эту операцию ]' ) ) {
                
                $this->write_log( 'Start indexing for new links, pages' );
                
                $this->run_search();
                
            } else {
                
                $this->write_log( 'Indexing pages from new links postponed. Stopped.' );
            }
            
        } else {
            
            if ( $this->confirm( 'Вы хотите переиндексировать уже имеющиеся ссылки?' ) ) {
                
                $this->write_log( 'Start indexing for old links, pages' );
                
                $this->run_search();
                
            } else {
                
                $this->write_log( 'Indexing pages from old links postponed. Stopped.' );
            }
        }
        
        $this->info( "\n\n".'Работа закончена' );
        
        $this->write_log( 'The successfully completed! Bye.' );
        
        $this->info( '-----------------------', TRUE );
    }
    
    public function run_search()
    {
        if ( ! $this->is_search ) {
            
            // Эту возможность я пока убрал, но это не долго добавить, закрыть
            if ( $this->confirm( 'Поиск ссылок по сайту '.$this->host.' не был произведен. Переиндексировать старые ссылки?' ) ) {
                
                $this->is_search = 1;
                
                $this->run_search();
            }
        }
        
        $this->write_log( 'Starting find pages by the links' );
        
        /*$res = $this->query( 'SELECT 
                                    * 
                                FROM 
                                    `search_found_url` 
                                WHERE 
                                    `sid` = '.$this->sid.' 
                                AND 
                                    `host` = '.$this->host_id.' 
                                ORDER BY 
                                    `id` 
                                ASC' );
        
        while ( $item = $this->fetch( $res ) ) {
            
            $page        = $this->get_page( 'http://'.$this->host.$item->url );
            $page_params = $this->get_page_params( 'http://'.$this->host.$item->url );*/
            
            /* -------------------------------------------------------------------------
            
            Ну и вот тут собсна уже пишите кто на что горазд, т.е. тут вы забираете страницу
            и делаете из нее индексное содержание и содержание для пред.показа
            
            Я специально здесь свой код убрал, т.к. я свой алгоритм никому не давал и не дам ;)
            
            Дальше тут уже легко ;)

            по таблицам search_content и search_index можно узнать что там должно быть, и здесь
            в цикле выявить эти сущности
            
            */
            
        //}
    }
    
    /**
     * Деструктор 
     * 
     * @access private
     */
    public function __destruct()
    {
        mysql_close( $this->db );
    }
}

?>


Это сообщение отредактировал(а) -=Ustas=- - 2.2.2007, 22:36


--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
-=Ustas=-
Дата 2.2.2007, 22:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



Затем, создаем файлик run.robot.php, у которого содежание примерно следующее:
Код

<?php

set_time_limit(0); // На всякий..., чтоб не вывалило случаем

// Ну здесь думаю все понятно. Пауку нужен доступ к БД
// и мы его ему даём :)
define( 'DB_HOST', 'localhost',          TRUE);
define( 'DB_USER', 'root',               TRUE);
define( 'DB_PASS', '111111111',          TRUE);
define( 'DB_NAME', 'sidx',               TRUE);

// Подключаем собсна самого паука
include_once( 'sidx.robot.class.php' ); 

// Далее делаем предварительную конфигурацию
try {

    $index = new Index( 'http://yandex.ru' );
    
    $index->interactive_mode( 1 ); // Ставим интерактивный режим общения )) Когда ставить накрон, то делаем ему - 0
    $index->debug_mode( 1 );       // Глушим ошибки, после того как отладили
    $index->display_mode( 1 );     // Включаем вывод сообщений процесса работы
    
    $index->set_lenght_bytes( 200 );        // Ставим минималку - страница = 200 символов
    $index->set_lenght_index( 4 );          // Длина слов для индекса не менее 4-х символов
    //$index->set_report_file( 'rep.log' ); // также можно задать свой лог, отличный по умолчанию
    $index->set_page_limit( 0 );            // Ставим лимит страниц для текущего процесса, 0 - без ограничений, т.е. весь сайт
    
    $index->disable_urls( array( 'my_cart', 'authorize' ) ); // Запрещаем индексирование ненужных нам страниц, указывая сегменты URL-адреса
    
    $index->run_index();
    
} catch ( Exception $e ) {

    echo "\n\n=============================\n";
    echo "Fatal error!\n";
    echo "Message: ".$e->getMessage()."\n";
    echo "File: ".$e->getFile()."\n";
    echo "Line: ".$e->getLine()."\n\n";
    echo "Exit()\n";
    echo "=============================\n\n";
    
    exit();
}

?>


Надо обязательно взять вызовы экземпляра объекта в try {} catch {} , т.е. класс при фатальных ошибках выкидывает исключения.
далее настойка идет через очень простой и понятный интерфейс:
Код

/**
 * Публичный интерфейс
 *
 */
interface IIndex 
{
    public function set_lenght_bytes( $b );
    public function set_lenght_index( $i );
    public function set_report_file( $file );
    public function set_page_limit( $limit );
    
    public function debug_mode( $mode );
    public function display_mode( $mode );
    public function interactive_mode( $mode );
    
    public function run_search();
    public function run_index();
}


Можно его полностью запусти в интерактивном режиме ;) 
Код

<?php

set_time_limit(0); // На всякий..., чтоб не вывалило случаем

// Ну здесь думаю все понятно. Пауку нужен доступ к БД
// и мы его ему даём :)
define( 'DB_HOST', 'localhost',          TRUE);
define( 'DB_USER', 'root',               TRUE);
define( 'DB_PASS', '111111111',          TRUE);
define( 'DB_NAME', 'sidx',               TRUE);

// Подключаем собсна самого паука
include_once( 'sidx.robot.class.php' ); 

// Далее делаем предварительную конфигурацию
try {

    $index = new Index( 'http://yandex.ru' );
    
    $index->frendly(); // Всего один вызов и всё :)
    
} catch ( Exception $e ) {

    echo "\n\n=============================\n";
    echo "Fatal error!\n";
    echo "Message: ".$e->getMessage()."\n";
    echo "File: ".$e->getFile()."\n";
    echo "Line: ".$e->getLine()."\n\n";
    echo "Exit()\n";
    echo "=============================\n\n";
    
    exit();
}

?>

Результатом дружественного интерфейса будет примерно следующее:
Код

Hi! :)

::> $ Хотите изменить настройки по умолчанию? (y / n) : y

::> $ Хотите указать адрес сайта? ( по умолчанию http://yandex.ru ) (y / n) : y

::> $ Введите адрес сайта или начальную страницу для индексации (без http://) : my.domain.com

::> $ Изменить файл для отчета по умолчанию? ( по умолчанию report-index-2007-02-02.log ) (y / n) : y

::> $ Введите новое имя файла для отчета : report-google.log

::> $ Установить режим отладки? (y / n) : n

::> $ Изменить размер для валидной индексируемой страницы? ( по умолчанию 200 ) (y / n) : y

::> $ Введите размер страницы в байтах : 300

::> $ Изменить количество символов для индекса слов ? ( по умолчанию 4 ) (y / n) : y

::> $ Введите количество символов : 3

::> $ Хотите установить лимит страниц на текущий процесс ?  (y / n) : y

::> $ Введите количество страниц ( по умолчанию 0, 0 - без ограничений ) : 150

::> $ Хотите добавить запрещенные URL-сегменты? (y / n) : y

::> $ Введите обозначение URL-сегмента : my_shop

::> $ Хотите еще добавить? ( y / n ) : y

::> $ Введите обозначение URL-сегмента : orders

::> $ Хотите еще добавить? ( y / n ) : y

::> $ Введите обозначение URL-сегмента : market

::> $ Хотите еще добавить? ( y / n ) : n

/// Здесь идет обработка и т.д. далее 
Сбор ссылок закончен.
На сайте my.domain.com найдено 150 ссылок.
// ну и далее он спрашивает, типа индексировать страницы сейчас или потом....

Короче код я снабдил оч. понятными комментами, так что если хотите, то разберетесь smile


Это сообщение отредактировал(а) -=Ustas=- - 2.2.2007, 22:40


--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
-=Ustas=-
Дата 2.2.2007, 22:47 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ustix IT Group
****


Профиль
Группа: Участник Клуба
Сообщений: 2222
Регистрация: 21.1.2005
Где: Краснодар

Репутация: 32
Всего: 69



В общем, смотрите. Формат запуска: php -f run.robot.php


--------------------
В искаженном мире все догмы одинаково произвольны, включая догму о произвольности догм.
-----
PM WWW ICQ Skype   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | PHP: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0843 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.