第6章Webクライアントアクセス


Webページの取得
#!/usr/bin/env python

import sys,urllib2

req=urllib2.Request(sys.argv[1])
fd=urllib2.urlopen(req)
while True:
    data=fd.read(1024)
    if not len(data):
        break
    sys.stdout.write(data)

まずurllib 2を構築した.URLでパラメータを作成するRequestオブジェクト.urlopenを呼び出してファイルクラスオブジェクトを取得します.もちろんurlopenはurlを直接パラメータとして使用することもできます.このほかgeturl()関数もあり、ソースのURLを取得するために使用されます.通常、リダイレクトされたページに追跡できます.info()関数は、ページのmeta-informationを取得するために使用されます.
資格認定
HTTP認証が必要なサイトもあります.HTTP認証は一般的に、クッキーとformベースの認証とは異なるユーザー名とパスワードを尋ねるポップアップウィンドウを表示します.認証が必要なURLにアクセスしようとすると、通常HTTPエラー401が得られますが、urllibは認証を処理することができます.例:
#!/usr/bin/env python

import sys,urllib2,getpass

class TerminalPassword(urllib2.HTTPPasswordMgr):
    def find_user_password(self,realm,authuri):
        retval=urllib2.HTTPPasswordMgr.find_user_password(self,realm,authuri)

        if retval[0]==None and retval[1]==None:
            sys.stdout.write("Login required for %s
" % (realm,authuri))             sys.stdout.write('Username: ')             username=sys.stdin.readline().strip()             password=getpass.getpass().rstrip()             return (username,password)         else:             return retval req=urllib2.Request(sys.argv[1]) opener=urllib2.build_opener(urllib2.HTTPBasicAuthHandler(TerminalPassword())) fd=opener.open(req) print 'Retrieved',fd.geturl() info=fd.info() for key,value in info.items():     print '%s = %s' % (key,value)

このプログラムにはTerminalPasswordクラスが定義されており、必要に応じてオペレータにユーザー名とパスワードを尋ねることができ、もう1つの呼び出しはbuild_opener().この関数を使用すると、追加のハンドラを指定できます.通常、デフォルトでは基本的なHTTPやFTPサポートなどのハンドラがありますが、他のハンドラも選択的に追加できます.このコードは基本認証をサポートするため、HTTPBasicAuthHandlerをプロセッサチェーンに追加する必要があります.前の例では、コードはurllib 2を簡単に呼び出す.urlopen()は、build_を内部で呼び出します.Opener()で、パラメータは一切付いていません.これにより、デフォルトのプロセッサのみが選択されます.接続が開かれると、変更はありません.認証が必要な場合、HTTPBasicAuthHandlerはTerminalPasswordの適切な関数を自動的に呼び出し、さらにチェックする必要はありません.認証を必要としない一般的なサイトにアクセスすると、このコードは前と同じように表現されます.
フォームデータの送信
CGIスクリプトやその他のインタラクティブなサーバ側プログラムは、一般的にフォームからデータをWebクライアントから受け取ることが多い.フォームデータをコミットするには、GETとPOSTの2つの方法があります.どの方法を使うかは、HTMLドキュメントの
タグの中の方法パラメータによって異なります.
1、GET方法で提出する
フォームをコミットするGETメソッドは、フォームデータをURLにエンコードすることです.要求されたページを与えた後、挨拶を加えて、フォームの要素です.各キーと値のペアは「&」で分割されます.一部の文字は避ける必要があります.たとえば、スペースは「+」で置き換える必要があります.URLにはすべてのデータが含まれているため、GETメソッドはデータ量が大きい場合にはあまり適していません.例:
#!/usr/bin/env python

import sys,urllib2,urllib

def addGETdata(url,data):
    """Adds data to url.Data should be a list or tuple consisting of 2-item lists or tuples of the form:(key,value).
        Items that have no key should have key set to None.
        A given key may occur more than once.
        """
    return url+'?'+urllib.urlencode(data)

zipcode=sys.argv[1]
url=addGETdata('http://www.wunderground.com/cgi-bin/findweather/getForecast',[('query',zipcode)])
print 'Using URL',url
req=urllib2.Request(url)
fd=urllib2.urlopen(req)
while True:
    data=fd.read(1024)
    if not len(data):
        break
    sys.stdout.write(data)

2、POST方法で提出する
符号化されたデータは、要求された個別の部分で送信される.大量のデータを交換する必要がある場合、POSTは良い方法です.例:
#!/usr/bin/env python

import sys,urllib2,urllib

zipcode=sys.argv[1]
url='http://www.wunderground.com/cgi-bin/findweather/getForecast'
data=urllib.urlencode([('query',zipcode)])
req=urllib2.Request(url)
fd=urllib2.urlopen(req,data)
while True:
    data=fd.read(1024)
    if not len(data):
        break
    sys.stdout.write(data)
#                urlopen()

処理エラー
リモートWebサーバと接続を確立すると、提供されたURLが間違っていること、提供されたURLが間違っていること、URLはサポートされていないプロトコルを使用しているかもしれません.ホスト名が間違っているかもしれません.またはサーバにアクセスできません.または、サーバがリクエストに対してエラーを返します.の接続中に発生する異常はurllib 2であるかのいずれかである.URLErrorのインスタンスは、そのサブクラスであるかのいずれかです.したがって,このスーパークラス(URLErrorを指す)をキャプチャすることで異常をキャプチャすることができる.しかし、HTTPのエラーメッセージには、実際に何が起こったのかを説明するドキュメントも含まれています.スーパークラスを直接使用すると、このドキュメントは表示されません.そのため、urllib 2にはHTTPErrorの異常クラス(URLErrorのサブクラス)があります.HTTPError自体はファイルクラスオブジェクトであり、読み取りに使用できます.HTTPErrorではないエラーもありますが、URLErrorを処理する必要があります.例:
#!/usr/bin/env python

import sys,urllib2

req=urllib2.Request(sys.argv[1])

try:
    fd=urllib2.urlopen(req)
except urllib2.HTTPError,e:
    print 'Error retrieving data:',e
    print 'Server error document follows:
'     print e.read()     sys.exit(1) except urllib2.URLError,e:     print 'Error retrieving data:',e     sys.exit(2) print 'Retrieved',fd.geturl() info=fd.info() for key,value in info.items():     print '%s = %s' % (key,value)

データの読み出しには2つの異なる問題が発生する.1つは通信エラーであり、socketモジュールがread()関数を呼び出すときにsocketを生成する.error;二つ目は、通信エラーがない場合に送信されるドキュメントが削除されることである.1つ目の場合はsocketエラーを処理する方法で処理することができる.2つ目の場合は難しいです.
クライアントにとって、削除されたドキュメントが異常なく受信されることは完全に可能です.たとえば、プログラムがドキュメントを送信すると、サーバに問題が発生し、サーバの問題によりリモートsocketが正常に閉じられるため、ユーザーのクライアントは異常なくファイル終了フラグを簡単に受信します.
この質問をチェックする方法は、サーバの回答でコンテンツ長のヘッダを見つけることであり、受信したデータ長とヘッダに提供された長さとを比較してチェックすることができる.ただし、コンテンツ長のヘッダは必ずしも提供されるものではなく、特にCGI生成ページにはこのヘッダは含まれていない.この場合、ファイルが削除されているかどうかをチェックすることはできません.例:
#!/usr/bin/env python

import sys,urllib2,socket

req=urllib2.Request(sys.argv[1])

try:
    fd=urllib2.urlopen(req)
except urllib2.HTTPError,e:
    print 'Error retrieving data:',e
    print 'Server error document follows:
'     print e.read()     sys.exit(1) except urllib2.URLError,e:     print 'Error retrieving data:',e     sys.exit(2) print 'Retrieved',fd.geturl() bytesread=0 while True:     try:         data=fd.read(1024)     except socket.error,e:         print 'Error reading data:',e         sys.exit(3)     if not len(data):         break     bytesread+=len(data)     sys.stdout.write(data) if fd.info().has_key('Content-Length') and long(fd.info()['Content-Length'])!=long(bytesread):     print 'Expected a document of size %d , but read %d bytes' % (long(fd.info()['Content-Length']),bytesread)     sys.exit(4)