一、php爬蟲框架有不少,包括不少傻瓜式的軟件php
二、照之前寫過java爬蟲的例子來看,真的很是簡單,就是一個獲取網頁數據的類或者方法(這裏的話$handle = fopen($url, "r");
),而後就能夠獲取網頁上的html源代碼,而後取啥數據就用正則表達式來取好了 html$content = stream_get_contents($handle, -1);
a、網頁的頁面源碼咱們能夠輕鬆得到 java
b、好比cnsd博客,文章的正文內容所有放在<article></article>當中,因此很是好獲取,此時咱們獲取的是html的頁面,不一樣網站標籤不一樣,具體看狀況就好node
c、html的數據自帶格式,因此直接放到數據庫便可,由於數據庫裏面存的也就是html數據,要顯示的話直接把這部分數據放到頁面上面來便可python
爬蟲是我一直以來躍躍欲試的技術,如今的爬蟲框架不少,比較流行的是基於python,nodejs,java,C#,PHP的的框架,其中又以基於python的爬蟲流行最爲普遍,還有的已是一套傻瓜式的軟件操做,如八爪魚,火車頭等軟件。mysql
今天咱們首先嚐試的是使用PHP實現一個爬蟲程序,首先在不使用爬蟲框架的基礎上實踐也是爲了理解爬蟲的原理,而後再利用PHP的lib,框架和擴展進行實踐。git
全部代碼掛在個人github上。github
爬蟲的原理:web
下面是網上一個例子,咱們列下來而後分析
從main
函數開始正則表達式
<?php
/** * 爬蟲程序 -- 原型 * * 從給定的url獲取html內容 * * @param string $url * @return string */
function _getUrlContent($url) {
$handle = fopen($url, "r");
if ($handle) {
$content = stream_get_contents($handle, -1);
//讀取資源流到一個字符串,第二個參數須要讀取的最大的字節數。默認是-1(讀取所有的緩衝數據)
// $content = file_get_contents($url, 1024 * 1024);
return $content;
} else {
return false;
}
}
/** * 從html內容中篩選連接 * * @param string $web_content * @return array */
function _filterUrl($web_content) {
$reg_tag_a = '/<[a|A].*?href=[\'\"]{0,1}([^>\'\"\ ]*).*?>/';
$result = preg_match_all($reg_tag_a, $web_content, $match_result);
if ($result) {
return $match_result[1];
}
}
/** * 修正相對路徑 * * @param string $base_url * @param array $url_list * @return array */
function _reviseUrl($base_url, $url_list) {
$url_info = parse_url($base_url);//解析url
$base_url = $url_info["scheme"] . '://';
if ($url_info["user"] && $url_info["pass"]) {
$base_url .= $url_info["user"] . ":" . $url_info["pass"] . "@";
}
$base_url .= $url_info["host"];
if ($url_info["port"]) {
$base_url .= ":" . $url_info["port"];
}
$base_url .= $url_info["path"];
print_r($base_url);
if (is_array($url_list)) {
foreach ($url_list as $url_item) {
if (preg_match('/^http/', $url_item)) {
// 已是完整的url
$result[] = $url_item;
} else {
// 不完整的url
$real_url = $base_url . '/' . $url_item;
$result[] = $real_url;
}
}
return $result;
} else {
return;
}
}
/** * 爬蟲 * * @param string $url * @return array */
function crawler($url) {
$content = _getUrlContent($url);
if ($content) {
$url_list = _reviseUrl($url, _filterUrl($content));
if ($url_list) {
return $url_list;
} else {
return ;
}
} else {
return ;
}
}
/** * 測試用主程序 */
function main() {
$file_path = "url-01.txt";
$current_url = "http://www.baidu.com/"; //初始url
if(file_exists($file_path)){
unlink($file_path);
}
$fp_puts = fopen($file_path, "ab"); //記錄url列表
$fp_gets = fopen($file_path, "r"); //保存url列表
do {
$result_url_arr = crawler($current_url);
if ($result_url_arr) {
foreach ($result_url_arr as $url) {
fputs($fp_puts, $url . "\r\n");
}
}
} while ($current_url = fgets($fp_gets, 1024)); //不斷得到url
}
main();
?>
Curl是比較成熟的一個lib,異常處理、http header、POST之類都作得很好,重要的是PHP下操做MySQL進行入庫操做比較省心。關於curl的說明具體能夠查看PHP官方文檔說明http://php.net/manual/zh/book.curl.php
不過在多線程Curl(Curl_multi)方面比較麻煩。
開啓crul
針對winow系統:
- php.in中修改(註釋;去掉便可)
extension=php_curl.dll
使用crul爬蟲的步驟:
- 使用cURL函數的基本思想是先使用curl_init()初始化一個cURL會話;
- 接着你能夠經過curl_setopt()設置你須要的所有選項;
- 而後使用curl_exec()來執行會話;
- 當執行完會話後使用curl_close()關閉會話。
例子
<?php
$ch = curl_init("http://www.example.com/");
$fp = fopen("example_homepage.txt", "w");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
?>
一個完整點的例子:
<?php
/** * 將demo1-01換成curl爬蟲 * 爬蟲程序 -- 原型 * 從給定的url獲取html內容 * @param string $url * @return string */
function _getUrlContent($url) {
$ch=curl_init(); //初始化一個cURL會話
/*curl_setopt 設置一個cURL傳輸選項*/
//設置須要獲取的 URL 地址
curl_setopt($ch,CURLOPT_URL,$url);
//TRUE 將curl_exec()獲取的信息以字符串返回,而不是直接輸出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//啓用時會將頭文件的信息做爲數據流輸出
curl_setopt($ch,CURLOPT_HEADER,1);
// 設置瀏覽器的特定header
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Host: www.baidu.com",
"Connection: keep-alive",
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests: 1",
"DNT:1",
"Accept-Language: zh-CN,zh;q=0.8,en-GB;q=0.6,en;q=0.4,en-US;q=0.2",
/*'Cookie:_za=4540d427-eee1-435a-a533-66ecd8676d7d; */
));
$result=curl_exec($ch);//執行一個cURL會話
$code=curl_getinfo($ch,CURLINFO_HTTP_CODE);// 最後一個收到的HTTP代碼
if($code!='404' && $result){
return $result;
}
curl_close($ch);//關閉cURL
}
/** * 從html內容中篩選連接 * @param string $web_content * @return array */
function _filterUrl($web_content) {
$reg_tag_a = '/<[a|A].*?href=[\'\"]{0,1}([^>\'\"\ ]*).*?>/';
$result = preg_match_all($reg_tag_a, $web_content, $match_result);
if ($result) {
return $match_result[1];
}
}
/** * 修正相對路徑 * @param string $base_url * @param array $url_list * @return array */
function _reviseUrl($base_url, $url_list) {
$url_info = parse_url($base_url);//解析url
$base_url = $url_info["scheme"] . '://';
if ($url_info["user"] && $url_info["pass"]) {
$base_url .= $url_info["user"] . ":" . $url_info["pass"] . "@";
}
$base_url .= $url_info["host"];
if ($url_info["port"]) {
$base_url .= ":" . $url_info["port"];
}
$base_url .= $url_info["path"];
print_r($base_url);
if (is_array($url_list)) {
foreach ($url_list as $url_item) {
if (preg_match('/^http/', $url_item)) {
// 已是完整的url
$result[] = $url_item;
} else {
// 不完整的url
$real_url = $base_url . '/' . $url_item;
$result[] = $real_url;
}
}
return $result;
} else {
return;
}
}
/** * 爬蟲 * @param string $url * @return array */
function crawler($url) {
$content = _getUrlContent($url);
if ($content) {
$url_list = _reviseUrl($url, _filterUrl($content));
if ($url_list) {
return $url_list;
} else {
return ;
}
} else {
return ;
}
}
/** * 測試用主程序 */
function main() {
$file_path = "./url-03.txt";
if(file_exists($file_path)){
unlink($file_path);
}
$current_url = "http://www.baidu.com"; //初始url
//記錄url列表 ab- 追加打開一個二進制文件,並在文件末尾寫數據
$fp_puts = fopen($file_path, "ab");
//保存url列表 r-只讀方式打開,將文件指針指向文件頭
$fp_gets = fopen($file_path, "r");
do {
$result_url_arr = crawler($current_url);
echo "<p>$current_url</p>";
if ($result_url_arr) {
foreach ($result_url_arr as $url) {
fputs($fp_puts, $url . "\r\n");
}
}
} while ($current_url = fgets($fp_gets, 1024)); //不斷得到url
}
main();
?>
要對https支持,須要在_getUrlContent
函數中加入下面的設置:
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ) ;
curl_setopt($ch, CURLOPT_USERPWD, "username:password");
curl_setopt($ch, CURLOPT_SSLVERSION,3);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
結果疑惑:
咱們經過1和2部分獲得的結果差別很大,第1部分能獲得四千多條url數據,而第2部分卻一直是45條數據。
還有咱們得到url數據可能會有重複的,這部分處理在個人github上,對應demo2-01.php,或者demo2-02.php
$handle = fopen($url, "r");
$content = stream_get_contents($handle, -1);//讀取資源流到一個字符串,第二個參數須要讀取的最大的字節數。默認是-1(讀取所有的緩衝數據)
$content = file_get_contents($url, 1024 * 1024);
【注】 若是要打開有特殊字符的 URL (好比說有空格),就須要使用進行 URL 編碼。
php中file_get_contents與curl性能比較分析一文中有詳細的對比分析,主要的對比如今列下來:
- fopen /file_get_contents 每次請求都會從新作DNS查詢,並不對 DNS信息進行緩存。可是CURL會自動對DNS信息進行緩存。對同一域名下的網頁或者圖片的請求只須要一次DNS查詢。這大大減小了DNS查詢的次數。因此CURL的性能比fopen /file_get_contents 好不少。
fopen /file_get_contents 在請求HTTP時,使用的是http_fopen_wrapper,不會keeplive。而curl卻能夠。這樣在屢次請求多個連接時,curl效率會好一些。
fopen / file_get_contents 函數會受到php.ini文件中allow_url_open選項配置的影響。若是該配置關閉了,則該函數也就失效了。而curl不受該配置的影響。
curl 能夠模擬多種請求,例如:POST數據,表單提交等,用戶能夠按照本身的需求來定製請求。而fopen / file_get_contents只能使用get方式獲取數據。
使用框架這一塊打算之後單獨研究,並拿出來單寫一篇博客
全部代碼掛在個人github上。
參考閱讀:
- 我用爬蟲一天時間「偷了」知乎一百萬用戶,只爲證實PHP是世界上最好的語言
- 知乎 – PHP, Python, Node.js 哪一個比較適合寫爬蟲?
- 最近關於對網絡爬蟲技術總結
- PHP實現簡單爬蟲 (http://www.oschina.net/code/snippet_258733_12343)]
- 一個PHP實現的輕量級簡單爬蟲
- php中file_get_contents與curl性能比較分析
- PHP curl之爬蟲初步
- 開源中國-PHP爬蟲框架列表
- 網頁抓取:PHP實現網頁爬蟲方式小結,抓取爬蟲
- PHP爬蟲框架–PHPCrawl
- php安裝pcntl擴展實現多進程