如何入門 Python 爬蟲?


「入門」是良好的動機,但是可能作用緩慢。如果你手裡或者腦子裡有一個項目,那麼實踐起來你會被目標驅動,而不會像學習模塊一樣慢慢學習。

另外如果說知識體系里的每一個知識點是圖裡的點,依賴關係是邊的話,那麼這個圖一定不是一個有向無環圖。因為學習A的經驗可以幫助你學習B。因此,你不需要學習怎麼樣「入門」,因為這樣的「入門」點根本不存在!你需要學習的是怎麼樣做一個比較大的東西,在這個過程中,你會很快地學會需要學會的東西的。當然,你可以爭論說需要先懂python,不然怎麼學會python做爬蟲呢?但是事實上,你完全可以在做這個爬蟲的過程中學習python :D

看到前面很多答案都講的「術」——用什麼軟體怎麼爬,那我就講講「道」和「術」吧——爬蟲怎麼工作以及怎麼在python實現。

先長話短說summarize一下:
你需要學習

  1. 基本的爬蟲工作原理
  2. 基本的http抓取工具,scrapy
  3. Bloom Filter: Bloom Filters by Example
  4. 如果需要大規模網頁抓取,你需要學習分散式爬蟲的概念。其實沒那麼玄乎,你只要學會怎樣維護一個所有集群機器能夠有效分享的分散式隊列就好。最簡單的實現是python-rq: https://github.com/nvie/rq
  5. rq和Scrapy的結合:darkrho/scrapy-redis · GitHub
  6. 後續處理,網頁析取(grangier/python-goose · GitHub),存儲(Mongodb)

以下是短話長說:

說說當初寫的一個集群爬下整個豆瓣的經驗吧。

1)首先你要明白爬蟲怎樣工作。
想像你是一隻蜘蛛,現在你被放到了互聯「網」上。那麼,你需要把所有的網頁都看一遍。怎麼辦呢?沒問題呀,你就隨便從某個地方開始,比如說人民日報的首頁,這個叫initial pages,用$表示吧。

在人民日報的首頁,你看到那個頁面引向的各種鏈接。於是你很開心地從爬到了「國內新聞」那個頁面。太好了,這樣你就已經爬完了倆頁面(首頁和國內新聞)!暫且不用管爬下來的頁面怎麼處理的,你就想像你把這個頁面完完整整抄成了個html放到了你身上。

突然你發現, 在國內新聞這個頁面上,有一個鏈接鏈回「首頁」。作為一隻聰明的蜘蛛,你肯定知道你不用爬回去的吧,因為你已經看過了啊。所以,你需要用你的腦子,存下你已經看過的頁面地址。這樣,每次看到一個可能需要爬的新鏈接,你就先查查你腦子裡是不是已經去過這個頁面地址。如果去過,那就別去了。

好的,理論上如果所有的頁面可以從initial page達到的話,那麼可以證明你一定可以爬完所有的網頁。

那麼在python里怎麼實現呢?
很簡單

import Queue

initial_page = "http://www.renminribao.com"

url_queue = Queue.Queue()
seen = set()

seen.insert(initial_page)
url_queue.put(initial_page)

while(True): #一直進行直到海枯石爛
if url_queue.size()&>0:
current_url = url_queue.get() #拿出隊例中第一個的url
store(current_url) #把這個url代表的網頁存儲好
for next_url in extract_urls(current_url): #提取把這個url里鏈向的url
if next_url not in seen:
seen.put(next_url)
url_queue.put(next_url)
else:
break

寫得已經很偽代碼了。

所有的爬蟲的backbone都在這裡,下面分析一下為什麼爬蟲事實上是個非常複雜的東西——搜索引擎公司通常有一整個團隊來維護和開發。

2)效率
如果你直接加工一下上面的代碼直接運行的話,你需要一整年才能爬下整個豆瓣的內容。更別說Google這樣的搜索引擎需要爬下全網的內容了。

問題出在哪呢?需要爬的網頁實在太多太多了,而上面的代碼太慢太慢了。設想全網有N個網站,那麼分析一下判重的複雜度就是N*log(N),因為所有網頁要遍歷一次,而每次判重用set的話需要log(N)的複雜度。OK,OK,我知道python的set實現是hash——不過這樣還是太慢了,至少內存使用效率不高。

通常的判重做法是怎樣呢?Bloom Filter. 簡單講它仍然是一種hash的方法,但是它的特點是,它可以使用固定的內存(不隨url的數量而增長)以O(1)的效率判定url是否已經在set中。可惜天下沒有白吃的午餐,它的唯一問題在於,如果這個url不在set中,BF可以100%確定這個url沒有看過。但是如果這個url在set中,它會告訴你:這個url應該已經出現過,不過我有2%的不確定性。注意這裡的不確定性在你分配的內存足夠大的時候,可以變得很小很少。一個簡單的教程:Bloom Filters by Example

注意到這個特點,url如果被看過,那麼可能以小概率重複看一看(沒關係,多看看不會累死)。但是如果沒被看過,一定會被看一下(這個很重要,不然我們就要漏掉一些網頁了!)。 [IMPORTANT: 此段有問題,請暫時略過]

好,現在已經接近處理判重最快的方法了。另外一個瓶頸——你只有一台機器。不管你的帶寬有多大,只要你的機器下載網頁的速度是瓶頸的話,那麼你只有加快這個速度。用一台機子不夠的話——用很多台吧!當然,我們假設每台機子都已經進了最大的效率——使用多線程(python的話,多進程吧)。

3)集群化抓取
爬取豆瓣的時候,我總共用了100多台機器晝夜不停地運行了一個月。想像如果只用一台機子你就得運行100個月了...

那麼,假設你現在有100台機器可以用,怎麼用python實現一個分散式的爬取演算法呢?

我們把這100台中的99台運算能力較小的機器叫作slave,另外一台較大的機器叫作master,那麼回顧上面代碼中的url_queue,如果我們能把這個queue放到這台master機器上,所有的slave都可以通過網路跟master聯通,每當一個slave完成下載一個網頁,就向master請求一個新的網頁來抓取。而每次slave新抓到一個網頁,就把這個網頁上所有的鏈接送到master的queue里去。同樣,bloom filter也放到master上,但是現在master只發送確定沒有被訪問過的url給slave。Bloom Filter放到master的內存里,而被訪問過的url放到運行在master上的Redis里,這樣保證所有操作都是O(1)。(至少平攤是O(1),Redis的訪問效率見:LINSERT – Redis)


考慮如何用python實現:
在各台slave上裝好scrapy,那麼各台機子就變成了一台有抓取能力的slave,在master上裝好Redis和rq用作分散式隊列。


代碼於是寫成

#slave.py

current_url = request_from_master()
to_send = []
for next_url in extract_urls(current_url):
to_send.append(next_url)

store(current_url);
send_to_master(to_send)

#master.py
distributed_queue = DistributedQueue()
bf = BloomFilter()

initial_pages = "www.renmingribao.com"

while(True):
if request == "GET":
if distributed_queue.size()&>0:
send(distributed_queue.get())
else:
break
elif request == "POST":
bf.put(request.url)

好的,其實你能想到,有人已經給你寫好了你需要的:darkrho/scrapy-redis · GitHub

4)展望及後處理
雖然上面用很多「簡單」,但是真正要實現一個商業規模可用的爬蟲並不是一件容易的事。上面的代碼用來爬一個整體的網站幾乎沒有太大的問題。

但是如果附加上你需要這些後續處理,比如

  1. 有效地存儲(資料庫應該怎樣安排)
  2. 有效地判重(這裡指網頁判重,咱可不想把人民日報和抄襲它的大民日報都爬一遍)
  3. 有效地信息抽取(比如怎麼樣抽取出網頁上所有的地址抽取出來,「朝陽區奮進路中華道」),搜索引擎通常不需要存儲所有的信息,比如圖片我存來幹嘛...
  4. 及時更新(預測這個網頁多久會更新一次)

如你所想,這裡每一個點都可以供很多研究者十數年的研究。雖然如此,
「路漫漫其修遠兮,吾將上下而求索」。

所以,不要問怎麼入門,直接上路就好了:)


看了大部分回答不禁嘆口氣,主要是因為看到很多大牛在回答像「如何入門爬蟲」這種問題的時候,一如當年學霸講解題目,跳步無數,然後留下一句「不就是這樣推嘛」,讓一眾小白菜鳥一臉懵逼。。作為一個0起步(之前連python都不會),目前總算掌握基礎,開始向上進階的菜鳥,深知其中的不易,所以我會在這個回答里,儘可能全面、細節地分享給大家從0學習爬蟲的各種步驟,如果對你有幫助,請點贊~

-------------------------------------------------------------------------------------------------
#我要寫爬蟲!
#Ver.1.2
#Based on: Python 2.7
#Author:高野良

#原創內容,轉載請註明出處

首先!你要對爬蟲有個明確的認識,這裡引用毛主席的思想:

在戰略上藐視:

  • 「所有網站皆可爬」:互聯網的內容都是人寫出來的,而且都是偷懶寫出來的(不會第一頁是a,下一頁是8),所以肯定有規律,這就給人有了爬取的可能,可以說,天下沒有不能爬的網站
  • 「框架不變」:網站不同,但是原理都類似,大部分爬蟲都是從 發送請求——獲得頁面——解析頁面——下載內容——儲存內容 這樣的流程來進行,只是用的工具不同

在戰術上重視:

  • 持之以恆,戒驕戒躁:對於初學入門,不可輕易自滿,以為爬了一點內容就什麼都會爬了,爬蟲雖然是比較簡單的技術,但是往深學也是沒有止境的(比如搜索引擎等)!只有不斷嘗試,刻苦鑽研才是王道!(為何有種小學作文即視感)

||
||
V

然後,你需要一個宏偉的目標,來讓你有持續學習的動力(沒有實操項目,真的很難有動力)

我要爬整個豆瓣!...
我要爬整個草榴社區!
我要爬知乎各種妹子的聯繫方式*^#%^$#

||
||
V

接著,你需要捫心自問一下,自己的python基本功吼不吼啊?

吼啊!——OK,開始歡快地學習爬蟲吧 !
不吼?你還需要學習一個!趕緊回去看廖雪峰老師的教程,
2.7的。至少這些功能和語法你要有基本的掌握 :

  • list,dict:用來序列化你爬的東西
  • 切片:用來對爬取的內容進行分割,生成
  • 條件判斷(if等):用來解決爬蟲過程中哪些要哪些不要的問題
  • 循環和迭代(for while ):用來循環,重複爬蟲動作
  • 文件讀寫操作:用來讀取參數、保存爬下來的內容等

||
||
V

然後,你需要補充一下下面幾個內容,作為你的知識儲備:
(註:這裡並非要求「掌握」,下面講的兩點,只需要先了解,然後通過具體項目來不斷實踐,直到熟練掌握)

1、網頁的基本知識:

基本的HTML語言知識(知道href等大學計算機一級內容即可)
理解網站的發包和收包的概念(POST GET)
稍微一點點的js知識,用於理解動態網頁(當然如果本身就懂當然更好啦)

2、一些分析語言,為接下來解析網頁內容做準備

NO.1 正則表達式:扛把子技術,總得會最基礎的:

NO.2 XPATH:高效的分析語言,表達清晰簡單,掌握了以後基本可以不用正則
參考:XPath 教程

NO.3 Beautifulsoup:
美麗湯模塊解析網頁神器,一款神器,如果不用一些爬蟲框架(如後文講到的scrapy),配合request,urllib等模塊(後面會詳細講),可以編寫各種小巧精幹的爬蟲腳本
官網文檔:Beautiful Soup 4.2.0 文檔
參考案例:

||
||
V
接著,你需要一些高效的工具來輔助
(同樣,這裡先了解,到具體的項目的時候,再熟悉運用)

NO.1 F12 開發者工具:

  • 看源代碼:快速定位元素
  • 分析xpath:1、此處建議谷歌系瀏覽器,可以在源碼界面直接右鍵看

NO.2 抓包工具:

  • 推薦httpfox,火狐瀏覽器下的插件,比谷歌火狐系自帶的F12工具都要好,可以方便查看網站收包發包的信息

NO.3 XPATH CHECKER (火狐插件):
非常不錯的xpath測試工具,但是有幾個坑,都是個人踩過的,,在此告誡大家:
1、xpath checker生成的是絕對路徑,遇到一些動態生成的圖標(常見的有列表翻頁按鈕等),飄忽不定的絕對路徑很有可能造成錯誤,所以這裡建議在真正分析的時候,只是作為參考
2、記得把如下圖xpath框里的「x:」去掉,貌似這個是早期版本xpath的語法,目前已經和一些模塊不兼容(比如scrapy),還是刪去避免報錯

NO.4 正則表達測試工具:
在線正則表達式測試 ,拿來多練練手,也輔助分析!裡面有很多現成的正則表達式可以用,也可以進行參考!

||
||
V
ok!這些你都基本有一些了解了,現在開始進入抓取時間,上各種模塊吧!python的火,很大原因就是各種好用的模塊,這些模塊是居家旅行爬網站常備的——

urllib
urllib2
requests

||
||
V
不想重複造輪子,有沒有現成的框架?

華麗麗的scrapy(這塊我會重點講,我的最愛)

||
||
V
遇到動態頁面怎麼辦?

selenium(會了這個配合scrapy無往不利,是居家旅行爬網站又一神器,下一版更新的時候會著重安利,因為這塊貌似目前網上的教程還很少)
phantomJS(不顯示網頁的selenium)

||
||
V
遇到反爬蟲策略驗證碼之類咋整?(不想折騰的直接第四個)

PIL
opencv
pybrain
打碼平台

||
||
V

然後是資料庫,這裡我認為開始並不需要非常深入,在需要的時候再學習即可

mysql
mongodb
sqllite

||
||
V

爬來的東西怎麼用?

numpy 數據分析,類似matlab的模塊
pandas(基於numpy的數據分析模塊,相信我,如果你不是專門搞TB級數據的,這個就夠了)

||
||
V

進階技術

多線程、分散式

———————————— 亂入的分割線 —————————————

然後學習編程關鍵的是學以致用,天天捧一本書看不如直接上手操練,下面我通過實際的例子來講解爬蟲——
比如最近,樓主在豆瓣上認識了一個很可愛的妹子,發現她一直會更新簽名和日誌,所以沒事就會去她主頁看看,但一直沒有互相加好友(作為一隻高冷的天蠍,怎麼可以輕易加好友嘛!而且加了好友,你更新什麼都會收到推送,那多沒意思啊!一點神秘感都沒有了!),可還是想及時獲得妹子的最新動態,怎麼辦?

於是我就寫了個70幾行的python腳本,包含爬蟲+郵件模塊,跑在家裡的一台閑置筆記本上,通過計劃任務每準點抓取妹子的簽名和最新文章一次,發送到我的郵箱。。嗯,其實是很簡單的技術,,代碼如下所示:

#-*-coding:utf-8-*- #編碼聲明,不要忘記!
import requests #這裡使用requests,小腳本用它最合適!
from lxml import html #這裡我們用lxml,也就是xpath的方法

#豆瓣模擬登錄,最簡單的是cookie,會這個方法,80%的登錄網站可以搞定
cookie = {}

raw_cookies = ""#引號裡面是你的cookie,用之前講的抓包工具來獲得

for line in raw_cookies.split(";"):
key,value = line.split("=", 1)
cookie[key] = value #一些格式化操作,用來裝載cookies

#重點來了!用requests,裝載cookies,請求網站
page = requests.get("#妹紙的豆瓣主頁#",cookies=cookie)

#對獲取到的page格式化操作,方便後面用XPath來解析
tree = html.fromstring(page.text)

#XPath解析,獲得你要的文欄位落!
intro_raw = tree.xpath("//span[@id="intro_display"]/text()")

#簡單的轉碼工作,這步根據需要可以省略
for i in intro_raw:
intro = i.encode("utf-8")

print intro #妹子的簽名就顯示在屏幕上啦

#接下來就是裝載郵件模塊,因為與本問題關聯不大就不贅述啦~

怎麼樣~是不是很簡單~

V1.2更新日誌:
修改了一些細節和內容順序


如果學會了python的基本語法,我認為入門爬蟲是很容易的。

我寫的第一個爬蟲大概只需要10分鐘,自學的 scrapyd , 看官方文檔花了20分鐘,

因為我英文不是很好,很多單詞需要搜索一下。

官方文檔鏈接 https://docs.scrapy.org/en/latest/intro/tutorial.html )

(scrapy 並不是入門必須的,所以你可以看完我的答案再酌情考慮 scrapy )

再接觸到了 requests , lxml ,配合基本庫 urllib, urllib2 就幾乎無所不能了。

後來有人推薦我用 BeatufulSoup 之類的庫,但其實原理都差不多。

一、入門爬蟲的乾貨

0. 爬蟲的基本思路

a. 通過URL或者文件獲取網頁,

b. 分析要爬取的目標內容所在的位置

c. 用元素選擇器快速提取(Raw) 目標內容

d. 處理提取出來的目標內容 ( 通常整理合成一個 Json)

e. 存儲處理好的目標內容 (比如放到 MongoDB 之類的資料庫,或者寫進文件里。)


1. 為什麼我入門爬蟲那麼快,我是不是在裝逼?

答:我自己總結了一下,在接觸爬蟲之前:

a. 我挺了解HTTP 協議(看了《HTTP權威指南》),

b. 我寫過基於Flask框架的後端(大概三年前@蕭井陌 在知乎上推薦Flask框架,然後我就自學了,用的是《Flask Web開發:基於Python的Web應用開發實戰 》)

c. 我寫過前端(HTML+CSS+JS),了解什麼是DOM ,會一點jquery。

d. 正則也是勉強夠用的。

e. 本人大學也是計算機專業,學習挺認真的。

f. 所以算是厚積薄發。


2. 那麼毫無專業基礎,也沒有前後端基礎的人應該怎麼辦?

答:那當然要超過半小時啦。先花點時間去大概了解以下內容:

a. HTTP協議的請求方法,請求頭部,請求數據

b. 大概了解一下什麼是 cookie

c. 學一點HTML和元素選擇器

d. 學會使用Chrome 的 開發者工具
磨刀不誤砍柴工,當然如果有人帶著,這些大概1-2小時就能過到能湊合用的程度了。如果沒人帶,就上網搜索學習一下,也很快的,估摸最多十小時。

ps, 阮一峰老師的技術入門博客寫得很不錯,除此之外,博客園也有很多好資源。


3. 放一個新鮮出爐的代碼,看懂就能入門了:

4. Python 爬蟲常用的庫是哪些?入門應該掌握哪些庫?

答:網上有很多相關的資料,但是我個人覺得新入門的人,不需要也不應該一下子接觸所有的庫。正如幼兒剛開始學說話的時候,不應該同時教普通話粵語閩南語英語。

我個人認為,學會 requests 和 lxml ,就可以入門爬蟲了。
其他的常用庫,自己搜,但注意貪多嚼不爛。 (我整理出來的被小馬甲人噴了,我很不開心,所以我自己存好刪了)


二、一點點涉及爬蟲進階的分界線

0. 知乎上很多爬蟲代碼,一個函數幾十行,是很不好的。應該盡量減少重複代碼。


1. 重要的事情說三次,

函數不是越長越好, 好代碼應該簡單易懂好維護!

函數不是越長越好, 好代碼應該簡單易懂好維護!

函數不是越長越好, 好代碼應該簡單易懂好維護!

(放在進階是因為能做到這一點的爬蟲代碼不多,很多都一團亂麻,坑死接盤俠)


2. Scrapy + MongoDB + Redis 分散式爬蟲系統其實不複雜。

a). Redis 用來存儲要爬取的網頁隊列,也就是任務隊列

b). MongoDB 用來存儲爬取的內容結果。

c) . Scrapy 里放爬蟲crawler , 分別爬取不同的網頁內容,

ps:分散式這個東西,聽起來很恐怖,但是拆開了也就這樣。所以不用害怕。

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

----- 講事故的分割線 -----

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

曾經在某創業公司被趕鴨子上架(我最初是一個後端程序員,現在成分有點複雜,一言難盡),要在一星期內跟一個分散式爬取各大網商(包括淘寶天貓京東等十幾家網商,Scrapy + MongoDB + Redis)的數據。

當時差點嚇壞我了,因為沒寫過爬蟲。

然後leader 給我的線索只有 基本框架是 Scrapy。

也許是無知者無畏, 也沒想到去問誰,就自己看了 Scrapy 的文檔,半小時就寫出來了。

後來就很順利把分散式爬蟲系統搭起來了。

還爬了谷歌、百度、Bing、 Pinterest 、Instagram 等大量和當時公司業務相關的數據。

就這樣,我做到了。

當然,加了不少班。

ps: 用很多的機器,代表需要爬取的爬取的數據量很多,但是和項目的複雜程度不一定相關。所以不要害怕。害怕也沒用,需求來了,一邊顫抖一邊加班也要寫完代碼的。


以下是我學python爬蟲的打怪升級之路,過程充滿艱辛,也充滿歡樂,雖然還未打倒大boss,但一路的風景就是最大的樂趣,不是么?希望大家能get到想要的東西!

多圖預警!

以下奉獻一段爬取知乎頭像的代碼

import requests
import urllib
import re
import random
from time import sleep
def main():
url="知乎 - 與世界分享你的知識、經驗和見解"
#感覺這個話題下面美女多
headers={省略}
i=1
for x in xrange(20,3600,20):
data={"start":"0",
"offset":str(x),
"_xsrf":"a128464ef225a69348cef94c38f4e428"}
#知乎用offset控制載入的個數,每次響應載入20
content=requests.post(url,headers=headers,data=data,timeout=10).text
#用post提交form data
imgs=re.findall("& #在爬下來的json上用正則提取圖片地址,去掉_m為大圖
for img in imgs:
try:
img=img.replace("\","")
#去掉字元這個干擾成分
pic=img+".jpg"
path="d:\bs4\zhihu\jpg\"+str(i)+".jpg"
#聲明存儲地址及圖片名稱
urllib.urlretrieve(pic,path)
#下載圖片
print u"下載了第"+str(i)+u"張圖片"
i+=1
sleep(random.uniform(0.5,1))
#睡眠函數用於防止爬取過快被封IP
except:
print u"抓漏1張"
pass
sleep(random.uniform(0.5,1))

if __name__=="__main__":

main()

結果:

最後,請關注我吧,我會好好維護你的時間線的 \( ^▽^ )/


其實寫爬蟲是一個很微小的事情, 在12年和14年,我卻單靠這2個爬蟲獲得了offer,所以爬蟲真的算Python工程師的必修課。這2個爬蟲其實都很cute,現在看起來有很多地方其實理解的不夠深入,也有一些實現地方做的不好,但是畢竟是當時的我,就保留著吧。

其實拿到數據怎麼用,比如做數據分析,做創業項目原始啟動數據,數據可視化等等。那我就利用Web開發的優勢,把數據在頁面上展示出來吧。無圖無真相,先上圖:

這是IPhone打開看到的:

移動版的圖有點糊,是因為使用了小尺寸的圖片,擔心太浪費讀者的手機流量。

我剛爬了網易雲音樂精彩評論, 其中包含了 28925 個歌手(組合)演唱的 710182 首歌曲中的 720699 條評論。它們都在這裡 雲音樂評論。

我在用隨機刷著玩的時候,看到了這麼一條:

AJAPKK:阿黛爾:你經歷過絕望嗎

我就點進去聽了下這首廖佳琳版本的Rolling in the deep,額那一天我單曲循環了一下午的這首神曲。建議一邊聽一邊繼續向下看。

其次是發現最熱的評論中薛之謙的歌曲佔了好幾個,好吧我得先承認,之前認為喜歡參與綜藝節目的歌手歌唱的都不行,尤其薛之謙以段子手而著名。但是看到總榜之後,我還是挨個聽了他的歌,覺得其實還行。

在知乎,感覺沒用過Python寫爬蟲都不好意思和人打招呼。我想寫篇爬蟲的文章,所以就開始找需求,其實一開始我是準備爬豆瓣害羞組(不懂得可以搜一搜),但是連續2個深夜2點去蹲守,發現現在那幾個小組不夠勁爆,而且量也太少,而且擔心發了文章有人舉報我 ~=(??? ? ???) ,所以作罷。

上班的路上,除了看kindle,我也經常會帶著耳機聽網易雲音樂(簡稱網雲吧)裡面收藏的歌,額,其實經常還能看到好多好玩的評論的,有辣眼睛的,有悲傷的,有總結很精闢的,有講一些不是同年代人不會懂的。可以先預覽下 網易雲音樂有哪些有趣的評論? 和 網易雲音樂評論量最高的歌曲有哪些?在這些評論里你又發現了什麼值得品味的故事?

等不及了? 它們都在這裡 雲音樂評論, 裡面有 28925 個歌手(組合)演唱的 710182 首歌曲中的 720699 條精彩評論。

這個項目地址是: GitHub - dongweiming/commentbox(fork時別忘記點贊哦 ), 使用的技術:

1. 後端: Flask + Mongoengine + Mako + requests + Redis + lxml + concurrent.futures

2. 前端:React + Mobx + Fetch + Material-UI + ES6 + Webpack + Babel

今天先和大家聊聊寫個爬蟲需要熟悉哪些知識,思路是什麼,怎麼實踐的,歡迎關注專欄,節後我再聊後端和前端的實現, 也有使用Flask的經驗。

需求分析

既然要爬整站的熱門評論,就要找到「入口」。什麼是入口呢?就是類似聚合頁,比如抓知乎的全部問題和答案,我的思路是先爬 話題廣場, 爬每個一級話題,再去爬話題下子話題,如 教育 - 熱門問答 。 然後不斷翻頁就好了。由於一個問題可以有多個話題標籤,需要注意緩存已爬取和正在爬取的答案頁面地址,防止重複爬取。

那網雲呢?評論在歌曲下,歌曲在歌手下。找到全部歌手就好了。所以先爬 http://music.163.com/#/discover/artist, 找到規律,按照歌手所在地區和首字母翻就能遍歷了。

其次是預估最後的結果量。其實我這幾十萬的只是網雲評論數據的一個小小小子集,主要是看爬取要花的時間,以及可提供存儲的空間。對這個需求來說,有些歌曲都是幾十萬個評論,我用一台非閑置的伺服器抓取,肯定一年也抓不完,不要忘記,抓取的瓶頸不在你使用多線程,多進程或者asyncio,主要在對方對你的抓取的容忍程度以及在爬和反爬策略傷的博弈的結果。而且我的VPS是1G內存,考慮爬取下來存入MongoDB的空間,並要給Redis預留緩存使用內存的使用量,所以我決定:

1. 每個歌手只抓取Ta最熱門的50首歌曲。

2. 每首歌只要最熱的前10條評論。

當時目測歌手數量在1-2w,而有些冷門歌手沒有50首歌曲,或者熱門評論不足,也就是大概200萬條左右(理想情況下 1-2w * 50 * 10)。實際上和我預期的少了不少,但是還是讓我的1G VPS捉襟見肘了... #論確定需求的重要性#

技術選型

我使用過各種解析頁面的庫,現在一般只使用BeautifulSoup(bs4)或者lxml,如果頁面比較簡單,標籤寫的比較嚴謹且需求單一或者一次性一般都用BeautifulSoup,比如豆瓣;複雜的、未來會一直都在用的選擇lxml,比如淘寶這種頁面被各業務線拼的模板。

我其實推薦大家好好學習xpath,這也是我選擇lxml的一個原因。那為什麼要用xpath,假如你只是爬一個站,其實無所謂,假如你要爬各種同類型的網站,比如豆瓣東西的發布東西,它支持發布數十個網站,肯定得讓抓取框架化,因為發布東西要的那些欄位都是定的,比如標題,價格,商品圖片,用xpath新人可以不關心它怎麼運作的,只是按照xpath尋找對應的元素去獲取之,幾分鐘就能學會如何對一個新的網站進行支持,假如使用BeautifulSoup,你要寫一坨坨的解析、遍歷。而xpath永遠都是一句搞定。

接著說 concurrent.futures, 這是一個在Python 3.2 的時候就被放進標準庫的模塊,它高度抽象出了非同步執行任務的介面,把隊列的使用隱藏起來,而且多進程和多線程介面統一,對於使用來說,切換多進程和多線程很簡單。這比你寫一大坨的多進程或者多線程的代碼要簡單很多。它的數據流是這樣的:

我們這個需求中抓取邏輯中,這樣使用:

from concurrent.futures import ProcessPoolExecutor

with ProcessPoolExecutor(max_workers=2) as executor:
for artist_id in unprocess_artist_list():
executor.submit(parser_artist, artist_id)

可以把它理解成一個2個進程的進程池。如果你的伺服器CPU個數更多,處理能力更強,不要吝嗇加大這個值哦。

最後說requests。這個太有名,不用它的人可能不理解為啥都用它,它的說明是「Python HTTP Requests for Humans」,是的,其實並沒有人要用它,你得自己寫一大坨的代碼才能支持會話,Cookie,代理等需求。對於沒有自虐傾向的人來說,Python標準庫提供的方案確實太底層了。我之前還特意研究了下為了這麼好的東西不直接放進標準庫? 看 Consider Requestsamp;amp;amp;#x27; Inclusion in Python 3.5amp;amp;amp;#x27;s Standard Library · Issue #2424 其中多個大神出沒哦。

怎麼樣不被發現

1. 不要用一個IP狂爬。所以要準備一堆可用的代理IP,如果公司有額外的比較閑的IP最好了,閑著也是閑著,在不影響正常業務的提前下,多換IP。否則就要想辦法獲取免費代理。我的書中這個地方有寫。

2. 勤換UA。我看很多人喜歡在配置中列一些UA, 其實吧,可以使用 GitHub - hellysmile/fake-useragent: up to date simple useragent faker with real world database。其實我也推薦大家偽裝成各大搜索網站的UA, 比如Google UA 有這樣一些 Google 抓取工具,說到這裡,有的網站,你添加referfer欄位是搜索網站也是有用的,因為網站是希望被索引的,所以會放寬搜索引擎的爬取策略。

3. 爬取間隔自適應。就是已經限制了你這個IP的抓取,就不要傻傻重複試,怎麼滴也得休息一會。網易雲音樂操作起來比較簡單,sleep一下就好了。其實sleep的間隔應該按情況累加,比如第一次sleep 10秒,發現還是被約束。那麼久sleep 20秒... 這個間隔的設置已經自適應的最終效果是經驗值。

4. 驗證碼識別。現在攻防讓驗證碼技術層出不窮,其實好多都是自己寫演算法識別,並不開源,開源的就是tesseract,還可以借用百度識圖平台試試。我個人還是傾其所有的做好其他的地方,不要讓人家彈出驗證碼讓我輸入。

開始爬

首先一定要防止「由於異常等原因造成爬蟲程序錯誤,重新啟動還會重新爬」的尷尬。我建了一張Process表,用來存爬取的狀態:開始爬取置狀態為「PENDING」,抓取完成置狀態為「 SUCCEEDED」(當然也有失敗,比如頁面解析未覆蓋到情況造成的失敗,之後失敗的狀態應該沒有條目才對,否則就要去兼容)。每次抓取程序啟動都會檢查哪些PENDING的先抓完,抓過的直接忽略去下一個。

真的數據Model包含4個:Artist(歌手)、Song(歌曲)、Comment(評論)和User(評論人),我們感受一下抓取的過程(截取重要部分):

def parser_artist(artist_id):
create_app() # Flask應用要先初始化
process = Process.get_or_create(id=artist_id) # Process以歌手為單位
if process.is_success: # 如果已經成功直接返回了
return

tree = get_tree(ARTIST_URL.format(artist_id)) # 使用requests獲取頁面文本,轉化為lxml對象

artist = Artist.objects.filter(id=artist_id)
if not artist: # 如果之前沒抓過
artist_name = tree.xpath("//h2[@id="artist-name"]/text()")[0]
picture = tree.xpath(
"//div[contains(@class, "n-artist")]//img/@src")[0]
artist = Artist(id=artist_id, name=artist_name, picture=picture)
artist.save()
else: # 如果之前抓過,但是該歌手的歌曲沒抓完
artist = artist[0]
song_items = tree.xpath("//div[@id="artist-top50"]//ul/li/a/@href")
songs = []
for item in song_items:
song_id = item.split("=")[1]
song = parser_song(song_id, artist) # 進入抓取和解析歌手模式
if song is not None:
songs.append(song)
artist.songs = songs
artist.save()
process.make_succeed() # 標記歌手下的熱門歌曲的熱門評論抓完

整體就是這樣。整個抓取解析的流程的代碼加上空格是110行。其中的熱門評論是通過API獲取的,思路可見 網易雲音樂新版WebAPI分析。

原文在這裡: https://zhuanlan.zhihu.com/p/22456856

歡迎關注本人的微信公眾號獲取更多Python相關的內容(也可以直接搜索「Python之美」):

http://weixin.qq.com/r/D0zH35LE_s_Frda89xkd (二維碼自動識別)


——————最重要的話寫在前面——————
0、新手/喜歡練習/歡迎交流/邀請/我是看著這個問題下面的答案學習的
1、帶著一個目的來學爬蟲。#我的目的實現了…所以我來寫這個回答了。
2、不要慫就是干!系統學習固然好,直接寫一個項目出來效果更加簡單粗暴!(不過自己現在的水平寫出來都是流水一般的面向過程的代碼,代碼的重複部分太多,正在回過頭去學習面向對象編程,學習類和方法的使用。不過我還是堅定地認為入門的時候應該直接簡單粗暴地實踐一個項目
3、哪裡不會搜哪裡!哪裡報錯改哪裡!相信我你遇到的99%的問題都能從網上找到相似的問題,你需要做的就是寫代碼!搜問題!調BUG!你搜不到解決辦法的情況下,80%的情況是你搜索的姿勢不對,另外20%可能需要你自己動動腦子,換個思路去做。
舉個印象最深的例子。
我在統計知乎回答voters的具體情況的時候(後面會介紹)發現知乎的數據是這樣發送的。
http://www.zhihu.com/answer/15219795/voters_profile?total=70offset=10follows=wAp13IyyllRUgyux1Zp3MSGySe4GNnw-AG6-yDt_MO68ywzyjX_TGN3o
什麼鬼(摔)。
等到我辛辛苦苦用正則把裡面的信息提出來的時候發現我得到的數據是這樣的…

我的內心是崩潰的……
問題很明顯是編碼問題……用戶那一列全部是unicode編碼……轉成中文就好了嘛……
我剛開始也是這麼想的…當我嘗試了各種encode和decode…以後整個人都不好了。
大概是這樣的。我用Shell演示一下…應該能看懂。

但是我的字元串是自動獲取的啊,怎麼可能挨著用 u" "賦值……
於是我開始了漫長的搜索之路……在看了無數篇重複度高於百分之80的關於編碼的文章後,在我都快要放棄的時候…看到了這個…水木社區-源於清華的高知社群 你能理解我當時內心的酸爽嗎…

大概就是這樣。
所以我遇到的問題已經很奇葩了依然還是有解決辦法的嘛。Windows下面編碼各種混亂,系統編碼,編程時編輯器的編碼,抓取網頁以後網頁的編碼,Python2的編碼,Python3的編碼……新人真的很容易搞昏頭。
例子不多言了。後面還有很多。

——————正文1:我的爬蟲入門,不談學習,只聊項目(代碼已貼)——————
前面說到學爬蟲需要一個目標。那我的目標是什麼呢?聽我慢慢講。
前些日子我回答了一個問題高考後暑假應該做什麼事? - 生活 我回答這個問題的時候呢關注人數也就才剛剛過百,我的贊數也漲的很慢…
可是突然有一天呢,我發現突然就出現了一個300贊的回答…當時那個問題的關注似乎還不到300。我百思不得其解…但是我看了看那個回答的贊同和答主的主頁。
大概是這樣的:

然後我隱隱覺得…可能會是刷贊?當然我們不能惡意地去揣測別人,要拿數據說話,畢竟知乎現在的三零真實用戶還是蠻多的,不一定都是水軍的小號。

於是我一個從來沒有學過爬蟲的人就開始學爬蟲了…然而並不一帆風順。首先是知乎顯示「等人贊同」的方式做了修改,參見如何評價知乎新的「某某等人贊同」顯示方式? - 如何評價 X。
其次我剛開始的時候不會維持登陸…每次抓到的數據里都有很多的「知乎用戶」(也就是未登錄狀態下抓取不成功)。
為了行文的連貫我跳過中間學習時做的幾個小爬蟲…直接放我做成功的結果吧。

選取的樣本回答依次為:
@段曉晨高考後暑假應該做什麼事? - 段曉晨的回答
@EdgeRunner高考後暑假應該做什麼事? - EdgeRunner 的回答
@孔鯉高考後暑假應該做什麼事? - 孔鯉的回答
@Emily L能利用爬蟲技術做到哪些很酷很有趣很有用的事情? - Emily L 的回答
@chenqin為什麼 2015 年初,上海有衛計委官員呼籲大家生二胎? - chenqin 的回答
感興趣的可以下載數據
getvoters.xls_免費高速下載
getvoters2.xls_免費高速下載
getvoters3.xls_免費高速下載
getvoters4.xls_免費高速下載
getvoters5.xls_免費高速下載
結論就是……沒有結論。
話說我回答的那個三零用戶比例也好高啊……我真的沒有刷贊我保證!(話說我的贊裡面要是有水軍的話我會很傷心的……我一直以為是我寫的好他們才贊我的QAQ)

到這裡第一個項目就結束了…
這個我暫時不貼代碼…代碼不完善…還得有點小修改。兩天內放上來。

——來貼代碼——
loveQt/Zhihu_voters · GitHub
使用前請填寫config.ini文件,cookie不用填。
依然不完善。是這樣的,知乎在獲取「等人贊同」的時候有一個很畸形的地方在於……答案的id很畸形。
比如我現在這個答案。
http://www.zhihu.com/question/20899988/answer/49749466
當我點擊「等人贊同」的時候。抓包得到請求地址。我用的是Firefox的Firebug

這個地址是這樣的:
http://www.zhihu.com/answer/15299264/voters_profile
如果你繼續往下拉,知乎會自動載入更多用戶,你會得到形如這樣的地址:
http://www.zhihu.com/answer/15299264/voters_profile?total=143offset=10follows=YCPQ47_p62oaS49riUMu-4sTvAQfYpoe5E2RRX9lj40vWR6E4J5W_T-U
分析這個地址的構成就會發現
/answer/這裡應該是這個回答的唯一id,而這個id顯然與上面的/question/20899988/answer/49749466是不一樣的,我抓了好多個回答,結論應該是沒有規律的,知乎應該是給每個問題一個id,每個問題下面的回答一個id,但是只有點贊的時候這個回答才會得到它關於voters的id……

所以我沒辦法實現完全的自動化…你如果想爬指定的回答,似乎得先手動抓包了 QAQ
抓包的示意如上,打開網路面板,點擊「等人贊同」,找到地址中的數字就可以了。

如果你會抓包了請接著看下去。
代碼的下載地址在上面的github。Python版本為2.7,希望你們會用pip安裝依賴的庫。
簡單說幾個方面。

1、知乎的登陸。我模仿了 @egrcc 和 @7sDream 的項目,使用了requests.session。

def login():
cf = ConfigParser.ConfigParser()
cf.read("config.ini")
cookies = cf._sections["cookies"]

email = cf.get("info", "email")
password = cf.get("info", "password")
cookies = dict(cookies)
global s
s = requests.session()
login_data = {"email": email, "password": password}
header = {
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:34.0) Gecko/20100101 Firefox/34.0",
"Host": "www.zhihu.com",
"Referer": "http://www.zhihu.com/",
"X-Requested-With": "XMLHttpRequest"
}
r = s.post(Login_url, data=login_data, headers=header)

在實現了登陸之後,只要使用s.get(url)得到的頁面都是登陸成功的狀態。
//注意,不登陸或者登陸不成功的情況下也可以爬取數據。點贊、感謝、提問、回答的數值都能正常獲取,但是會出現部分用戶無法獲取名稱和用戶地址,顯示為「知乎用戶」

2、獲取數據
假如我們獲取到了單頁數據,那麼使用正則可以很簡單地獲取到想要的數據,具體參見代碼。
我們需要做的,其實是獲取那些需要爬取的URL。
通過上面對於網址的分析我們可以發現,網址的組成為domain/answer/ans_id/voters_profile?total=xxxoffset=xx...
後面那堆亂碼不重要,重要的是total和offset,每次會展示出10個用戶的數據,所以我們只需要獲取到點贊的總數total,就可以知道需要循環多少步(total/10),注意從是0開始,不然會漏掉前十個數據。
而這也就是我在zhihu-vote.py中的做法。其餘的部分就沒有什麼難度了,入門的同學應該都可以看懂。

3、改進
我們在zhihu-vote.py中通過構造地址的方法來,通過循環實現對所有voters_profile的遍歷。但是如果我們了解json的知識的話,我們可以發現其實每個頁面都是json格式的。

其中最關鍵的地方在於next。我們會發現其實每個頁面中都包含了下一頁的地址!這樣我們是不是可以讓爬蟲每爬一頁自己找到一個地址,然後自己去爬下一頁呢?
可是這樣做有一個問題,如何控制循環呢?
假如我們去看最後一個頁面的話,會發現是這樣的。

注意這裡的next值為空
而我們知道(不知道的你現在知道了),空字元串在作為條件判斷時相當於False
所以我寫了zhihu-voteV2.py
其中核心的改動是

Vote_url = Zhihu + "answer/" + ans_id +"/voters_profile"
h = s.get(Vote_url)
html = h.content.encode("utf-8")
target = json.loads(html)
while target["paging"]["next"]:
Vote_url = "http://www.zhihu.com"+target["paging"]["next"]

這樣就實現了程序每次爬取頁面時從頁面中獲取地址,而不是人為構造地址循環。下面是原來的做法。

for num in range (0,page_num):
Vote_url = Zhihu + "answer/" + ans_id +"/voters_profile?total="+str(total)+"offset="+str(num)+"0"

講實話我不知道這兩種寫法哪種好,但我還是蠻高興自己發現了第二種做法。
於是我做了一個運行時間的測試…提車了,下一步需要做什麼? - 車海沉浮高永強的回答 16K的贊
運行結果如下:

構造地址的辦法用時451秒,第二種辦法用時251秒。
……我不知道為什麼方法二會比方法一快,可能是網速吧……QAQ。有了解的前輩還望告知原因…

到這裡也就結束了。最後的結果是寫入excel的,有知友說讓我去學習csv,已經在看了,不過這次還是用的讓人又愛又恨的excel。

按照慣例寫To-dos:

  • 完善Github的文檔說明
  • 想辦法看能不能自動獲取那個蛋疼的ans_id就不用每次都手動抓包了,selenium?我不會用啊TAT
  • 在點贊的頁面我們只能得到用戶的4個數據,也就是贊同、感謝、提問、回答,有些時候我們或許想知道他的關注人數和被關注人數…然而那個得到用戶的頁面中去爬取了。不過想通過用戶URL得到用戶的具體數據是有現成的輪子的……egrcc/zhihu-python · GitHub (PY2) @egrcc 和7sDream/zhihu-py3 · GitHub(PY3) @7sDream 我想辦法看怎麼把我現在獲取答案點贊用戶信息的方法pull給他們…直接調用他們的User類就ok了~
  • 抓特別多的數據時考慮多線程和gzip…?接下來就要學這個了…會的話我就用了…還記得我去爬知乎贊數最高的答案…一個答案爬了30分鐘…

——更新完畢,大家學習愉快,共同進步——

——Windows 平台Py2編碼問題畸形但有效解法——

在..Python27Libsite-packages下新建sitecustomize.py

添加代碼

import sys
sys.setdefaultencoding("utf-8")

——————正文2:學習路上順便寫的項目——————
在學習路上寫了許多類似test的小小項目,就不贅述了。下面貼出來三個還算有結果的。
1、抓取知乎話題下面的問題,分析容易得贊的問題
具體描述在 第一次在知乎獲得 1000 以上的贊是什麼體驗? - 段曉晨的回答 寫過了。
代碼在知乎將如何應對「狗日的知乎」計劃? - 段曉晨的回答 裡面有。需要用到7sDream/zhihu-py3 · GitHub

2、寫完1中項目以後。我爬取了爬蟲話題分類下面的所有回答。結果爬蟲話題所有問題_20150531.xls_免費高速下載
然後我從其中挑選了「關注量/回答量」較大的問題(也就是有人關注但有效回答較少)寫了以下兩個回答,大家可以看看。
如何使用 python 抓取 雪球網頁? - 段曉晨的回答
如何用Python抓取寫一個抓取新浪財經網指定企業年報的腳本? - 段曉晨的回答

——————結語:談談學習——————
至此我能說的就說完了。
鼓起勇氣來回答這個問題,不知道自己有沒有資格。畢竟自己也就才學了一周多一點。自認為還談不上入門……因為不會的實在太多。
系統學習爬蟲的思路別人講的肯定比我好。我的經驗在開頭已經說過了……不要慫就是干哪裡不會搜哪裡!哪裡報錯改哪裡!
如果一定要再補充寫什麼,貼上我之前回復知友的評論吧。
首先要帶著一個目的去學,這個目的不能太複雜,不能一上來就搞那種需要模擬登陸,需要js動態實現的網站,那樣你會在登陸那兒卡很久,又在js實現那兒卡很久,很容易挫傷學習積極性。比如我最初的目的就是爬知乎。知乎登陸/不登陸數據會有差別,比如抓不到某些人的數據,返回「知乎用戶」這種。
有了目的,你需要一些基礎知識。html是什麼,標籤是什麼,瀏覽器和伺服器之間通信(比如抓包)。爬蟲的原理就是要把網頁的源碼整個下載下來,然後去裡面尋找我們需要的信息。所以首先你得能獲取正確的網址,然後通過配置你的程序(Headers偽裝瀏覽器,代理防止封ip等)來成功訪問網頁並獲取源碼。…………諸如此類的基礎知識,其實特別簡單。你可以去找一些爬百度貼吧,爬煎蛋,爬糗事百科的例子,很容易就會上手。
有了源碼你需要去裡面尋找東西,比較簡單的有正則表達式,更方便的有BeautifulSoup。對json解析有json。等等。
最後你可能需要一些模塊化的思想。比如我在寫爬知乎問題的時候,寫了一些代碼來讓它把輸出的結果自動保存到excel里…那我是不是可以把寫入excel這個行為單獨抽出來,定義為一個方法。以後每次遇到需要excel的地方我就拿過來改一下就能用。同樣的思路,登陸過程,post數據的過程,解析數據的過程,是不是都可以自己慢慢積累為模塊。就好像你有了很多樂高積木,以後再做的時候就不需要做重複的事情,直接搭積木就好~

最後感謝一下在我學習過程中參考過的別人的回答和博客。太多了無法一一列舉。再次感謝。

編程是最容易獲得的超能力。你還在等什麼?


呃本來只是想給題主一個傳送,因為本身也是一個Python愛好者。

簡單介紹一下我的那個入門教程,其實根本算不上教程,基本上算是一個學習的筆記,很多內容都是從網上整理然後自己實踐得到的結果。

如果說深入學習爬蟲,還是建議那本《自己動手寫網路爬蟲》,是我的啟蒙教程,語法是Java的,但是思路是相通的。

Python爬蟲的學習,最主要的是多摸索,多試驗(哪個不是這樣)。先從最簡單的例子做起,比如爬取百度主頁,爬取百度圖片,然後正則,巴拉巴拉。

我的學習筆記可以作為一個參考的索引,裡面很多東西沒有深入探討,因為畢竟當時我也只是一個小菜(現在也差不多)。

給初學者一個入門的途徑,接下來的路還是要自己走^_^

至於匿名、個人習慣潛水。

繼續匿了。

------------------------------------------
以前寫過一個爬蟲入門的系列,傳送:專欄:Python爬蟲入門教程
一共12篇:
[Python]網路爬蟲(一):抓取網頁的含義和URL基本構成

[Python]網路爬蟲(二):利用urllib2通過指定的URL抓取網頁內容

[Python]網路爬蟲(三):異常的處理和HTTP狀態碼的分類

[Python]網路爬蟲(四):Opener與Handler的介紹和實例應用

[Python]網路爬蟲(五):urllib2的使用細節與抓站技巧

[Python]網路爬蟲(六):一個簡單的百度貼吧的小爬蟲

[Python]網路爬蟲(七):Python中的正則表達式教程

[Python]網路爬蟲(八):糗事百科的網路爬蟲(v0.2)源碼及解析

[Python]網路爬蟲(九):百度貼吧的網路爬蟲(v0.4)源碼及解析

[Python]網路爬蟲(十):一個爬蟲的誕生全過程(以山東大學績點運算為例)

[Python]網路爬蟲(11):亮劍!爬蟲框架小抓抓Scrapy閃亮登場!

[Python]網路爬蟲(12):爬蟲框架Scrapy的第一個爬蟲示例入門教程

比較入門,不過多接觸一些小demo沒有壞處哈


啦啦啦,我就是來送福利得!在這裡分享自己寫的抓取淘寶MM照片的爬蟲
原文:Python爬蟲實戰四之抓取淘寶MM照片
其實還有好多,大家可以看 Python爬蟲學習系列教程

福利啊福利,本次為大家帶來的項目是抓取淘寶MM照片並保存起來,大家有沒有很激動呢?

本篇目標

1.抓取淘寶MM的姓名,頭像,年齡

2.抓取每一個MM的資料簡介以及寫真圖片

3.把每一個MM的寫真圖片按照文件夾保存到本地

4.熟悉文件保存的過程

1.URL的格式

在這裡我們用到的URL是 http://mm.taobao.com/json/request_top_list.htm?page=1,問號前面是基地址,後面的參數page是代表第幾頁,可以隨意更換地址。點擊開之後,會發現有一些淘寶MM的簡介,並附有超鏈接鏈接到個人詳情頁面。

我們需要抓取本頁面的頭像地址,MM姓名,MM年齡,MM居住地,以及MM的個人詳情頁面地址。

2.抓取簡要信息

相信大家經過上幾次的實戰,對抓取和提取頁面的地址已經非常熟悉了,這裡沒有什麼難度了,我們首先抓取本頁面的MM詳情頁面地址,姓名,年齡等等的信息列印出來,直接貼代碼如下

__author__ = "CQC"
# -*- coding:utf-8 -*-

import urllib
import urllib2
import re

class Spider:

def __init__(self):
self.siteURL = "http://mm.taobao.com/json/request_top_list.htm"

def getPage(self,pageIndex):
url = self.siteURL + "?page=" + str(pageIndex)
print url
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode("gbk")

def getContents(self,pageIndex):
page = self.getPage(pageIndex)
pattern = re.compile("&.*?&(.*?)&.*?&(.*?)&",re.S)
items = re.findall(pattern,page)
for item in items:
print item[0],item[1],item[2],item[3],item[4]

spider = Spider()
spider.getContents(1)

運行結果如下

2.文件寫入簡介

在這裡,我們有寫入圖片和寫入文本兩種方式

1)寫入圖片

#傳入圖片地址,文件名,保存單張圖片
def saveImg(self,imageURL,fileName):
u = urllib.urlopen(imageURL)
data = u.read()
f = open(fileName, "wb")
f.write(data)
f.close()

2)寫入文本

def saveBrief(self,content,name):
fileName = name + "/" + name + ".txt"
f = open(fileName,"w+")
print u"正在偷偷保存她的個人信息為",fileName
f.write(content.encode("utf-8"))

3)創建新目錄

#創建新目錄
def mkdir(self,path):
path = path.strip()
# 判斷路徑是否存在
# 存在 True
# 不存在 False
isExists=os.path.exists(path)
# 判斷結果
if not isExists:
# 如果不存在則創建目錄
# 創建目錄操作函數
os.makedirs(path)
return True
else:
# 如果目錄存在則不創建,並提示目錄已存在
return False

3.代碼完善

主要的知識點已經在前面都涉及到了,如果大家前面的章節都已經看了,完成這個爬蟲不在話下,具體的詳情在此不再贅述,直接帖代碼啦。


spider.py

__author__ = "CQC"
# -*- coding:utf-8 -*-

import urllib
import urllib2
import re
import tool
import os

#抓取MM
class Spider:

#頁面初始化
def __init__(self):
self.siteURL = "http://mm.taobao.com/json/request_top_list.htm"
self.tool = tool.Tool()

#獲取索引頁面的內容
def getPage(self,pageIndex):
url = self.siteURL + "?page=" + str(pageIndex)
request = urllib2.Request(url)
response = urllib2.urlopen(request)
return response.read().decode("gbk")

#獲取索引界面所有MM的信息,list格式
def getContents(self,pageIndex):
page = self.getPage(pageIndex)
pattern = re.compile("&.*?&(.*?)&.*?&(.*?)&",re.S)
items = re.findall(pattern,page)
contents = []
for item in items:
contents.append([item[0],item[1],item[2],item[3],item[4]])
return contents

#獲取MM個人詳情頁面
def getDetailPage(self,infoURL):
response = urllib2.urlopen(infoURL)
return response.read().decode("gbk")

#獲取個人文字簡介
def getBrief(self,page):
pattern = re.compile("&(.*?)&