python3-urllib_Handler_1

24489 단어 Python모듈 학습
본 고 는 python 3. x 의 urllib 라 이브 러 리 (공식 표준 라 이브 러 리) 를 바탕 으로 사용자 정의 Handler 프로 세 서 를 실현 하 는 방법 을 다 루 고 있 으 며, 코드 에서 이 루어 진 효 과 는 다른 간단 한 방식 으로 이 루어 질 수 있 으 며, 여 기 는 예 일 뿐이다.
    :Python 3.7
    :http://httpbin.org

전술한
보통 링크 를 요청 하 는 python 코드 작성 방법 은:
import requests
url = "http://httpbin.org/get"
res = requests.get(url)
print(res.content)

실행 후 다음 과 같은 결 과 를 얻 을 수 있 습 니 다.
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  },
  "origin": "x.x.x.x, x.x.x.x",
  "url": "https://httpbin.org/get"
}

그러나 우 리 는 제3자 요청 라 이브 러 리 requests 을 사용 할 생각 이 없 기 때문에 urllib 라 이브 러 리 의 실현 버 전 은 다음 과 같 습 니 다. 얻 은 결 과 는 기본 적 인 User-Agent 필드 가 urllib 으로 바 뀌 었 습 니 다.
import urllib.request as urlreq
url = "http://httpbin.org/get"
res = urlreq.urlopen(url)
print(res.read().decode('utf-8'))

현재 모든 OK. 만약 에 우리 가 대리 IP 를 사용 하여 요 구 를 하려 고 한다 면 urllib 의 기본 적 인 요구 방식 은 만족 할 수 없 기 때문에 openerHandler 의 용법 을 도입 했다.
import urllib.request as urlreq

url = "http://httpbin.org/get"
proxy = {'http':'112.87.68.202:9999'}#              IP
proxyhandler = urlreq.ProxyHandler(proxies=proxy)
opener = urlreq.build_opener(proxyhandler)
res = opener.open(url)
print(res.read().decode('utf-8'))

orgin 필드 가 우리 가 사용 하 는 프 록 시 IP 의 주소 가 되 었 음 을 발견 할 수 있 습 니 다.
  "origin": "112.87.68.202, 112.87.68.202",
urllib 라 이브 러 리 자체 가 Handler 를 실현 했다. 예 를 들 어 위 에서 사용 한 ProxyHandler 이다. 그러나 기능 적 인 수 요 는 사람 에 따라 다르다. 예 를 들 어 나 는 특정한 링크 를 요청 할 때 이 과정 에서 세 번 만 방향 을 바 꾸 고 이 세 번 의 방향 을 바 꾸 는 것 은 매번 user - agent 가 다르다.그러면 모듈 이 제공 한 handler 는 이러한 기능 을 만족 시 킬 수 없습니다. 사용자 정의 Handler 가 필요 합 니 다.
본문
part-1
앞의 요청 예 에서 user - agent 필드 는 모듈 의 기본 필드 로 브 라 우 저 에 있 는 user - agent 처럼 보이 지 않 습 니 다. 간단 한 python 코드 는:
import requests
url = '...'
headers = {"User-Agent":"......"}
res = requests.get(url,headers=headers)

혹은
import urllib.request as urlreq
url = '...'
headers = {"User-Agent":'.....'}
req = urlreq.Request(url,headers=headers)
res = urlreq.urlopen(req)

사용자 정의 Handler 버 전이 라면:
from fake_useragent import UserAgent
import urllib.request as request

ua = UserAgent()
url = 'http://httpbin.org/get'

class HeaderHandler(request.BaseHandler):

    def __init__(self,headers=None):
        if headers is None:
            headers = ua.random
        self.headers = headers
        
    def http_request(self,req):
        req.headers['User-Agent'] = self.headers
        return req

    https_request = http_request

#   handler  *****************
proxyhandler = request.ProxyHandler(proxies={'http':'163.204.242.73:9999'})
headerhandler = HeaderHandler()
handlers = [proxyhandler,headerhandler]
opener = request.build_opener(*handlers)
req_2  = request.Request(url)
res_2 = opener.open(req_2)
print(res_2.read().decode('utf-8'))

이 예 는 분명히 앞의 두 가지 실현 방식 보다 복잡 하고 큰 인재 가 적 게 사용 되 지만 더욱 복잡 한 기능 수요 라면 그의 맞 춤 형 화 는 그 강 함 을 보 여 준다.
  • 코드 는 제3자 모듈 fake_useragent 을 도 입 했 는데 이 모듈 은 주로 user-agent 필드 를 제공 하 는 데 사용 되 고 독 자 는 user-agent 필드 를 직접 복사 할 수 있다.
  • 코드 는 Handler, proxyhandlerheaderhandler 을 중첩 하여 사용 하 였 으 며, 하 나 는 IP 프 록 시 기능 을 완성 하 였 으 며, 하 나 는 요청 헤드 필드 의 수정 을 완성 하 였 다.
  • 사용자 정의 Handler 클래스 는 Handler 기본 클래스 BaseHandler 를 계승 합 니 다. 이것 은 모든 Handler 의 기본 클래스 입 니 다. 모듈 이 제공 한 Handler (예 를 들 어 HTTPRedirectHandler) 를 바탕 으로 이 루어 지 려 면
  • 에서 계승 할 수 있 습 니 다.
  • 은 BaseHandler 의 자 류 를 계승 하여 하나의 요청 처리 시기 에 따라 그 방법 을 실현 하고 나중에 설명 할 수 있다.

  • BaseHandler
    BaseHandler 가 제공 하 는 몇 가지 함수 의 기능 은 Scrapy 파충류 프레임 의 중간 부품 이 이 를 바탕 으로 이 루어 졌 는 지 를 연상 할 수 밖 에 없다.하위 클래스 화 를 고려 하기 전에 Opener, 즉 Opener Director 대상 이 url 또는 Request 대상 을 처리 할 때 겪 는 세 가지 시 기 를 알 아야 합 니 다.
  • Request 예 처리 시기.이 시 기 는 주로 Request 대상 에 대해 urlopen 또는 open 작업 을 하기 전에 재 가공 합 니 다. 이 시기 에 opener 는 우리 의 handler 순서 (우 리 는 여러 개의 Handler 가 있 습 니 다. 예 를 들 어 에 두 개 있 습 니 다) 에 따라 그들의 protocol 을 순서대로 호출 합 니 다.request () 방법, 여기 protocol 은 http 또는 https 를 말 합 니 다. 예 를 들 어 재 작성 함 수 는 http 입 니 다.request()。이 방법 은 Request 대상 을 받 아들 이 고 Request 대상 을 되 돌려 줍 니 다. 이 대상 은 다음 Handler 의 protocol 에 게 계속 전 달 됩 니 다.request 방법 가공 또는 기타 조작.
  • Request 처리 시기.이 시 기 는 opener 가 진정 으로 네트워크 에 요청 하 는 시기 입 니 다. 이 시기 에 영향 을 주 려 면 BaseHandler 의 계승 류 에 대한 실현 이 필요 합 니 다 *open 방법.open 방법 은 세 가지 방법 (default open (), protocol open (), unknown open () 으로 구성 되 지만 모든 BaseHandler 하위 클래스 에 대해 그들 을 실현 하거나 그 중 하 나 를 실현 하 라 고 요구 하지 않 습 니 다.미 완성 계속
  • Response 처리 시기.미 완성 계속
  • part-2
    다음 코드 예 는 리 셋 횟수 를 제한 하 는 기능 을 실 현 했 고 테스트 사 이 트 는 requests 라 이브 러 리 작성 자의 또 다른 오픈 소스 항목 을 사용 합 니 다.http://httpbin.org사이트 에서 테스트 방향 을 바 꾸 는 기능 을 제공 합 니 다. 여기 서 의 용법 은?
    http://httpbin.org/absolute-redirect/{n}
      n,                  ,     3 , 
    http://httpbin.org/absolute-redirect/3
    

    python3-urllib_Handler_1_第1张图片 여기 서 저 는 첫 번 째 예 처럼 기본 클래스 인 BaseHandler 를 계승 하지 않 고 하위 클래스 인 HTTP Redirect Handler 를 계승 하여 그 중의 소스 코드 를 직접 복사 하여 수정 합 니 다.그럼 왜 이렇게 고 쳐 야 되 는 지 알 았 죠?
    import urllib.request as urlreq
    from urllib.error import HTTPError
    from urllib.parse import urlencode
    
    class RedirectTimesOutError(Exception):
        def __init__(self,expression,message):
            self.expression = expression
            self.message = message
    
    class RedirectError(Exception):
        def __init__(self,expression,message):
            self.expression = expression
            self.message = message
    
    class MyHTTPRedirectHandler(urlreq.HTTPRedirectHandler):
    
        def __init__(self,timeslimit=3):
            self.limit = timeslimit
            self.flag = 1
    
        def redirect_request(self,req,fp,code,msg,headers,newurl):
            m = req.get_method()
            if (code != 302) and (m != 'GET'):
                raise RedirectError('','Must 302 redirect from GET Method')
                # raise HTTPError(req.full_url,code,msg,headers,fp)
            
            print('newurl:',newurl)
            print('-'*8)
            newurl = newurl.replace(' ','%20')
            CONTENT_HEADERS = ("content-length","content-type")
            newheaders = {k:v for k,v in req.headers.items()
                            if k.lower() not in CONTENT_HEADERS}
     
            if self.flag > self.limit:
            	self.flag = 1
                raise RedirectTimesOutError(expression='32',message='Redirect times out!')
            else:
                self.flag +=1
    
            return urlreq.Request(newurl,
                           headers=newheaders,
                           origin_req_host=req.origin_req_host,
                           unverifiable=True)
    
        # def http_error_302(self,req,fp,code,msg,hdrs):
        #     print('call here')
        #     pass
    
    url = 'http://httpbin.org/absolute-redirect/4' #GET  
    data = None
    
    myhandler = MyHTTPRedirectHandler(4)
    opener = urlreq.build_opener(myhandler)
    try:
        res_2 = opener.open(url,data=data)
    except RedirectTimesOutError as e:
        print(e.message)
    else:
        print(res_2.getcode())
        # print(res_2.read().decode('utf-8'))
    

    코드 는 원본 url lib 모듈 의 request. py 파일 의 HTTP Redirect Handler 류 의 redirect 를 복사 합 니 다.request 함수, 그리고 부분 수정:self.limit 값 을 설정 함으로써 한 링크 의 방문 중간 에 몇 번 의 리 셋 이 허용 되 는 지 제한 하고 self.flag 기록 을 이용 하여 몇 번 째 리 셋 을 처리 하 며 마지막 으로 이상 을 던 지기 전에 self.flag 값 을 리 셋 해 야 한다. 그렇지 않 으 면 이 Handler 가 새로운 요청 의 리 셋 을 계속 처리 할 때 이전 요청 에 방 해 를 받는다.
    공식 API 에서 이 Handler 에 대한 설명 이 있 습 니 다.
    HTTPRedirectHandler.redirect_request(req, fp, code, msg, hdrs, newurl)
    Return a Request or None in response to a redirect. This is called by the default implementations of the http_error_30*() methods when a redirection is received from the server. If a redirection should take place, return a new Request to allow http_error_30*() to perform the redirect to newurl. Otherwise, raise HTTPError if no other handler should try to handle this URL, or return None if you can’t but another handler might.
    이 말 에는 주로 이런 두 가지 정보 가 있다.
  • redirect_request 함 수 는 재 설정 을 처리 하 는 데 사 용 됩 니 다. 이 Handler 가 이 재 설정 을 처리 할 수 있다 면 Request 대상 으로 돌아 갑 니 다.만약 이 Handler 가 할 수 없다 면, None 로 돌아 가 처리 권 을 다음 Handler 로 넘 깁 니 다.
  • Handler 는 리 셋 요청 을 발견 할 때 http_error_30* 함 수 를 먼저 촉발 합 니 다. 이 함 수 는 redirect_request 함 수 를 촉발 하여 처리 합 니 다.
  • 그리고 독자 들 은 다음 과 같은 두 가지 의문 이 있 을 수 있다.
  • 기본 클래스 자체 가 http_error_30* 을 실 현 했 기 때문에 우리 위의 예 는 이 방법 을 실현 하지 못 했 지만 왜 촉발 되 었 고 시스템 의 기본 처리 방안 보다 우선 시 되 었 습 니까?
  • 여러 처리 재 정립 핸들 러 가 http_error_30* 을 달성 했다 면 집행 순 서 는 어떻게 될 까?

  • 이 문제 들 은 다음 Handler 의 주제 에서 해석 되 는데, 여기에 소스 코드 를 결합 하여 그 행 위 를 해석 해 야 한다.

    좋은 웹페이지 즐겨찾기