python 파충류 잡기 역 의 기술 총화:진급 편(전)
Posted on November 23, 2010 by observer
전에 한 편 쓴 적 이 있어 요.
python 파충류 가 역 을 잡 는 기술 을 정리 하여 많은 파충류 가 사용 하 는 방법 을 정리 했다.그 동 동 은 지금도 매우 유용 하 게 보이 지만 그 때 는 매우 요리 가 되 었 다.이 진급 편 은'쓸 수 있다'를'편리 하고 편리 하 게 쓸 수 있다'는 단계 로 끌 어 올 릴 계획 이다.
gzip/deflate 지원
현재 웹 페이지 는 gzip 압축 을 보편적으로 지원 합 니 다.이것 은 대량의 전송 시간 을 해결 할 수 있 습 니 다.Very CD 의 홈 페이지 를 예 로 들 면 압축 되 지 않 은 버 전 247 K 는 나중에 45K 를 압축 하여 원래 의 1/5 로 합 니 다.잡기 속도 가 5 배 빠르다 는 뜻 이다.
그러나 python 의 urllib/urllib 2 는 기본적으로 압축 을 지원 하지 않 습 니 다.압축 형식 으로 돌아 가 려 면 request 의 header 에'accept-encoding'이 라 고 쓰 고 response 를 읽 은 후에 header 가'content-encoding'항목 이 있 는 지 확인 하여 디 코딩 이 필요 한 지 여 부 를 판단 해 야 합 니 다.번 거 롭 습 니 다.어떻게 하면 urllib 2 가 gzip,defalte 를 자동 으로 지원 합 니까?
사실 BaseHanlder 류 를 계승 하고 buildopener 방식 으로 처리:
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)
그리고 간단 해..
encoding_support = ContentEncodingProcessor
opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler )
# opener , gzip/defalte
content = opener.open(url).read()
2.더욱 편리 하 게 다 중 스 레 드
한 글 을 요약 하면 간단 한 다 중 스 레 드 템 플 릿 을 언급 했 지만 그 동 동 은 프로그램 에 진정 으로 응용 하면 프로그램 을 지리멸렬 하 게 만 들 고 눈 에 띄 지 않 을 것 이다.어떻게 하면 더 편리 하 게 다 중 스 레 드 를 진행 할 수 있 는 지 에 대해 나 도 머리 를 썼 다.먼저 다 중 스 레 드 호출 을 어떻게 하 는 것 이 가장 편리 한 지 생각해 보 세 요.
1.twisted 로 비동기 I/O 캡 처
사실 더 효율 적 인 캡 처 는 반드시 다 중 스 레 드 를 사용 해 야 하 는 것 이 아니 라 비동기 I/O 법 을 사용 할 수 있 습 니 다.twisted 의 getPage 방법 을 직접 사용 한 다음 에 비동기 I/O 가 끝 날 때의 callback 과 errback 방법 을 각각 추가 하면 됩 니 다.예 를 들 어 이렇게 할 수 있다.
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()
tisted 사람 은 이름 그대로 코드 가 너무 왜곡 되 어 비정 상 사람들 이 받 아들 일 수 있 습 니 다.비록 이 간단 한 예 는 괜찮아 보이 지만.매번 tisted 를 쓰 는 프로그램 은 모두 가 왜곡 되 어 매우 피곤 하 다.문 서 는 없 는 것 과 같다.반드시 소스 코드 를 봐 야 어떻게 하 는 지 알 수 있다.아,말 도 마 세 요.
gzip/deflate 를 지원 하고 로그 인 확장 까지 하려 면 twisted 에 새로운 HTTP Client Factory 류 를 써 야 합 니 다.저 는 이 눈썹 이 정말 구 겨 져 서 포기 합 니 다.끈기 가 있 는 자 는 스스로 시도 해 보 세 요.
트 위 스 티 드 로 대량 사이트 처 리 를 어떻게 하 는 지 에 대한 이 글 은 좋 습 니 다.얕 은 것 에서 깊 은 것 으로,깊 은 것 에서 얕 은 것 으로,한 번 볼 수 있 습 니 다.
2.간단 한 다 중 스 레 드 캡 처 클래스 를 설계 합 니 다.
아니면 urllib 같은 python'본토'의 동쪽 에서 괴 롭 히 는 것 이 더 편 하 다 고 생각 합 니까?생각해 보 세 요.만약 Fetcher 류 가 있다 면,당신 은 이렇게 호출 할 수 있 습 니 다.
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 는'실행 중인 작업'이나'대기 열 에 있 는 작업'이 있 으 면'하기 도 쉽 습 니 다.그래서 코드 는 다음 과 같 습 니 다.
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)
3.사소한 경험
1.연결 탱크:
opener.open 은 urllib 2.urlopen 과 마찬가지 로 http 요청 을 새로 만 듭 니 다.일반적인 상황 에서 이것 은 문제 가 되 지 않 습 니 다.선형 환경 에서 1 초 만 에 요청 이 새로 생 성 될 수 있 기 때 문 입 니 다.그러나 다 중 스 레 드 환경 에서 매 초 에 몇 십 수백 개의 요청 이 있 을 수 있 습 니 다.이렇게 하면 몇 분 이면 정상 적 인 이성 적 인 서버 가 반드시 차단 할 것 입 니 다.
그러나 정상 적 인 html 요청 시 서버 와 수 십 개의 연결 을 동시에 유지 하 는 것 은 정상 적 인 일이 기 때문에 HttpConnection 의 풀 을 수 동 으로 유지 한 다음 캡 처 할 때마다 연결 풀 에서 연결 을 선택 하여 연결 하면 됩 니 다.
여기 서 교묘 한 방법 이 있 습 니 다.바로 squid 를 프 록 시 서버 로 캡 처 하면 squid 는 자동 으로 연결 풀 을 유지 하고 데이터 캐 시 기능 도 추가 합 니 다.그리고 squid 는 원래 제 모든 서버 에 필요 한 것 입 니 다.더 이상 귀 찮 게 연결 풀 을 쓸 필요 가 있 습 니까?
2.스 레 드 의 스 택 크기 설정
스 택 크기 의 설정 은 python 의 메모리 점용 에 현저 한 영향 을 줄 것 입 니 다.python 다 중 스 레 드 가 이 값 을 설정 하지 않 으 면 프로그램 이 대량의 메모 리 를 점용 할 수 있 습 니 다.이것 은 openvz 의 vps 에 매우 치 명 적 입 니 다.stack_size 는 32768 이상 이 어야 하 며,실제로는 32768*2 이상 이 어야 합 니 다.
from threading import stack_size
stack_size(32768*16)
3、설정 실패 후 자동 재 시도
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.설정 시간 초과
import socket
socket.setdefaulttimeout(10) # 10
5.로그 인
로그 인 이 더욱 간소화 되 었 습 니 다.우선 buildopener 에 쿠키 지원 을 추가 하려 면'총화'라 는 글 을 참고 하 십시오.Very CD 에 로그 인 하려 면 Fetcher 에 빈 방법 login 을 추가 하고init__()중간 호출,그리고 Fetcher 클래스 를 계승 하고 override login 방법:
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 가 초기 화 될 때 자동 으로 Very CD 사이트 에 접속 합 니 다.
총화
이렇게 하면 상기 모든 작은 기 교 를 융합 시 키 면 현재 제 가 숨 겨 놓 은 최종 판 의 Fetcher 류 와 차이 가 멀 지 않 습 니 다.다 중 스 레 드,gzip/deflate 압축,시간 초과 설정,자동 재 시도,스 택 크기 설정,자동 로그 인 등 기능 을 지원 합 니 다.코드 가 간단 하고 사용 하기에 편리 하 며 성능 도 뛰 어 나 집에 서 여행 하고 사람 을 죽 이 고 불 을 지 르 며 기침 을 하 는 데 필수 적 인 도구 라 고 할 수 있 습 니 다.
최종 판 과 크게 다 르 지 않 은 이 유 는 최종 판 에 보존 기능 인'조끼 술'이 있 기 때문이다.다 중 에이전트 가 자동 으로 선택 하기 때문이다.보기 에는 random.choice 의 차이 일 뿐 인 것 같 지만 사실은 대리 획득,대리 검증,대리 속도 측정 등 여러 부분 을 포함 하고 있 습 니 다.이것 이 바로 다른 이야기 입 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JAVA 다 중 스 레 드 메커니즘 의 스 레 드 생 성target 을 실행 대상 으로 지정 한 name 을 이름 으로 하고 group 에서 참조 하 는 스 레 드 그룹의 일원 으로 새 Thread 대상 을 할당 합 니 다. 이 스 레 드 가 독립 된 Runnable 실...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.