十年資深程序員教你用 python 爬蟲抓站總結了一些獨特的技巧!

學用python才3個多月!但是用編程語言還是十年左右了趴捅,今天就教大家一些非常簡單但是很實用的爬蟲技巧吧兵钮,希望能幫助到各位!用得最多的還是各類爬蟲腳本:寫過抓代理本機驗證的腳本钠右,寫過在discuz論壇中自動登錄自動發(fā)貼的腳本赋元,寫過自動收郵件的腳本,寫過簡單的驗證碼識別的腳本飒房,本來想寫google music的抓取腳本的搁凸,結果有了強大的gmbox,也就不用寫了狠毯。

2.使用代理服務器

這在某些情況下比較有用护糖,比如IP被封了,或者比如IP訪問的次數(shù)受到限制等等嚼松。

importurllib2

proxy_support = urllib2.ProxyHandler({'http':'http://XX.XX.XX.XX:XXXX'})

opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)

urllib2.install_opener(opener)

content = urllib2.urlopen('http://XXXX').read()

3.需要登錄的情況? ? ? ? ? ? ? ? ? 在學習中有迷茫不知如何學習的朋友小編推薦一個學Python的學習q u n 227? -435-? 450可以來了解一起進步一起學習嫡良!免費分享視頻資料

登錄的情況比較麻煩我把問題拆分一下:

3.2 表單的處理

登錄必要填表,表單怎么填献酗?首先利用工具截取所要填表的內容寝受。

比如我一般用firefox+httpfox插件來看看自己到底發(fā)送了些什么包

這個我就舉個例子好了,以verycd為例罕偎,先找到自己發(fā)的POST請求很澄,以及POST表單項:

可以看到verycd的話需要填username,password,continueURI,fk,login_submit這幾項,其中fk是隨機生成的(其實不太隨機颜及,看上去像是把epoch時間經過簡單的編碼生成的)甩苛,需要從網頁獲取,也就是說得先訪問一次網頁俏站,用正則表達式等工具截取返回數(shù)據中的fk項讯蒲。continueURI顧名思義可以隨便寫,login_submit是固定的乾翔,這從源碼可以看出。還有username施戴,password那就很顯然了反浓。

3.3 偽裝成瀏覽器訪問

某些網站反感爬蟲的到訪,于是對爬蟲一律拒絕請求赞哗。這時候我們需要偽裝成瀏覽器雷则,這可以通過修改http包中的header來實現(xiàn):

headers = {

'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'

}

req = urllib2.Request(

url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/',

data = postdata,

headers = headers

)

3.4 反”反盜鏈”

某些站點有所謂的反盜鏈設置,其實說穿了很簡單肪笋,就是檢查你發(fā)送請求的header里面月劈,referer站點是不是他自己度迂,所以我們只需要像3.3一樣,把headers的referer改成該網站即可猜揪,以黑幕著稱地cnbeta為例:

headers = {

'Referer':'http://www.cnbeta.com/articles'

}

headers是一個dict數(shù)據結構惭墓,你可以放入任何想要的header,來做一些偽裝而姐。例如腊凶,有些自作聰明的網站總喜歡窺人隱私,別人通過代理訪問拴念,他偏偏要讀取header中的X-Forwarded-For來看看人家的真實IP钧萍,沒話說,那就直接把X-Forwarde-For改了吧政鼠,可以改成隨便什么好玩的東東來欺負欺負他风瘦,呵呵。

3.5 終極絕招

有時候即使做了3.1-3.4公般,訪問還是會被據万搔,那么沒辦法,老老實實把httpfox中看到的headers全都寫上俐载,那一般也就行了蟹略。 再不行,那就只能用終極絕招了遏佣,selenium直接控制瀏覽器來進行訪問挖炬,只要瀏覽器可以做到的,那么它也可以做到状婶。類似的還有pamie意敛,watir,等等等等膛虫。

4.多線程并發(fā)抓取

單線程太慢的話草姻,就需要多線程了,這里給個簡單的線程池模板 這個程序只是簡單地打印了1-10稍刀,但是可以看出是并發(fā)地撩独。

fromthreadingimportThread

fromQueueimportQueue

fromtimeimportsleep

#q是任務隊列

#NUM是并發(fā)線程總數(shù)

#JOBS是有多少任務

q = Queue()

NUM = 2

JOBS = 10

#具體的處理函數(shù),負責處理單個任務

defdo_somthing_using(arguments):

print arguments

#這個是工作進程账月,負責不斷從隊列取數(shù)據并處理

defworking():

whileTrue:

arguments = q.get()

do_somthing_using(arguments)

sleep(1)

q.task_done()

#fork NUM個線程等待隊列

foriinrange(NUM):

t = Thread(target=working)

t.setDaemon(True)

t.start()

#把JOBS排入隊列

foriinrange(JOBS):

q.put(i)

#等待所有JOBS完成

q.join()

6 gzip/deflate支持

現(xiàn)在的網頁普遍支持gzip壓縮综膀,這往往可以解決大量傳輸時間,以VeryCD的主頁為例局齿,未壓縮版本247K剧劝,壓縮了以后45K,為原來的1/5抓歼。這就意味著抓取速度會快5倍讥此。

然而python的urllib/urllib2默認都不支持壓縮拢锹,要返回壓縮格式,必須在request的header里面寫明’accept-encoding’萄喳,然后讀取response后更要檢查header查看是否有’content-encoding’一項來判斷是否需要解碼卒稳,很繁瑣瑣碎。如何讓urllib2自動支持gzip, defalte呢取胎?

其實可以繼承BaseHanlder類展哭,然后build_opener的方式來處理:

importurllib2

fromgzipimportGzipFile

fromStringIOimportStringIO

classContentEncodingProcessor(urllib2.BaseHandler):

"""A handler to add gzip capabilities to urllib2 requests """

# add headers to requests

defhttp_request(self, req):

req.add_header("Accept-Encoding", "gzip, deflate")

returnreq

# decode

defhttp_response(self, req, resp):

old_resp = resp

# gzip

ifresp.headers.get("content-encoding") == "gzip":

gz = GzipFile(

fileobj=StringIO(resp.read()),

mode="r"

)

resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)

resp.msg = old_resp.msg

# deflate

ifresp.headers.get("content-encoding") == "deflate":

gz = StringIO( deflate(resp.read()) )

resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) # 'class to add info() and

resp.msg = old_resp.msg

returnresp

# deflate support

importzlib

defdeflate(data): # zlib only provides the zlib compress format, not the deflate format;

try: # so on top of all there's this workaround:

returnzlib.decompress(data, -zlib.MAX_WBITS)

exceptzlib.error:

returnzlib.decompress(data)

然后就簡單了,

encoding_support = ContentEncodingProcessor

opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )

#直接用opener打開網頁闻蛀,如果服務器支持gzip/defalte則自動解壓縮

content = opener.open(url).read()

fromtwisted.web.clientimportgetPage

fromtwisted.internetimportreactor

links = [ 'http://www.verycd.com/topics/%d/'%iforiinrange(5420,5430) ]

defparse_page(data,url):

print len(data),url

deffetch_error(error,url):

print error.getErrorMessage(),url

# 批量抓取鏈接

forurlinlinks:

getPage(url,timeout=5) \

.addCallback(parse_page,url) \ #成功則調用parse_page方法

.addErrback(fetch_error,url) #失敗則調用fetch_error方法

reactor.callLater(5, reactor.stop) #5秒鐘后通知reactor結束程序

reactor.run()

這么個多線程調用簡單明了匪傍,那么就這么設計吧,首先要有兩個隊列觉痛,用Queue搞定役衡,多線程的基本架構也和“技巧總結”一文類似,push方法和pop方法都比較好處理薪棒,都是直接用Queue的方法手蝎,taskleft則是如果有“正在運行的任務”或者”隊列中的任務”則為是,也好辦俐芯,于是代碼如下:

importurllib2

fromthreadingimportThread,Lock

fromQueueimportQueue

importtime

classFetcher:

def__init__(self,threads):

self.opener = urllib2.build_opener(urllib2.HTTPHandler)

self.lock = Lock() #線程鎖

self.q_req = Queue() #任務隊列

self.q_ans = Queue() #完成隊列

self.threads = threads

foriinrange(threads):

t = Thread(target=self.threadget)

t.setDaemon(True)

t.start()

self.running = 0

def__del__(self): #解構時需等待兩個隊列完成

time.sleep(0.5)

self.q_req.join()

self.q_ans.join()

deftaskleft(self):

returnself.q_req.qsize()+self.q_ans.qsize()+self.running

defpush(self,req):

self.q_req.put(req)

defpop(self):

returnself.q_ans.get()

defthreadget(self):

whileTrue:

req =self.q_req.get()

withself.lock: #要保證該操作的原子性棵介,進入critical area

self.running += 1

try:

ans =self.opener.open(req).read()

exceptException, what:

ans = ''

print what

self.q_ans.put((req,ans))

withself.lock:

self.running -= 1

self.q_req.task_done()

time.sleep(0.1) # don't spam

if__name__ == "__main__":

links = [ 'http://www.verycd.com/topics/%d/'%iforiinrange(5420,5430) ]

f = Fetcher(threads=10)

forurlinlinks:

f.push(url)

whilef.taskleft():

url,content = f.pop()

print url,len(content)

fromthreadingimportstack_size

stack_size(32768*16)

3、設置失敗后自動重試

defget(self,req,retries=3):

try:

response =self.opener.open(req)

data = response.read()

exceptException , what:

print what,req

ifretries>0:

returnself.get(req,retries-1)

else:

print 'GET Failed',req

return''

returndata

4吧史、設置超時

importsocket

socket.setdefaulttimeout(10) #設置10秒后連接超時

5邮辽、登陸

登陸更加簡化了,首先build_opener中要加入cookie支持贸营,參考“總結”一文吨述;如要登陸VeryCD,給Fetcher新增一個空方法login钞脂,并在init()中調用揣云,然后繼承Fetcher類并override login方法:

deflogin(self,username,password):

importurllib

data=urllib.urlencode({'username':username,

'password':password,

'continue':'http://www.verycd.com/',

'login_submit':u'登錄'.encode('utf-8'),

'save_cookie':1,})

url = 'http://www.verycd.com/signin'

self.opener.open(url,data).read()

于是在Fetcher初始化時便會自動登錄VeryCD網站。

9. 總結

如此冰啃,把上述所有小技巧都糅合起來就和我目前的私藏最終版的Fetcher類相差不遠了邓夕,它支持多線程,gzip/deflate壓縮阎毅,超時設置焚刚,自動重試,設置棧大小净薛,自動登錄等功能汪榔;代碼簡單蒲拉,使用方便肃拜,性能也不俗痴腌,可謂居家旅行,殺人放火燃领,咳咳士聪,之必備工具。

之所以說和最終版差得不遠猛蔽,是因為最終版還有一個保留功能“馬甲術”:多代理自動選擇剥悟。看起來好像僅僅是一個random.choice的區(qū)別曼库,其實包含了代理獲取,代理驗證,代理測速等諸多環(huán)節(jié)畏邢,這就是另一個故事了袱衷。

get到了嗎?是不是非常實用呢种玛?

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末藐鹤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赂韵,更是在濱河造成了極大的恐慌娱节,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祭示,死亡現(xiàn)場離奇詭異肄满,居然都是意外死亡,警方通過查閱死者的電腦和手機绍移,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門悄窃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蹂窖,你說我怎么就攤上這事轧抗。” “怎么了瞬测?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵横媚,是天一觀的道長。 經常有香客問我月趟,道長灯蝴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任孝宗,我火速辦了婚禮穷躁,結果婚禮上,老公的妹妹穿的比我還像新娘因妇。我一直安慰自己问潭,他們只是感情好猿诸,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狡忙,像睡著了一般梳虽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上灾茁,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天窜觉,我揣著相機與錄音,去河邊找鬼北专。 笑死禀挫,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拓颓。 我是一名探鬼主播特咆,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼录粱!你這毒婦竟也來了腻格?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤啥繁,失蹤者是張志新(化名)和其女友劉穎菜职,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旗闽,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡酬核,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了适室。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫡意。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捣辆,靈堂內的尸體忽然破棺而出蔬螟,到底是詐尸還是另有隱情,我是刑警寧澤汽畴,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布旧巾,位于F島的核電站,受9級特大地震影響忍些,放射性物質發(fā)生泄漏鲁猩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一罢坝、第九天 我趴在偏房一處隱蔽的房頂上張望廓握。 院中可真熱鬧,春花似錦、人聲如沸隙券。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽是尔。三九已至,卻和暖如春开仰,著一層夾襖步出監(jiān)牢的瞬間拟枚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工众弓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恩溅,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓谓娃,卻偏偏與公主長得像脚乡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子滨达,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容