国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - 總結python爬蟲抓站的實用技巧

總結python爬蟲抓站的實用技巧

2020-09-03 10:32Python教程網 Python

很多人學用python,用得最多的還是各類爬蟲腳本:有寫過抓代理本機驗證的腳本,有寫過自動收郵件的腳本,還有寫過簡單的驗證碼識別的腳本,那么我們今天就來總結下python爬蟲抓站的一些實用技巧。

前言

寫過的這些腳本有一個共性,都是和web相關的,總要用到獲取鏈接的一些方法,累積不少爬蟲抓站的經驗,在此總結一下,那么以后做東西也就不用重復勞動了。

1.最基本的抓站

?
1
2
import urllib2
content = urllib2.urlopen('http://XXXX').read()

2.使用代理服務器

這在某些情況下比較有用,比如IP被封了,或者比如IP訪問的次數受到限制等等。

?
1
2
3
4
5
import urllib2
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.需要登錄的情況

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

3.1 cookie的處理

?
1
2
3
4
5
import urllib2, cookielib
cookie_support= urllib2.HTTPCookieProcessor(cookielib.CookieJar())
opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)
urllib2.install_opener(opener)
content = urllib2.urlopen('http://XXXX').read()

是的沒錯,如果想同時用代理和cookie,那就加入proxy_support然后operner改為

?
1
opener = urllib2.build_opener(proxy_support, cookie_support, urllib2.HTTPHandler)

3.2 表單的處理

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

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

這個我就舉個例子好了,以verycd為例,先找到自己發的POST請求,以及POST表單項:

總結python爬蟲抓站的實用技巧

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

好的,有了要填寫的數據,我們就要生成postdata

?
1
2
3
4
5
6
7
8
import urllib
postdata=urllib.urlencode({
 'username':'XXXXX',
 'password':'XXXXX',
 'continueURI':'http://www.verycd.com/',
 'fk':fk,
 'login_submit':'登錄'
})

然后生成http請求,再發送請求:

?
1
2
3
4
5
req = urllib2.Request(
 url = 'http://secure.verycd.com/signin/*/http://www.verycd.com/',
 data = postdata
)
result = urllib2.urlopen(req).read()

3.3 偽裝成瀏覽器訪問

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

?
1
2
3
4
5
6
7
8
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 反”反盜鏈”

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

?
1
2
3
headers = {
 'Referer':'http://www.cnbeta.com/articles'
}

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

3.5 終極絕招

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

4.多線程并發抓取

單線程太慢的話,就需要多線程了,這里給個簡單的線程池模板 這個程序只是簡單地打印了1-10,但是可以看出是并發地。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from threading import Thread
from Queue import Queue
from time import sleep
#q是任務隊列
#NUM是并發線程總數
#JOBS是有多少任務
q = Queue()
NUM = 2
JOBS = 10
#具體的處理函數,負責處理單個任務
def do_somthing_using(arguments):
 print arguments
#這個是工作進程,負責不斷從隊列取數據并處理
def working():
 while True:
  arguments = q.get()
  do_somthing_using(arguments)
  sleep(1)
  q.task_done()
#fork NUM個線程等待隊列
for i in range(NUM):
 t = Thread(target=working)
 t.setDaemon(True)
 t.start()
#把JOBS排入隊列
for i in range(JOBS):
 q.put(i)
#等待所有JOBS完成
q.join()

5.驗證碼的處理

碰到驗證碼咋辦?這里分兩種情況處理:

     1、google那種驗證碼,涼拌

     2、簡單的驗證碼:字符個數有限,只使用了簡單的平移或旋轉加噪音而沒有扭曲的,這種還是有可能可以處理的,一般思路是旋轉的轉回來,噪音去掉,然后劃分單個字符,劃分好了以后再通過特征提取的方法(例如PCA)降維并生成特征庫,然后把驗證碼和特征庫進行比較。這個比較復雜,一篇博文是說不完的,這里就不展開了,具體做法請弄本相關教科書好好研究一下。

事實上有些驗證碼還是很弱的,這里就不點名了,反正我通過2的方法提取過準確度非常高的驗證碼,所以2事實上是可行的。

6 gzip/deflate支持

現在的網頁普遍支持gzip壓縮,這往往可以解決大量傳輸時間,以 VeryCD 的主頁為例,未壓縮版本247K,壓縮了以后45K,為原來的1/5。這就意味著抓取速度會快5倍。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import urllib2
from gzip import GzipFile
from StringIO import StringIO
class ContentEncodingProcessor(urllib2.BaseHandler):
 """A handler to add gzip capabilities to urllib2 requests """
 
 # add headers to requests
 def http_request(self, req):
 req.add_header("Accept-Encoding", "gzip, deflate")
 return req
 
 # decode
 def http_response(self, req, resp):
 old_resp = resp
 # gzip
 if resp.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
 if resp.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
 return resp
 
# deflate support
import zlib
def deflate(data): # zlib only provides the zlib compress format, not the deflate format;
 try:    # so on top of all there's this workaround:
 return zlib.decompress(data, -zlib.MAX_WBITS)
 except zlib.error:
 return zlib.decompress(data)

然后就簡單了,

?
1
2
3
4
5
encoding_support = ContentEncodingProcessor
opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )
 
#直接用opener打開網頁,如果服務器支持gzip/defalte則自動解壓縮
content = opener.open(url).read()

7. 更方便地多線程

總結一文的確提及了一個簡單的多線程模板,但是那個東東真正應用到程序里面去只會讓程序變得支離破碎,不堪入目。在怎么更方便地進行多線程方面我也動了一番腦筋。先想想怎么進行多線程調用最方便呢?

1、用twisted進行異步I/O抓取

事實上更高效的抓取并非一定要用多線程,也可以使用異步I/O法:直接用twisted的getPage方法,然后分別加上異步I/O結束時的callback和errback方法即可。例如可以這么干:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from twisted.web.client import getPage
from twisted.internet import reactor
 
links = [ 'http://www.verycd.com/topics/%d/'%i for i in range(5420,5430) ]
 
def parse_page(data,url):
 print len(data),url
 
def fetch_error(error,url):
 print error.getErrorMessage(),url
 
# 批量抓取鏈接
for url in links:
 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()

twisted人如其名,寫的代碼實在是太扭曲了,非正常人所能接受,雖然這個簡單的例子看上去還好;每次寫twisted的程序整個人都扭曲了,累得不得了,文檔等于沒有,必須得看源碼才知道怎么整,唉不提了。

如果要支持gzip/deflate,甚至做一些登陸的擴展,就得為twisted寫個新的 HTTPClientFactory 類諸如此類,我這眉頭真是大皺,遂放棄。有毅力者請自行嘗試。

2、設計一個簡單的多線程抓取類

還是覺得在urllib之類python“本土”的東東里面折騰起來更舒服。試想一下,如果有個Fetcher類,你可以這么調用

?
1
2
3
4
5
6
f = Fetcher(threads=10) #設定下載線程數為10
for url in urls:
 f.push(url) #把所有url推入下載隊列
while f.taskleft(): #若還有未完成下載的線程
 content = f.pop() #從下載完成隊列中取出結果
 do_with(content) # 處理content內容

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import urllib2
from threading import Thread,Lock
from Queue import Queue
import time
 
class Fetcher:
 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
  for i in range(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()
 
 def taskleft(self):
  return self.q_req.qsize()+self.q_ans.qsize()+self.running
 
 def push(self,req):
  self.q_req.put(req)
 
 def pop(self):
  return self.q_ans.get()
 
 def threadget(self):
  while True:
   req = self.q_req.get()
   with self.lock: #要保證該操作的原子性,進入critical area
    self.running += 1
   try:
    ans = self.opener.open(req).read()
   except Exception, what:
    ans = ''
    print what
   self.q_ans.put((req,ans))
   with self.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/'%i for i in range(5420,5430) ]
 f = Fetcher(threads=10)
 for url in links:
  f.push(url)
 while f.taskleft():
  url,content = f.pop()
  print url,len(content)

8. 一些瑣碎的經驗

1、連接池:

opener.open和urllib2.urlopen一樣,都會新建一個http請求。通常情況下這不是什么問題,因為線性環境下,一秒鐘可能也就新生成一個請求;然而在多線程環境下,每秒鐘可以是幾十上百個請求,這么干只要幾分鐘,正常的有理智的服務器一定會封禁你的。

然而在正常的html請求時,保持同時和服務器幾十個連接又是很正常的一件事,所以完全可以手動維護一個 HttpConnection 的池,然后每次抓取時從連接池里面選連接進行連接即可。

這里有一個取巧的方法,就是利用squid做代理服務器來進行抓取,則squid會自動為你維護連接池,還附帶數據緩存功能,而且squid本來就是我每個服務器上面必裝的東東,何必再自找麻煩寫連接池呢。

2、設定線程的棧大小

棧大小的設定將非常顯著地影響python的內存占用,python多線程不設置這個值會導致程序占用大量內存,這對openvz的vps來說非常致命。stack_size必須大于32768,實際上應該總要32768*2以上

?
1
2
from threading import stack_size
stack_size(32768*16)

3、設置失敗后自動重試

?
1
2
3
4
5
6
7
8
9
10
11
12
def get(self,req,retries=3):
 try:
  response = self.opener.open(req)
  data = response.read()
 except Exception , what:
  print what,req
  if retries>0:
   return self.get(req,retries-1)
  else:
   print 'GET Failed',req
   return ''
 return data

4、設置超時

?
1
2
import socket
socket.setdefaulttimeout(10) #設置10秒后連接超時

登陸更加簡化了,首先build_opener中要加入cookie支持,如要登陸 VeryCD ,給Fetcher新增一個空方法login,并在 init ()中調用,然后繼承Fetcher類并override login方法:

?
1
2
3
4
5
6
7
8
9
def login(self,username,password):
 import urllib
 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. 總結

如此,以上就是總結python爬蟲抓站的實用技巧的全部內容了,本文內容代碼簡單,使用方便,性能也不俗,相信對各位使用python有很大的幫助。

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 成人国产精品久久 | 日韩在线精品视频 | 五月婷婷在线观看视频 | 黄视频免费观看 | 99免费视频 | 亚洲电影在线观看 | jizz18毛片 | 国产精品高潮呻吟久久av野狼 | 日本一本视频 | 日本免费视频 | 超碰天天 | jizz18毛片 | 亚洲免费在线观看 | 中文字幕久久精品 | 中文字幕视频在线 | 国内成人精品2018免费看 | 爱免费视频 | 亚洲人天堂 | 亚洲视频中文字幕 | 久久99蜜桃综合影院免费观看 | 亚洲成人av | 日本三级电影网站 | 成人免费观看cn | 91免费观看视频 | 久久成人人人人精品欧 | 日韩欧美中文字幕在线视频 | 我要看日本黄色小视频 | 久久一区| 在线国产精品一区 | 欧美 日韩 国产 成人 在线 91 | 亚洲精品视频一区 | 欧美日韩高清在线一区 | 99色综合 | 日韩有码在线观看 | 日韩国产精品一区二区 | 日本视频免费高清一本18 | 日本三级视频在线观看 | 国产综合精品 | 毛片在线观看网站 | 日本一区二区三区中文字幕 | 亚洲第一视频 |