phpquery 學習筆記


phpQuery是一個基於PHP的服務端開源項目,它可以讓PHP開發人員輕松處理DOM文檔內容,比如獲取某新聞網站的頭條信息。更有意思的是,它采用了jQuery的思想,你可以像使用jQuery一樣處理頁面內容,獲取你想要的頁面信息。

Query的選擇器之強大是有目共睹的,phpQuery 讓php也擁有了這樣的能力,它就相當於服務端的jQuery。

 

存在的意義


我們有時需要抓取一個網頁的內容,但只需要特定部分的信息,通常會用正則來解決,這當然沒有問題。正則是一個通用解決方案,但特定情況下,往往有更簡單快 捷的方法。

1、編寫條件表達式比較麻煩

尤其對於新手,看到一堆”不知所雲”的字符評湊在一起,有種腦袋都要炸了的感覺。如果要分離的對象沒有太明顯的特征,正則寫起來更是麻煩。

2、效率不高

對於php來說,正則應該是沒有辦法的辦法,能通過字符串函數解決的,就不要勞煩正則了。用正則去處理一個30多k的文件,效率不敢保證。

3、有phpQuery

如果你使用過jQuery,想獲取某個特定元素應該是輕而易舉的事情,phpQuery讓這成為了可能。

淺析phpQuery


phpQuery是基於php5新添加的DOMDocument。而DOMDocument則是專門用來處理html/xml。它提供了強大xpath選 擇器及其他很多html/xml操作函數,使得處理html/xml起來非常方便。那為什么不直接使用呢?這個,去看一下官網的函數列表 ( http://www.php.net/manual/en/class.domdocument.php ) 就知道了,如果對自己的記憶力很有信心, 不妨一試。

 

項目官網地址:http://code.google.com/p/phpquery/

 

 

http://demo.jingwentian.com/phpQuery/

采集頭條

先看一實例,現在我要采集新浪網國內新聞的頭條,代碼如下:

include 'phpQuery/phpQuery.php'; phpQuery::newDocumentFile('http://news.sina.com.cn/china'); echo pq(".blk121 a:eq(0)")->html();


簡單的三行代碼,就可以獲取頭條內容。首先在程序中包含phpQuery.php核心程序,然后調用讀取目標網頁,最后輸出對應標簽下的內容。
pq()是一個功能強大的方法,跟jQuery的$()如出一轍,jQuery的選擇器基本上都能使用在phpQuery上,只要把“.”變成“->”。如上例中,pq(".blkTop h1:eq(0)")抓取了頁面class屬性為blkTop的DIV元素,並找到該DIV內部的第一個h1標簽,然后用html()方法獲取h1標簽里的內容(帶html標簽),也就是我們要獲取的頭條信息,如果使用text()方法,則只獲取頭條的文本內容。當然要使用好phpQuery,關鍵是要找對文檔中對應內容的節點。

 

 

 

采集文章列表

下面再來看一個例子,獲取 http://www.jingwentian.com 網站的blog列表,請看代碼:

include 'phpQuery/phpQuery.php'; phpQuery::newDocumentFile('http://www.jingwentian.com'); $artlist = pq(".post-list > .item-content"); foreach($artlist as $li){ echo pq($li)->find('h1')->html()."<br>"; }


通過循環列表中的DIV,找出文章標題並輸出,就是這么簡單。

解析XML文檔

假設現在有一個這樣的test.xml文檔:

<?xml version="1.0" encoding="utf-8"?> <root> <contact> <name>張三</name> <age>22</age> </contact> <contact> <name>王五</name> <age>18</age> </contact> </root>


現在我要獲取名字為張三的聯系人的年齡,代碼如下:

include 'phpQuery/phpQuery.php'; phpQuery::newDocumentFile('test.xml'); echo pq('contact > age:eq(0)');


結果輸出:22

像jQuery一樣,精准查找文檔節點,輸出節點下的內容,解析一個XML文檔就是這么簡單。現在你不必為采集網站內容而使用那些頭疼的正則算法、內容替換等繁瑣的代碼了,有了phpQuery,一切就變得輕松多了。

 

 

一、phpQuery的hello word!

下面簡單舉例:

include 'phpQuery.php'; phpQuery::newDocumentFile('http://www.phper.org.cn'); echo pq("title")->text(); // 獲取網頁標題 echo pq("div#header")->html(); // 獲取id為header的div的html內容

 上例中第一行引入phpQuery.php文件,

第二行通過newDocumentFile加載一個文件,

第三行通過pq()函數獲取title標簽的文本內容,

第四行獲取id為header的div標簽所包含的HTML內容。

主要做了兩個動作,即加載文件和讀取文件內容。

 

二、載入文檔(loading documents)

加載文檔主要通過phpQuery::newDocument來進行操作,其作用是使得phpQuery可以在服務器預先讀取到指定的文件或文本內容。

主要的方法包括:

phpQuery::newDocument($html, $contentType = null)

phpQuery::newDocumentFile($file, $contentType = null)

phpQuery::newDocumentHTML($html, $charset = ‘utf-8′)

phpQuery::newDocumentXHTML($html, $charset = ‘utf-8′)

phpQuery::newDocumentXML($html, $charset = ‘utf-8′)

phpQuery::newDocumentPHP($html, $contentType = null)

phpQuery::newDocumentFileHTML($file, $charset = ‘utf-8′)

phpQuery::newDocumentFileXHTML($file, $charset = ‘utf-8′)

phpQuery::newDocumentFileXML($file, $charset = ‘utf-8′)

phpQuery::newDocumentFilePHP($file, $contentType) 

 

三、pq()函數用法

pq()函數的用法是phpQuery的重點,主要分兩部分:即選擇器和過濾器

【選擇器】

要了解phpQuery選擇器的用法,建議先了解jQuery的語法

最常用的語法包括有:

pq('#id'):即以#號開頭的ID選擇器,用於選擇已知ID的容器所包括的內容

pq('.classname'):即以.開頭的class選擇器,用於選擇class匹配的容器內容

pq('parent > child'):選擇指定層次結構的容器內容,如:pq('.main > p')用於選擇class=main容器的所有p標簽

更多的語法請參考jQuery手冊

【過濾器】

主要包括::first,:last,:not,:even,:odd,:eq(index),:gt(index),:lt(index),:header,:animated等

如:

pq('p:last'):用於選擇最后一個p標簽

pq('tr:even'):用於選擇表格中偶然行

 

四、phpQuery連貫操作

pq()函數返回的結果是一個phpQuery對象,可以對返回結果繼續進行后續的操作,例如:

 pq('a')->attr('href', 'newVal')->removeClass('className')->html('newHtml')->...

詳情請查閱jQuery相關資料,用法基本一致,只需要注意.與->的區別即可。

 

 

------------------------------------------------------------------------------------------------------------

官方文檔自學。

  1. Basics
  2. Ported jQuery sections
    1. Selectors
    2. Attributes
    3. Traversing
    4. Manipulation
    5. Ajax
    6. Events
    7. Utilities
    8. Plugin ports
  3. PHP Support
  4. Command Line Interface
  5. Multi document support
  6. Plugins
    1. WebBrowser
    2. Scripts
  7. jQueryServer
  8. Debugging
  9. Bootstrap file

 

001

Loading documents 加載文件 文檔

phpQuery::newDocument($html, $contentType = null) Creates new document from markup. If no $contentType, autodetection is made (based on markup). If it fails, text/html in utf-8 is used. 從標記 建立新文檔自動識別 識別失敗 自動默認utf8

phpQuery::newDocumentFile($file, $contentType = null) Creates new document from file. Works like newDocument() 從文件建立文檔

phpQuery::newDocumentHTML($html, $charset = 'utf-8') 從html文件建立

phpQuery::newDocumentXHTML($html, $charset = 'utf-8') 從xhtml建立
phpQuery::newDocumentXML($html, $charset = 'utf-8') 從xml建立
phpQuery::newDocumentPHP($html, $contentType = null) Read more about it on PHPSupport page 從php建立(詳情閱讀https://code.google.com/p/phpquery/wiki/PHPSupport)


phpQuery::newDocumentFileHTML($file, $charset = 'utf-8')
phpQuery::newDocumentFileXHTML($file, $charset = 'utf-8')
phpQuery::newDocumentFileXML($file, $charset = 'utf-8')
phpQuery::newDocumentFilePHP($file, $contentType) Read more about it on PHPSupport page


pq function pq方法

pq($param, $context = null); 相當於jquery的$(); 有三種使用方式

01.Importing markup 導入標記

// Import into selected document: 導入要選擇的標記
// doesn't accept text nodes at beginning of input string
// 在開頭不接受文本節點
pq('<div/>')
// Import into document with ID from $pq->getDocumentID():
// 從 $pq->getDocumentID():文檔中選擇 id為獲取id的那個
pq('<div/>', $pq->getDocumentID())

// Import into same document as DOMNode belongs to:
// 導入相同的文檔作為dom
pq('<div/>', DOMNode)
// Import into document from phpQuery object:
// 從對象導入
pq('<div/>', $pq)


02.Running queries 運行查詢 查找
// Run query on last selected document: 在最后選定的文檔上查詢。查找div 且class為。。。
pq('div.myClass')


// Run query on document with ID from $pq->getDocumentID(): 根據id查詢
pq('div.myClass', $pq->getDocumentID())

// Run query on same document as DOMNode belongs to and use node(s)as root for query:DOMNode屬於同一文檔上運行查詢和使用節點作為根用戶(s)查詢
pq('div.myClass', DOMNode)
// Run query on document from phpQuery object
//
// and use object's stack as root node(s) for query:
// 使用對象的棧作為查詢的根節點(s
pq('div.myClass', $pq)

03.Wrapping DOMNodes with phpQuery objects 用對象包裝dom文件
foreach(pq('li') as $li)
// $li is pure DOMNode, change it to phpQuery object
// $li是純dom節點 把它變成phpquery對象
pq($li);

 

手冊第二部分

  1. Ported jQuery sections

phpQuery幾乎是一個完整的jQuery JavaScript庫

Ported Sections 

  1. Selectors 選擇器
  2. Attributes
  3. Traversing
  4. Manipulation
  5. Ajax
  6. Events
  7. Utilities
  8. Plugin ports

Additional methods 額外方法 附加方法

phpQuery features many additional methods comparing to jQuery:

 

Other Differences 其他的不同

選擇器:

Selectors  
 

Selectors are the heart of jQuery-like interface. Most of CSS Level 3 syntax is implemented (in state same as in jQuery).選擇器是jQuery-like接口的核心

Example

pq(".class ul > li[rel='foo']:first:has(a)")->appendTo('.append-target-wrapper div')->...

Basics 基礎

Hierarchy 層級

Basic Filters 基礎過濾

  • :first Matches the first selected element. 選在第一個選定的元素
  • :last Matches the last selected element.選擇最后一個選定的元素
  • :not(selector) Filters out all elements matching the given selector. 過濾掉所有給定的選擇器的元素
  • :even Matches even elements, zero-indexed.
  • :odd Matches odd elements, zero-indexed.
  • :eq(index) Matches a single element by its index.匹配單個元素的索引( 位置 第幾個)
  • :gt(index) Matches all elements with an index above the given one. 匹配大於的
  • :lt(index) Matches all elements with an index below the given one. 匹配小於的
  • :header Matches all elements that are headers, like h1, h2, h3 and so on.標題匹配所有元素,如h1,h2,h3等等
  • :animated Matches all elements that are currently being animated.匹配當前正在動畫發展的所有元素 。

Content Filters 內容過濾

  • :contains(text) 匹配元素包含給定的文本
  • :empty 匹配無子元素的 包括文本的
  • :has(selector) 匹配元素包含至少一個指定selecto相匹配的元素
  • :parent 匹配所有父元素(包含元素的元素)

Visibility Filters可見過濾

Attribute Filters屬性過濾

Child Filters 子元素過濾

  • :nth-child(index/even/odd/equation) Matches all elements that are the nth-child of their parent or that are the parent's even or odd children.
  • 匹配所有元素的nth-child父母或父母的偶數或奇數的孩子。
  • :first-child Matches all elements that are the first child of their parent.第一個孩子元素
  • :last-child Matches all elements that are the last child of their parent.最后一個孩子元素
  • :only-child Matches all elements that are the only child of their parent. 僅有的子元素

Forms 表單

  • :input Matches all input, textarea, select and button elements. 所有輸入
  • :text Matches all input elements of type text. text的
  • :password Matches all input elements of type password. 密碼輸入框的
  • :radio Matches all input elements of type radio. 單選按鈕
  • :checkbox Matches all input elements of type checkbox. 復選框
  • :submit Matches all input elements of type submit.提交
  • :image Matches all input elements of type image.圖片
  • :reset Matches all input elements of type reset. 重置
  • :button Matches all button elements and input elements of type button.按鈕
  • :file Matches all input elements of type file.文件
  • :hidden Matches all elements that are hidden, or input elements of type "hidden".隱藏

Form Filters 表單過濾

  • :enabled Matches all elements that are enabled. 啟用的元素
  • :disabled Matches all elements that are disabled.禁用的元素
  • :checked Matches all elements that are checked.勾選的
  • :selected Matches all elements that are selected. 被選中的

 

***************

屬性

pq('a')->attr('href', 'newVal')->removeClass('className')->html('newHtml')->...

 

**********

Attr

  • attr($name) 獲取第一個匹配元素的一個屬性。該方法便於檢索第一個匹配元素的屬性值。如果元素沒有這樣一個名字,一個屬性將返回未定義。
  • attr($properties) 鍵/值對象設置為所有匹配的元素屬性
  • attr($key, $value) 將所有匹配元素的單個值設置為。。。
  • attr($key, $fn) 一個屬性設置為一個計算值,在所有匹配的元素
  • removeAttr($name) 從每一個匹配的元素中刪除一個屬性

Class

  • addClass($class) Adds the specified class(es) to each of the set of matched elements.
  • hasClass($class) Returns true if the specified class is present on at least one of the set of matched elements.
  • removeClass($class) Removes all or the specified class(es) from the set of matched elements.
  • toggleClass($class) Adds the specified class if it is not present, removes the specified class if it is present.

HTML

  • html() 獲得第一個匹配元素的html內容(innerHTML)。這個屬性是不可以在XML文檔(盡管它將為XHTML文檔工作)。
  • html($val) 設置每一個匹配元素的html內容。這個屬性是不可以在XML文檔(盡管它將為XHTML文檔工作)。

Text

  • text() 把所有匹配的元素的文本內容相結合.
  • text($val) 所有匹配的元素的文本內容

Value

  • val() 得到的內容第一個匹配元素的屬性值 獲取屬性
  • val($val) 設置每一個匹配元素的屬性值。
  • val($val) Checks, or selects, all the radio buttons, checkboxes, and select options that match the set of values.

 

**************

Traversing  

pq('div > p')->add('div > ul')->filter(':has(a)')->find('p:first')->nextAll()->andSelf()->...

**********

Filtering 過濾

  • eq($index) Reduce the set of matched elements to a single element.
  • hasClass($class) Checks the current selection against a class and returns true, if at least one element of the selection has the given class.
  • filter($expr) Removes all elements from the set of matched elements that do not match the specified expression(s).
  • filter($fn) Removes all elements from the set of matched elements that does not match the specified function.
  • is($expr) Checks the current selection against an expression and returns true, if at least one element of the selection fits the given expression.
  • map($callback) Translate a set of elements in the jQuery object into another set of values in an array (which may, or may not, be elements).
  • not($expr) Removes elements matching the specified expression from the set of matched elements.
  • slice($start, $end) Selects a subset of the matched elements.

Finding 查找

  • add($expr) Adds more elements, matched by the given expression, to the set of matched elements.
  • children($expr) Get a set of elements containing all of the unique immediate children of each of the matched set of elements.
  • contents() Find all the child nodes inside the matched elements (including text nodes), or the content document, if the element is an iframe.
  • find($expr) Searches for all elements that match the specified expression. This method is a good way to find additional descendant elements with which to process.
  • next($expr) Get a set of elements containing the unique next siblings of each of the given set of elements.
  • nextAll($expr) Find all sibling elements after the current element.
  • parent($expr) Get a set of elements containing the unique parents of the matched set of elements.
  • parents($expr) Get a set of elements containing the unique ancestors of the matched set of elements (except for the root element). The matched elements can be filtered with an optional expression.
  • prev($expr) Get a set of elements containing the unique previous siblings of each of the matched set of elements.
  • prevAll($expr) Find all sibling elements before the current element.
  • siblings($expr) Get a set of elements containing all of the unique siblings of each of the matched set of elements. Can be filtered with an optional expressions.

Chaining連接

  • andSelf() Add the previous selection to the current selection.
  • end() Revert the most recent 'destructive' operation, changing the set of matched elements to its previous state (right before the destructive operation).

Read more at Traversing section on jQuery Documentation Site.

 

 

*****************

Manipulation  
Updated  Feb 4, 2010 by tobiasz....@gmail.com

Example

pq('div.old')->replaceWith( pq('div.new')->clone() )->appendTo('.trash')->prepend('Deleted')->...

Table of Contents

Changing Contents

  • html() Get the html contents (innerHTML) of the first matched element. This property is not available on XML documents (although it will work for XHTML documents).
  • html($val) Set the html contents of every matched element. This property is not available on XML documents (although it will work for XHTML documents).
  • text() Get the combined text contents of all matched elements.
  • text($val) Set the text contents of all matched elements.

Inserting Inside

Inserting Outside

Inserting Around

  • wrap($html) Wrap each matched element with the specified HTML content.
  • wrap($elem) Wrap each matched element with the specified element.
  • wrapAll($html) Wrap all the elements in the matched set into a single wrapper element.
  • wrapAll($elem) Wrap all the elements in the matched set into a single wrapper element.
  • wrapInner($html) Wrap the inner child contents of each matched element (including text nodes) with an HTML structure.
  • wrapInner($elem) Wrap the inner child contents of each matched element (including text nodes) with a DOM element.

Replacing

Removing

  • empty() Remove all child nodes from the set of matched elements.
  • remove($expr) Removes all matched elements from the DOM.

Copying

  • clone() Clone matched DOM Elements and select the clones.
  • clone($true) Clone matched DOM Elements, and all their event handlers, and select the clones.

Read more at Manipulation section on jQuery Documentation Site.

 

************************8

Ajax  
Updated  Feb 4, 2010 by tobiasz....@gmail.com

Example

pq('#element')->load('http://somesite.com/page .inline-selector')->...

Table of Contents

Server Side Ajax

Ajax, standing for Asynchronous JavaScript and XML is combination of HTTP Client and XML parser which doesn't lock program's thread (doing request in asynchronous way).

phpQuery also offers such functionality, making use of solid quality Zend_Http_Client. Unfortunately requests aren't asynchronous, but nothing is impossible. For today, instead of XMLHttpRequest you always get Zend_Http_Client instance. API unification is planned.

Cross Domain Ajax

For security reasons, by default phpQuery doesn't allow connections to hosts other than actual $_SERVER['HTTP_HOST']. Developer needs to grant rights to other hosts before making an Ajax request.

There are 2 methods for allowing other hosts

  • phpQuery::ajaxAllowURL($url)
  • phpQuery::ajaxAllowHost($host)

 

// connect to google.com
phpQuery::ajaxAllowHost('google.com');
phpQuery::get('http://google.com/ig');
// or using same string
$url = 'http://google.com/ig';
phpQuery::ajaxAllowURL($url);
phpQuery::get($url);

Ajax Requests

Ajax Events

  • ajaxComplete($callback) Attach a function to be executed whenever an AJAX request completes. This is an Ajax Event.
  • ajaxError($callback) Attach a function to be executed whenever an AJAX request fails. This is an Ajax Event.
  • ajaxSend($callback) Attach a function to be executed before an AJAX request is sent. This is an Ajax Event.
  • ajaxStart($callback) Attach a function to be executed whenever an AJAX request begins and there is none already active. This is an Ajax Event.
  • ajaxStop($callback) Attach a function to be executed whenever all AJAX requests have ended. This is an Ajax Event.
  • ajaxSuccess($callback) Attach a function to be executed whenever an AJAX request completes successfully. This is an Ajax Event.

Misc

  • phpQuery::ajaxSetup($options) Setup global settings for AJAX requests.
  • serialize() Serializes a set of input elements into a string of data. This will serialize all given elements.
  • serializeArray() Serializes all forms and form elements (like the .serialize() method) but returns a JSON data structure for you to work with.

Options

Detailed options description in available at jQuery Documentation Site.

  • async Boolean
  • beforeSend Function
  • cache Boolean
  • complete Function
  • contentType String
  • data Object, String
  • dataType String
  • error Function
  • global Boolean
  • ifModified Boolean
  • jsonp String
  • password String
  • processData Boolean
  • success Function
  • timeout Number
  • type String
  • url String
  • username String

********************

Events  
Updated  Feb 4, 2010 by tobiasz....@gmail.com

Table of Contents

Example

pq('form')->bind('submit', 'submitHandler')->trigger('submit')->...
function submitHandler($e) {
  print 'Target: '.$e->target->tagName;
  print 'Bubbling ? '.$e->currentTarget->tagName;
}

Server Side Events

phpQuery support server-side events, same as jQuery handle client-side ones. On server there isn't, of course, events such as mouseover (but they can be triggered).

By default, phpQuery automatically fires up only change event for form elements. If you load WebBrowser plugin, submit and click will be handled properly - eg submitting form with inputs' data to action URL via new Ajax request.

$this (this in JS) context for handler scope isn't available. You have to use one of following manually:

  • $event->target
  • $event->currentTarget
  • $event->relatedTarget

 

Page Load

none

Event Handling

  • bind($type, $data, $fn) Binds a handler to one or more events (like click) for each matched element. Can also bind custom events.
  • one($type, $data, $fn) Binds a handler to one or more events to be executed once for each matched element.
  • trigger($type , $data ) Trigger a type of event on every matched element.
  • triggerHandler($type , $data ) This particular method triggers all bound event handlers on an element (for a specific event type) WITHOUT executing the browsers default actions.
  • unbind($type , $data ) This does the opposite of bind, it removes bound events from each of the matched elements.

Interaction Helpers

none

Event Helpers

  • change() Triggers the change event of each matched element.
  • change($fn) Binds a function to the change event of each matched element.
  • submit() Trigger the submit event of each matched element.
  • submit($fn) Bind a function to the submit event of each matched element.

Read more at Events section on jQuery Documentation Site.

 

**********

Utilities  
Updated  Feb 4, 2010 by tobiasz....@gmail.com

Table of Contents

User Agent

none

Array and Object operations

Test operations

String operations

Read more at Utilities section on jQuery Documentation Site.

 

 

Examples

CLI

Fetch number of downloads of all release packages

phpquery 'http://code.google.com/p/phpquery/downloads/list?can=1' \
  --find '.vt.col_4 a' --contents \
  --getString null array_sum

PHP

Examples from demo.php

require('phpQuery/phpQuery.php');
// for PEAR installation use this
// require('phpQuery.php');

INITIALIZE IT

// $doc = phpQuery::newDocumentHTML($markup);
// $doc = phpQuery::newDocumentXML();
// $doc = phpQuery::newDocumentFileXHTML('test.html');
// $doc = phpQuery::newDocumentFilePHP('test.php');
// $doc = phpQuery::newDocument('test.xml', 'application/rss+xml');
// this one defaults to text/html in utf8
$doc = phpQuery::newDocument('<div/>');

FILL IT

// array syntax works like ->find() here
$doc['div']->append('<ul></ul>');
// array set changes inner html
$doc['div ul'] = '<li>1</li><li>2</li><li>3</li>';

MANIPULATE IT

// almost everything can be a chain
$li = null;
$doc['ul > li']
        ->addClass('my-new-class')
        ->filter(':last')
                ->addClass('last-li')
// save it anywhere in the chain
                ->toReference($li);

SELECT DOCUMENT

// pq(); is using selected document as default
phpQuery::selectDocument($doc);
// documents are selected when created or by above method
// query all unordered lists in last selected document
pq('ul')->insertAfter('div');

ITERATE IT

// all LIs from last selected DOM
foreach(pq('li') as $li) {
        // iteration returns PLAIN dom nodes, NOT phpQuery objects
        $tagName = $li->tagName;
        $childNodes = $li->childNodes;
        // so you NEED to wrap it within phpQuery, using pq();
        pq($li)->addClass('my-second-new-class');
}

PRINT OUTPUT

// 1st way
print phpQuery::getDocument($doc->getDocumentID());
// 2nd way
print phpQuery::getDocument(pq('div')->getDocumentID());
// 3rd way
print pq('div')->getDocument();
// 4th way
print $doc->htmlOuter();
// 5th way
print $doc;
// another...
print $doc['ul'];


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM