c網路爬蟲源碼
A. 怎麼在DOS下用c語言寫網路爬蟲
獲取cspider_t。
自定義user agent,cookie,timeout,proxy以及抓取線程和解析線程的最大數量。
添加初始要抓取的url到任務隊列。
編寫解析函數和數據持久化函數。
啟動爬蟲。
- #include<cspider/spider.h>
- /*
- 自定義的解析函數,d為獲取到的html頁面字元串
- */
- void p(cspider_t *cspider, char *d, void *user_data) {
- char *get[100];
- //xpath解析html
- int size = xpath(d, "//body/div[@class='wrap']/div[@class='sort-column area']/div[@class='column-bd cfix']/ul[@class='st-list cfix']/li/strong/a", get, 100);
- int i;
- for (i = 0; i < size; i++) {
- //將獲取到的電影名稱,持久化
- saveString(cspider, get[i]);
- }
- }
- /*
- 數據持久化函數,對上面解析函數中調用的saveString()函數傳入的數據,進行進一步的保存
- */
- void s(void *str, void *user_data) {
- char *get = (char *)str;
- FILE *file = (FILE*)user_data;
- fprintf(file, "%s ", get);
- return;
- }
- int main() {
- //初始化spider
- cspider_t *spider = init_cspider();
- char *agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0";
- //char *cookie = "bid=s3/yuH5Jd/I; ll=108288; viewed=1130500_24708145_6433169_4843567_1767120_5318823_1899158_1271597; __utma=30149280.927537245.1446813674.1446983217.1449139583.4; __utmz=30149280.1449139583.4.4.utmcsr=accounts.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/login; ps=y; [email protected]; dbcl2=58742090:QgZ2PSLiDLQ; ck=T9Wn; push_noty_num=0; push_doumail_num=7; ap=1; __utmb=30149280.0.10.1449139583; __utmc=30149280";
- //設置要抓取頁面的url
- cs_setopt_url(spider, "so.tv.sohu.com/list_p1100_p20_p3_u5185_u5730_p40_p5_p6_p77_p80_p9_2d1_p101_p11.html");
- //設置user agent
- cs_setopt_useragent(spider, agent);
- //cs_setopt_cookie(spider, cookie);
- //傳入解析函數和數據持久化函數的指針
- cs_setopt_process(spider, p, NULL);
- //s函數的user_data指針指向stdout
- cs_setopt_save(spider, s, stdout);
- //設置線程數量
- cs_setopt_threadnum(spider, DOWNLOAD, 2);
- cs_setopt_threadnum(spider, SAVE, 2);
- //FILE *fp = fopen("log", "wb+");
- //cs_setopt_logfile(spider, fp);
- //開始爬蟲
- return cs_run(spider);
- }
例子
先來看下簡單的爬蟲例子,會在後面詳細講解例子。
B. 網路爬蟲 spider
「網路蜘蛛」或者說「網路爬蟲」,是一種能訪問網站並跟蹤鏈接的程序,通過它,可快速地畫出一個網站所包含的網頁地圖信息。本文主要講述如何使用java編程來構建一個「蜘蛛」,我們會先以一個可復用的蜘蛛類包裝一個基本的「蜘蛛」,並在示常式序中演示如何創建一個特定的「蜘蛛」來掃描相關網站並找出死鏈接。 Java語言在此非常適合構建一個「蜘蛛」程序,其內建了對HTTP協議的支持,通過它可以傳輸大部分的網頁信息;其還內建了一個HTML解析器,正是這兩個原因使Java語言成為本文構建「蜘蛛」程序的首選。 文章後面例1的示常式序,將會掃描一個網站,並尋找死鏈接。使用這個程序時需先輸入一個URL並單擊「Begin」按鈕,程序開始之後,「Begin」按鈕會變成「Cancel」按鈕。在程序掃描網站期間,會在「Cancel」按鈕之下顯示進度,且在檢查當前網頁時,也會顯示相關正常鏈接與死鏈接的數目,死鏈接將顯示在程序底部的滾動文本框中。單擊「Cancel」按鈕會停止掃描過程,之後可以輸入一個新的URL;如果期間沒有單擊「Cancel」,程序將會一直運行直到查找完所有網頁,此後,「Cancel」按鈕會再次變回「Begin」,表示程序已停止。 下面將演示示常式序是如何與可復用「Spider」類交互的,示常式序包含在例1的CheckLinks類中,這個類實現了ISpiderReportable介面,如例2所示,正是通過這個介面,蜘蛛類才能與示常式序相交互。在這個介面中,定義了三個方法:第一個方法是「spiderFoundURL」,它在每次程序定位一個URL時被調用,如果方法返回true,表示程序應繼續執行下去並找出其中的鏈接;第二個方法是「spiderURLError」,它在每次程序檢測URL導致錯誤時被調用(如「404 頁面未找到」);第三個方法是「spiderFoundEMail」,它在每次發現電子郵件地址時被調用。有了這三個方法,Spider類就能把相關信息反饋給創建它的程序了。 在begin方法被調用後,「蜘蛛」就開始工作了;為允許程序重繪其用戶界面,「蜘蛛」是作為一個單獨的線程啟動的。點擊「Begin」按鈕會開始這個後台線程,當後台線程運行之後,又會調用「CheckLinks」類的run方法,而run方法是由Spider對象實例化時啟動的,如下所示: spider = new Spider(this); spider.clear(); base = new URL(url.getText()); spider.addURL(base); spider.begin(); 首先,一個新的Spider對象被實例化,在此,需要傳遞一個「ISpiderReportable」對象給Spider對象的構造函數,因為「CheckLinks」類實現了「ISpiderReportable」介面,只需簡單地把它作為當前對象(可由關鍵字this表示)傳遞給構造函數即可;其次,在程序中維護了一個其訪問過的URL列表,而「clear」方法的調用則是為了確保程序開始時URL列表為空,程序開始運行之前必須添加一個URL到它的待處理列表中,此時用戶輸入的URL則是添加到列表中的第一個,程序就由掃描這個網頁開始,並找到與這個起始URL相鏈接的其他頁面;最後,調用「begin」方法開始運行「蜘蛛」,這個方法直到「蜘蛛」工作完畢或用戶取消才會返回。 當「蜘蛛」運行時,可以調用由「ISpiderReportable」介面實現的三個方法來報告程序當前狀態,程序的大部分工作都是由「spiderFoundURL」方法來完成的,當「蜘蛛」發現一個新的URL時,它首先檢查其是否有效,如果這個URL導致一個錯誤,就會把它當作一個死鏈接;如果鏈接有效,就會繼續檢查它是否在一個不同的伺服器上,如果鏈接在同一伺服器上,「spiderFoundURL」返回true,表示「蜘蛛」應繼續跟蹤這個URL並找出其他鏈接,如果鏈接在另外的伺服器上,就不會掃描是否還有其他鏈接,因為這會導致「蜘蛛」不斷地瀏覽Internet,尋找更多、更多的網站,所以,示常式序只會查找用戶指定網站上的鏈接。 構造Spider類 前面已經講了如何使用Spider類,請看例3中的代碼。使用Spider類及「ISpiderReportable」介面能方便地為某一程序添加「蜘蛛」功能,下面繼續講解Spider類是怎樣工作的。 Spider類必須保持對其訪問過的URL的跟蹤,這樣做的目的是為了確保「蜘蛛」不會訪問同一URL一次以上;進一步來說,「蜘蛛」必須把URL分成三組,第一組存儲在「workloadWaiting」屬性中,包含了一個未處理的URL列表,「蜘蛛」要訪問的第一個URL也存在其中;第二組存儲在「workloadProcessed」中,它是「蜘蛛」已經處理過且無需再次訪問的URL;第三組存儲在「workloadError」中,包含了發生錯誤的URL。 Begin方法包含了Spider類的主循環,其一直重復遍歷「workloadWaiting」,並處理其中的每一個頁面,當然我們也想到了,在這些頁面被處理時,很可能有其他的URL添加到「workloadWaiting」中,所以,begin方法一直繼續此過程,直到調用Spider類的cancel方法,或「workloadWaiting」中已不再剩有URL。這個過程如下: cancel = false; while ( !getWorkloadWaiting().isEmpty() && !cancel ) { Object list[] = getWorkloadWaiting().toArray(); for ( int i=0; (i<list.length)&&!cancel; i++="" )=""> processURL((URL)list[i]); } 當上述代碼遍歷「workloadWaiting」時,它把每個需處理的URL都傳遞給「processURL」方法,而這個方法才是真正讀取並解析URL中HTML信息的。 讀取並解析HTML Java同時支持訪問URL內容及解析HTML,而這正是「processURL」方法要做的。在Java中讀取URL內容相對還比較簡單,下面就是「processURL」方法實現此功能的代碼: URLConnection connection = url.openConnection(); if ( (connection.getContentType()!=null) &&!connection.getContentType().toLowerCase().startsWith("text/") ) { getWorkloadWaiting().remove(url); getWorkloadProcessed().add(url); log("Not processing because content type is: " + connection.getContentType() );
C. 我想用c編寫一個爬蟲程序,可是看完一本c語言教程後,還是覺得只會編寫一些計算類的小代碼,要學會編寫
想做爬蟲程序不是學完語言就行了,是要學很多東西的,你可以查一查大學計算機專業有哪些課程。
做爬蟲可以學學java語言。
《網路機器人Java編程指南》
開源的爬蟲產品
http://www.oschina.net/project/tag/64/spider?lang=19&os=0&sort=view&p=1
D. 我想學習網路爬蟲,哪位高手能給我發代碼c、C++都可以,先謝謝了。郵箱:[email protected]
C#特別適合於構造蜘蛛程序,這是因為它已經內置了HTTP和多線程的能力,而這兩種能力對於蜘蛛程序來說都是非常關鍵的。下面是構造一個蜘蛛程序要解決的關鍵問題:⑴ HTML分析:需要某種HTML解析器來分析蜘蛛程序遇到的每一個頁面。
⑵ 頁面處理:需要處理每一個下載得到的頁面。下載得到的內容可能要保存到磁碟,或者進一步分析處理。
⑶ 多線程:只有擁有多線程能力,蜘蛛程序才能真正做到高效。
⑷ 確定何時完成:不要小看這個問題,確定任務是否已經完成並不簡單,尤其是在多線程環境下。
一、HTML解析
提供的HTML解析器由ParseHTML類實現,使用非常方便:首先創建該類的一個實例,然後將它的Source屬性設置為要解析的HTML文檔:
以下是代碼片段:
ParseHTMLparse=newParseHTML();
parse.Source="HelloWorld";
接下來就可以利用循環來檢查HTML文檔包含的所有文本和標記。通常,檢查過程可以從一個測試Eof方法的while循環開始:
以下是代碼片段:
while(!parse.Eof())
{
charch=parse.Parse();
Parse方法將返回HTML文檔包含的字元它返回的內容只包含那些非HTML標記的字元,如果遇到了HTML標記,Parse方法將返回0值,表示現在遇到了一個HTML標記。遇到一個標記之後,我們可以用GetTag()方法來處理它。
以下是代碼片段:
if(ch==0)
{
HTMLTagtag=parse.GetTag();
}
一般地,蜘蛛程序最重要的任務之一就是找出各個HREF屬性,這可以藉助C#的索引功能完成。例如,下面的代碼將提取出HREF屬性的值(如果存在的話)。
以下是代碼片段:
Attributehref=tag["HREF"];
stringlink=href.Value;
獲得Attribute對象之後,通過Attribute.Value可以得到該屬性的值。
二、處理HTML頁面
下面來看看如何處理HTML頁面。首先要做的當然是下載HTML頁面,這可以通過C#提供的HttpWebRequest類實現:
以下是代碼片段:
HttpWebRequestrequest=(HttpWebRequest)WebRequest.Create(m_uri);
response=request.GetResponse();
stream=response.GetResponseStream();
接下來我們就從request創建一個stream流。在執行其他處理之前,我們要先確定該文件是二進制文件還是文本文件,不同的文件類型處理方式也不同。下面的代碼確定該文件是否為二進制文件。
以下是代碼片段:
if(!response.ContentType.ToLower().StartsWith("text/"))
{
SaveBinaryFile(response);
returnnull;
}
stringbuffer="",line;
如果該文件不是文本文件,我們將它作為二進制文件讀入。如果是文本文件,首先從stream創建一個StreamReader,然後將文本文件的內容一行一行加入緩沖區。
以下是代碼片段:
reader=newStreamReader(stream);
while((line=reader.ReadLine())!=null)
{
buffer+=line+"rn";
}
裝入整個文件之後,接著就要把它保存為文本文件。
以下是代碼片段:
SaveTextFile(buffer);
下面來看看這兩類不同文件的存儲方式。
二進制文件的內容類型聲明不以"text/"開頭,蜘蛛程序直接把二進制文件保存到磁碟,不必進行額外的處理,這是因為二進制文件不包含HTML,因此也不會再有需要蜘蛛程序處理的HTML。下面是寫入二進制文件的步驟。
首先准備一個緩沖區臨時地保存二進制文件的內容。
以下是代碼片段:
byte[]buffer=newbyte[1024];
接下來要確定文件保存到本地的路徑和名稱。如果要把一個myhost.com網站的內容下載到本地的c:test文件夾,二進制文件的網上路徑和名稱是,則本地路徑和名稱應當是c:testimageslogo.gif。與此同時,我們還要確保c:test目錄下已經創建了images子目錄。這部分任務由convertFilename方法完成。
以下是代碼片段:
stringfilename=convertFilename(response.ResponseUri);
convertFilename方法分離HTTP地址,創建相應的目錄結構。確定了輸出文件的名字和路徑之後就可以打開讀取Web頁面的輸入流、寫入本地文件的輸出流。
以下是代碼片段:
StreamoutStream=File.Create(filename);
StreaminStream=response.GetResponseStream();
接下來就可以讀取Web文件的內容並寫入到本地文件,這可以通過一個循環方便地完成。
以下是代碼片段:
intl;
do
{
l=inStream.Read(buffer,0,
buffer.Length);
if(l>0)
outStream.Write(buffer,0,l);
}
while(l>0);
三、多線程
我們用DocumentWorker類封裝所有下載一個URL的操作。每當一個DocumentWorker的實例被創建,它就進入循環,等待下一個要處理的URL。下面是DocumentWorker的主循環:
以下是代碼片段:
while(!m_spider.Quit)
{
m_uri=m_spider.ObtainWork();
m_spider.SpiderDone.WorkerBegin();
stringpage=GetPage();
if(page!=null)
ProcessPage(page);
m_spider.SpiderDone.WorkerEnd();
}
這個循環將一直運行,直至Quit標記被設置成了true(當用戶"Cancel"按鈕時,Quit標記就被設置成true)。在循環之內,我們調用ObtainWork獲取一個URL。ObtainWork將一直等待,直到有一個URL可用這要由其他線程解析文檔並尋找才能獲得。Done類利用WorkerBegin和WorkerEnd方法來確定何時整個下載操作已經完成。
從圖一可以看出,蜘蛛程序允許用戶自己確定要使用的線程數量。在實踐中,線程的最佳數量受許多因素影響。如果你的機器性能較高,或者有兩個處理器,可以設置較多的線程數量;反之,如果網路帶寬、機器性能有限,設置太多的線程數量其實不一定能夠提高性能。
四、任務完成了嗎?
利用多個線程同時下載文件有效地提高了性能,但也帶來了線程管理方面的問題。其中最復雜的一個問題是:蜘蛛程序何時才算完成了工作?在這里我們要藉助一個專用的類Done來判斷。
首先有必要說明一下"完成工作"的具體含義。只有當系統中不存在等待下載的URL,而且所有工作線程都已經結束其處理工作時,蜘蛛程序的工作才算完成。也就是說,完成工作意味著已經沒有等待下載和正在下載的URL。
Done類提供了一個WaitDone方法,它的功能是一直等待,直到Done對象檢測到蜘蛛程序已完成工作。下面是WaitDone方法的代碼。
以下是代碼片段:
publicvoidWaitDone()
{
Monitor.Enter(this);
while(m_activeThreads>0)
{
Monitor.Wait(this);
}
Monitor.Exit(this);
}
WaitDone方法將一直等待,直到不再有活動的線程。但必須注意的是,下載開始的最初階段也沒有任何活動的線程,所以很容易造成蜘蛛程序一開始就立即停止的現象。為解決這個問題,我們還需要另一個方法WaitBegin來等待蜘蛛程序進入"正式的"工作階段。一般的調用次序是:先調用WaitBegin,再接著調用WaitDone,WaitDone將等待蜘蛛程序完成工作。下面是WaitBegin的代碼:
以下是代碼片段:
publicvoidWaitBegin()
{
Monitor.Enter(this);
while(!m_started)
{
Monitor.Wait(this);
}
Monitor.Exit(this);
在《爬蟲/蜘蛛程序的製作(C#語言)》一文中,已經介紹了爬蟲程序實現的基本方法,可以說,已經實現了爬蟲的功能。只是它存在一個效率問題,下載速度可能很慢。這是兩方面的原因造成的:
1.分析和下載不能同步進行。在《爬蟲/蜘蛛程序的製作(C#語言)》中已經介紹了爬蟲程序的兩個步驟:分析和下載。在單線程的程序中,兩者是無法 同時進行的。也就是說,分析時會造成網路空閑,分析的時間越長,下載的效率越低。反之也是一樣,下載時無法同時進行分析,只有停下下載後才能進行下一步的 分析。問題浮出水面,我想大家都會想到:把分析和下載用不同的線程進行,問題不就解決了嗎?
2.只是單線程下載。相信大家都有用過網際快車等下載資源的經歷,它裡面是可以設置線程數的(近年版本默認是10,曾經默認是5)。它會將文件分成 與線程數相同的部分,然後每個線程下載自己的那一部分,這樣下載效率就有可能提高。相信大家都有加多線程數,提升下載效率的經歷。但細心的用戶會發現,在 帶寬一定的情況下,並不是線程越多,速度越快,而是在某一點達到峰值。爬蟲作為特殊的下載工具,不具備多線程的能力何以有效率可談?爬蟲在信息時代的目 的,難道不是快速獲取信息嗎?所以,爬蟲需要有多線程(可控數量)同時下載網頁。
好了,認識、分析完問題,就是解決問題了:
多線程在C#中並不難實現。它有一個命名空間:System.Threading,提供了多線程的支持。
要開啟一個新線程,需要以下的初始化:
ThreadStart startDownload = new ThreadStart( DownLoad );
//線程起始設置:即每個線程都執行DownLoad(),注意:DownLoad()必須為不帶有參數的方法
Thread downloadThread = new Thread( startDownload ); //實例化要開啟的新類
downloadThread.Start();//開啟線程
由於線程起始時啟動的方法不能帶有參數,這就為多線程共享資源添加了麻煩。不過我們可以用類級變數(當然也可以使用其它方法,筆者認為此方法最簡單易用)來解決這個問題。知道開啟多線程下載的方法後,大家可能會產生幾個疑問:
1.如何控制線程的數量?
2.如何防止多線程下載同一網頁?
3.如何判斷線程結束?
4.如何控制線程結束?
下面就這幾個問題提出解決方法:
1.線程數量我們可以通過for循環來實現,就如同當年初學編程的打點程序一樣。
比如已知用戶指定了n(它是一個int型變數)個線程吧,可以用如下方法開啟五個線程。
Thread[] downloadThread;
//聲名下載線程,這是C#的優勢,即數組初始化時,不需要指定其長度,可以在使用時才指定。
這個聲名應為類級,這樣也就為其它方法控制項它們提供了可能
ThreadStart startDownload = new ThreadStart( DownLoad );
//線程起始設置:即每個線程都執行DownLoad()
downloadThread = new Thread[ n ];//為線程申請資源,確定線程總數
for( int i = 0; i < n; i++ )//開啟指定數量的線程數
{
downloadThread[i] = new Thread( startDownload );//指定線程起始設置
downloadThread[i].Start();//逐個開啟線程
}
好了,實現控制開啟線程數是不是很簡單啊?
2.下面出現的一個問題:所有的線程都調用DonwLoad()方法,這樣如何避免它們同時下載同一個網頁呢?
這個問題也好解決,只要建立一下Url地址表,表中的每個地址只允許被一個線程申請即可。具體實現:
可以利用資料庫,建立一個表,表中有四列,其中一列專門用於存儲Url地址,另外兩列分別存放地址對應的線程以及該地址被申請的次數,最後一列存放 下載的內容。(當然,對應線程一列不是必要的)。當有線程申請後,將對應線程一列設定為當前線程編號,並將是否申請過一列設置為申請一次,這樣,別的線程 就無法申請該頁。如果下載成功,則將內容存入內容列。如果不成功,內容列仍為空,作為是否再次下載的依據之一,如果反復不成功,則進程將於達到重試次數 (對應該地址被申請的次數,用戶可設)後,申請下一個Url地址。主要的代碼如下(以VFP為例):
<建立表>
CREATE TABLE (ctablename) ( curl M , ctext M , ldowned I , threadNum I )
&&建立一個表ctablename.dbf,含有地址、文本內容、已經嘗試下載次數、
線程標志(初值為-1,線程標志是從0開始的整數)四個欄位
<提取Url地址>
cfullname = (ctablename) + '.dbf'&&為表添加擴展名
USE (cfullname)
GO TOP
LOCATE FOR (EMPTY( ALLTRIM( ctext ) ) AND ldowned < 2 AND
( threadNum = thisNum OR threadNum = - 1) )
&&查找尚未下載成功且應下載的屬於本線程許可權的Url地址,thisNum是當前線程的編號,
可以通過參數傳遞得到
gotUrl = curl
recNum = RECNO()
IF recNum <= RECCOUNT() THEN &&如果在列表中找到這樣的Url地址
UPDATE (cfullname) SET ldowned = ( ldowned + 1 ) , threadNum =
thisNum WHERE RECNO() = recNum &&更新表,將此記錄更新為已申請,即下載次數加1,
線程標志列設為本線程的編號。
<下載內容>
cfulltablename = (ctablename) + '.dbf'
USE (cfulltablename)
SET EXACT ON
LOCATE FOR curl = (csiteurl) && csiteurl是參數,為下載到的內容所對應的Url地址
recNumNow = RECNO()&&得到含有此地址的記錄號
UPDATE (cfulltablename) SET ctext = (ccontent) WHERE RECNO() =
recNumNow &&插入對應地址的對應內容
<插入新地址>
ctablename = (ctablename) + '.dbf'
USE (ctablename)
GO TOP
SET EXACT ON
LOCATE FOR curl = (cnewurl) &&查找有無此地址
IF RECNO() > RECCOUNT() THEN &&如果尚無此地址
SET CARRY OFF
INSERT INTO (ctablename) ( curl , ctext , ldowned , threadNum )
VALUES ( (cnewurl) , "" , 0 , -1 ) &&將主頁地址添加到列表
好了,這樣就解決了多線程中,線程沖突。當然,去重問題也可以在C#語言內解決,只根建立一個臨時文件(文本就可以),保存所有的Url地址,差對它們設置相應的屬性即可,但查找效率可能不及資料庫快。
共2頁
3.線程結束是很難判斷的,因為它總是在查找新的鏈接。用者認為可以假設:線程重復N次以後還是沒有能申請到新的Url地址,那麼可以認為它已經下載完了所有鏈接。主要代碼如下:
string url = "";
int times = 0;
while ( url == "" )//如果沒有找到符合條件的記錄,則不斷地尋找符合條件的記錄
{
url = getUrl.GetAUrl( …… );//調用GetAUrl方法,試圖得到一個url值
if ( url == "" )//如果沒有找到
{
times ++;//嘗試次數自增
continue; //進行下一次嘗試
}
if ( times > N ) //如果已經嘗試夠了次數,則退出進程
{
downloadThread[i].Abort; //退出進程
}
else//如果沒有嘗試夠次數
{
Times = 0; //嘗試次數歸零處理
}
//進行下一步針對得到的Url的處理
}
4.這個問題相對簡單,因為在問題一中已經建議,將線程聲名為類級數組,這樣就很易於控制。只要用一個for循環即可結束。代碼如下:
for( int i = 0; i < n; i++ )//關閉指定數量n的線程數
{
downloadThread[i].Abort();//逐個關閉線程
}
好了,一個蜘蛛程序就這樣完成了,在C#面前,它的實現原來如此簡單。
這里筆者還想提醒讀者:筆者只是提供了一個思路及一個可以實現的解決方案,但它並不是最佳的,即使這個方案本身,也有好多可以改進的地方,留給讀者思考。
最後說明一下我所使用的環境:
winXP sp2 Pro
VFP 9.0
Visual Studio 2003 .net中文企業版
E. 各位好,有誰做過網路蜘蛛(爬蟲)啊可以提供C語言的源代碼給我不啊能幫我解釋一下更好,謝謝!
網上又好多,可以自己下載啊。http://bbs.chinaunix.net/thread-2163118-1-1.html
F. 求一份C++ HTTP 網路爬蟲源碼 最好是基於Socket,自己寫的有點問題,程序是正確的 但是效率好像有點低
基於Socket,自己寫的有點問題俺發給你一篇。
G. 基於c語言的爬蟲項目(網路詞典),獲取用戶的英文字元串,通過網路爬蟲通信實現翻譯,求思路代碼~
你現在「UI的簡單的客服端和服務端通信」大體用到哪些頭文件(技術)?
H. C++ 獲取網頁真實源碼
Ajax,我是用webbrowser,
var htmldocument = (mshtml.HTMLDocument)wb.Document.DomDocument;
string Content = htmldocument.documentElement.outerHTML;
I. 有人有簡單爬蟲源碼可以學習嗎
爬蟲學習之一個簡單的網路爬蟲
概述
這是一個網路爬蟲學習的技術分享,主要通過一些實際的案例對爬蟲的原理進行分析,達到對爬蟲有個基本的認識,並且能夠根據自己的需要爬到想要的數據。有了數據後可以做數據分析或者通過其他方式重新結構化展示。
什麼是網路爬蟲
網路爬蟲(又被稱為網頁蜘蛛,網路機器人,在FOAF社區中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。另外一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲。via網路網路爬蟲
網路蜘蛛(Web spider)也叫網路爬蟲(Web crawler)[1],螞蟻(ant),自動檢索工具(automatic indexer),或者(在FOAF軟體概念中)網路疾走(WEB scutter),是一種「自動化瀏覽網路」的程序,或者說是一種網路機器人。它們被廣泛用於互聯網搜索引擎或其他類似網站,以獲取或更新這些網站的內容和檢索方式。它們可以自動採集所有其能夠訪問到的頁面內容,以供搜索引擎做進一步處理(分檢整理下載的頁面),而使得用戶能更快的檢索到他們需要的信息。via維基網路網路蜘蛛
以上是網路和維基網路對網路爬蟲的定義,簡單來說爬蟲就是抓取目標網站內容的工具,一般是根據定義的行為自動進行抓取,更智能的爬蟲會自動分析目標網站結構類似與搜索引擎的爬蟲,我們這里只討論基本的爬蟲原理。
###爬蟲工作原理
網路爬蟲框架主要由控制器、解析器和索引庫三大部分組成,而爬蟲工作原理主要是解析器這個環節,解析器的主要工作是下載網頁,進行頁面的處理,主要是將一些JS腳本標簽、CSS代碼內容、空格字元、HTML標簽等內容處理掉,爬蟲的基本工作是由解析器完成。所以解析器的具體流程是:
入口訪問->下載內容->分析結構->提取內容
分析爬蟲目標結構
這里我們通過分析一個網站[落網:http://luoo.net] 對網站內容進行提取來進一步了解!
第一步 確定目的
抓取目標網站的某一期所有音樂
第二步 分析頁面結構
訪問落網的某一期刊,通過Chrome的開發者模式查看播放列表中的歌曲,右側用紅色框線圈出來的是一些需要特別注意的語義結構,見下圖所示:
以上紅色框線圈出的地方主要有歌曲名稱,歌曲的編號等,這里並沒有看到歌曲的實際文件地址,所以我們繼續查看,點擊某一個歌曲就會立即在瀏覽器中播放,這時我們可以看到在Chrome的開發者模式的Network中看到實際請求的播放文件,如下圖所示:
根據以上分析我們可以得到播放清單的位置和音樂文件的路徑,接下來我們通過python來實現這個目的。
實現爬蟲
Python環境安裝請自行Google
主要依賴第三方庫
Requests(http://www.python-requests.org) 用來發起請求
BeautifulSoup(bs4) 用來解析HTML結構並提取內容
faker(http://fake-factory.readthedocs.io/en/stable/)用來模擬請求UA(User-Agent)
主要思路是分成兩部分,第一部分用來發起請求分析出播放列表然後丟到隊列中,第二部分在隊列中逐條下載文件到本地,一般分析列錶速度更快,下載速度比較慢可以藉助多線程同時進行下載。
主要代碼如下:
#-*- coding: utf-8 -*-'''by sudo rm -rf http://imchenkun.com'''import osimport requestsfrom bs4 import BeautifulSoupimport randomfrom faker import Factoryimport Queueimport threadingfake = Factory.create()luoo_site = 'http://www.luoo.net/music/'luoo_site_mp3 = 'http://luoo-mp3.kssws.ks-cdn.com/low/luoo/radio%s/%s.mp3'proxy_ips = [ '27.15.236.236' ] # 替換自己的代理IPheaders = { 'Connection': 'keep-alive', 'User-Agent': fake.user_agent() }def random_proxies(): ip_index = random.randint(0, len(proxy_ips)-1) res = { 'http': proxy_ips[ip_index] } return resdef fix_characters(s): for c in ['<', '>', ':', '"', '/', '\\', '|', '?', '*']: s = s.replace(c, '') return sclass LuooSpider(threading.Thread): def __init__(self, url, vols, queue=None): threading.Thread.__init__(self) print '[luoo spider]' print '=' * 20 self.url = url self.queue = queue self.vol = '1' self.vols = vols def run(self): for vol in self.vols: self.spider(vol) print '\ncrawl end\n\n' def spider(self, vol): url = luoo_site + vol print 'crawling: ' + url + '\n' res = requests.get(url, proxies=random_proxies()) soup = BeautifulSoup(res.content, 'html.parser') title = soup.find('span', attrs={'class': 'vol-title'}).text cover = soup.find('img', attrs={'class': 'vol-cover'})['src'] desc = soup.find('div', attrs={'class': 'vol-desc'}) track_names = soup.find_all('a', attrs={'class': 'trackname'}) track_count = len(track_names) tracks = [] for track in track_names: _id = str(int(track.text[:2])) if (int(vol) < 12) else track.text[:2] # 12期前的音樂編號1~9是1位(如:1~9),之後的都是2位 1~9會在左邊墊0(如:01~09) _name = fix_characters(track.text[4:]) tracks.append({'id': _id, 'name': _name}) phases = { 'phase': vol, # 期刊編號 'title': title, # 期刊標題 'cover': cover, # 期刊封面 'desc': desc, # 期刊描述 'track_count': track_count, # 節目數 'tracks': tracks # 節目清單(節目編號,節目名稱) } self.queue.put(phases)class LuooDownloader(threading.Thread): def __init__(self, url, dist, queue=None): threading.Thread.__init__(self) self.url = url self.queue = queue self.dist = dist self.__counter = 0 def run(self): while True: if self.queue.qsize() <= 0: pass else: phases = self.queue.get() self.download(phases) def download(self, phases): for track in phases['tracks']: file_url = self.url % (phases['phase'], track['id']) local_file_dict = '%s/%s' % (self.dist, phases['phase']) if not os.path.exists(local_file_dict): os.makedirs(local_file_dict) local_file = '%s/%s.%s.mp3' % (local_file_dict, track['id'], track['name']) if not os.path.isfile(local_file): print 'downloading: ' + track['name'] res = requests.get(file_url, proxies=random_proxies(), headers=headers) with open(local_file, 'wb') as f: f.write(res.content) f.close() print 'done.\n' else: print 'break: ' + track['name']if __name__ == '__main__': spider_queue = Queue.Queue() luoo = LuooSpider(luoo_site, vols=['680', '721', '725', '720'],queue=spider_queue) luoo.setDaemon(True) luoo.start() downloader_count = 5 for i in range(downloader_count): luoo_download = LuooDownloader(luoo_site_mp3, 'D:/luoo', queue=spider_queue) luoo_download.setDaemon(True) luoo_download.start()
以上代碼執行後結果如下圖所示
Github地址:https://github.com/imchenkun/ick-spider/blob/master/luoospider.py
總結
通過本文我們基本了解了網路爬蟲的知識,對網路爬蟲工作原理認識的同時我們實現了一個真實的案例場景,這里主要是使用一些基礎的第三方Python庫來幫助我們實現爬蟲,基本上演示了網路爬蟲框架中基本的核心概念。通常工作中我們會使用一些比較優秀的爬蟲框架來快速的實現需求,比如scrapy框架,接下來我會通過使用Scrapy這類爬蟲框架來實現一個新的爬蟲來加深對網路爬蟲的理解!
J. python爬蟲怎麼獲取動態的網頁源碼
一個月前實習導師布置任務說通過網路爬蟲獲取深圳市氣象局發布的降雨數據,網頁如下:
心想,爬蟲不太難的,當年跟zjb爬煎蛋網無(mei)聊(zi)圖的時候,多麼清高。由於接受任務後的一個月考試加作業一大堆,導師也不催,自己也不急。
但是,導師等我一個月都得讓我來寫意味著這東西得有多難吧。。。今天打開一看的確是這樣。網站是基於Ajax寫的,數據動態獲取,所以無法通過下載源代碼然後解析獲得。
從某不良少年寫的抓取淘寶mm的例子中收到啟發,對於這樣的情況,一般可以同構自己搭建瀏覽器實現。phantomJs,CasperJS都是不錯的選擇。
導師的要求是獲取過去一年內深圳每個區每個站點每小時的降雨量,執行該操作需要通過如上圖中的歷史查詢實現,即通過一個時間來查詢,而這個時間存放在一個hidden類型的input標簽里,當然可以通過js語句將其改為text類型,然後執行send_keys之類的操作。然而,我失敗了。時間可以修改設置,可是結果如下圖。
為此,僅抓取實時數據。選取python的selenium,模擬搭建瀏覽器,模擬人為的點擊等操作實現數據生成和獲取。selenium的一大優點就是能獲取網頁渲染後的源代碼,即執行操作後的源代碼。普通的通過 url解析網頁的方式只能獲取給定的數據,不能實現與用戶之間的交互。selenium通過獲取渲染後的網頁源碼,並通過豐富的查找工具,個人認為最好用的就是find_element_by_xpath("xxx"),通過該方式查找到元素後可執行點擊、輸入等事件,進而向伺服器發出請求,獲取所需的數據。
[python]view plain
#coding=utf-8
fromtestStringimport*
fromseleniumimportwebdriver
importstring
importos
fromselenium.webdriver.common.keysimportKeys
importtime
importsys
default_encoding='utf-8'
ifsys.getdefaultencoding()!=default_encoding:
reload(sys)
sys.setdefaultencoding(default_encoding)
district_navs=['nav2','nav1','nav3','nav4','nav5','nav6','nav7','nav8','nav9','nav10']
district_names=['福田區','羅湖區','南山區','鹽田區','寶安區','龍崗區','光明新區','坪山新區','龍華新區','大鵬新區']
flag=1
while(flag>0):
driver=webdriver.Chrome()
driver.get("hianCe/")
#選擇降雨量
driver.find_element_by_xpath("//span[@id='fenqu_H24R']").click()
filename=time.strftime("%Y%m%d%H%M",time.localtime(time.time()))+'.txt'
#創建文件
output_file=open(filename,'w')
#選擇行政區
foriinrange(len(district_navs)):
driver.find_element_by_xpath("//div[@id='"+district_navs[i]+"']").click()
#printdriver.page_source
timeElem=driver.find_element_by_id("time_shikuang")
#輸出時間和站點名
output_file.write(timeElem.text+',')
output_file.write(district_names[i]+',')
elems=driver.find_elements_by_xpath("//span[@onmouseover='javscript:changeTextOver(this)']")
#輸出每個站點的數據,格式為:站點名,一小時降雨量,當日累積降雨量
foreleminelems:
output_file.write(AMonitorRecord(elem.get_attribute("title"))+',')
output_file.write(' ')
output_file.close()
driver.close()
time.sleep(3600)
- 文件中引用的文件testString只是修改輸出格式,提取有效數據。
#Encoding=utf-8
defOnlyCharNum(s,oth=''):
s2=s.lower()
fomart=',.'
forcins2:
ifnotcinfomart:
s=s.replace(c,'')
returns
defAMonitorRecord(str):
str=str.split(":")
returnstr[0]+","+OnlyCharNum(str[1])
- 一小時抓取一次數據,結果如下:
[python]view plain