Changeset 36521
- Timestamp:
- 01/24/10 21:01:45 (3 years ago)
- Location:
- lang/python/twopy/trunk/twopy
- Files:
-
- 6 modified
Legend:
- Unmodified
- Added
- Removed
-
lang/python/twopy/trunk/twopy/board.py
r34400 r36521 6 6 7 7 class Board (object): 8 """9 2chの板全般を管理するクラスです。10 """11 12 __tr_re = re.compile(r"(?P<title>.*) \((?P<res>\d*)\)")13 __server_re = re.compile(r"http://.+?/")14 __name_re = re.compile(r"http://.+?/(.+?)/")15 16 def __init__(self, url, user=None):17 """18 オブジェクトのコンストラクタです。19 20 url : 対象の2ch板のURL21 user : 通信に用いるtwopy.Userクラスのインスタンス22 """23 u = url.endswith("/") and url or url + "/"24 self.__url = u25 self.__user = user or twopy.User.anonymouse()26 self.__isRetrieved = False27 28 self.__index = 029 self.__threads = []30 31 def getUrl(self): return self.__url32 def setUrl(self, url):33 if type(url) == str:34 self.__url = url35 else: raise AttributeError36 url = property(getUrl, setUrl)37 38 def getConfig(self): return self.__conf39 def setConfig(self, conf): self__conf = conf40 config = property(getConfig, setConfig)41 42 def getSubject(self):43 su = self.url.endswith("/") and \44 self.url + "subject.txt" or self.url + "/subject.txt"45 return su46 subject_url = property(getSubject)47 48 def getBBS(self):49 b = self.url.endswith("/") and \50 self.url + "test/bbs.cgi" or self.url + "/test/bbs.cgi"51 52 def get_isRetrieved(self): return self.__isRetrieved53 isRetrieved = property(get_isRetrieved)54 55 def getUser(self): return self.__user56 user = property(getUser)57 58 def getServer(self):59 return Board.__server_re.search(self.url).group(0)60 server = property(getServer)61 62 def getName(self):63 return Board.__name_re.search(self.url).group(1)64 name = property(getName)65 66 def __init_threads(self):67 self.__threads = []68 self.__isRetrieved = False69 70 def retrieve(self):71 """72 対象の板からスレッド一覧を取得します。73 """74 self.__init_threads()75 76 try:77 response = self.user.urlopen(self.subject_url, gzip=False)78 except urllib2.HTTPError, e:79 return e.code80 if response.code == 200:81 rawdata = unicode(response.read(), 'Shift_JIS', 'ignore')82 dat = rawdata.split("\n")83 for thread_str in dat:84 columns = thread_str.split("<>")85 if len(columns) < 2 : continue86 r = Board.__tr_re.search(columns[1])87 title = r.group("title")88 res = int(r.group("res"))89 th = twopy.Thread(self, columns[0], self.user, title, res)90 self.__threads.append(th)91 self.__isRetrieved = True92 93 return response.code94 95 def createNewThread(self, subject=u"", name=u"", mailaddr=u"", message=u"",96 submit=u"新規スレッド作成", hidden={}, delay=5):97 """98 新しくスレッドを生成します.99 100 引数:101 subject : スレッドのタイトル102 name : 名前103 mailaddr : メールアドレス104 message : 本文の文章105 submit : 書き込みボタンのキャプション106 hidden : hidden属性の値107 delay : 本来の時間から何秒だけ後戻りさせるか(未来の時間に書き込むのを防ぐ)108 109 返り値:110 レスポンスコードと受信した文章の本文のタプル.111 レスポンスコード:112 twopy.STATUS_TRUE 書き込み成功113 twopy.STATUS_FALSE 書き込み成功&警告114 twopy.STATUS_ERROR 書き込み失敗115 twopy.STATUS_CHECK 書き込み警告116 twopy.STATUS_COOKIE 書き込み確認117 118 ただし、レスポンスコードがtwopy.STATUS_COOKIEの場合、119 返り値はレスポンスコード、受信した文章の本文、input->hidden属性の辞書が返されます.120 """121 referer = self.url122 send_dict = { "bbs" : self.name,123 "subject" : subject.encode("Shift_JIS"),124 "time" : int(time.time())-delay,125 "FROM" : name.encode("Shift_JIS"),126 "mail" : mailaddr.encode("Shift_JIS"),127 "MESSAGE" : message.encode("Shift_JIS"),128 "submit" : submit.encode("Shift_JIS")}129 send_dict.update(hidden)130 params = urllib.urlencode(send_dict)131 132 return utility.bbsPost(self.user, self, params, referer)133 134 def __len__(self):135 if not self.isRetrieved: raise twopy.NotRetrievedError136 return len(self.__threads)137 138 def __iter__(self):139 if not self.isRetrieved: raise twopy.NotRetrievedError140 for thread in self.__threads:141 yield thread142 143 def __getitem__(self, i):144 if not self.isRetrieved: raise twopy.NotRetrievedError145 return self.__threads[i]8 """ 9 2chの板全般を管理するクラスです。 10 """ 11 12 __tr_re = re.compile(r"(?P<title>.*) \((?P<res>\d*)\)") 13 __server_re = re.compile(r"http://.+?/") 14 __name_re = re.compile(r"http://.+?/(.+?)/") 15 16 def __init__(self, url, user=None): 17 """ 18 オブジェクトのコンストラクタです。 19 20 url : 対象の2ch板のURL 21 user : 通信に用いるtwopy.Userクラスのインスタンス 22 """ 23 u = url.endswith("/") and url or url + "/" 24 self.__url = u 25 self.__user = user or twopy.User.anonymouse() 26 self.__isRetrieved = False 27 28 self.__index = 0 29 self.__threads = [] 30 31 def getUrl(self): return self.__url 32 def setUrl(self, url): 33 if type(url) == str: 34 self.__url = url 35 else: raise AttributeError 36 url = property(getUrl, setUrl) 37 38 def getConfig(self): return self.__conf 39 def setConfig(self, conf): self__conf = conf 40 config = property(getConfig, setConfig) 41 42 def getSubject(self): 43 su = self.url.endswith("/") and \ 44 self.url + "subject.txt" or self.url + "/subject.txt" 45 return su 46 subject_url = property(getSubject) 47 48 def getBBS(self): 49 b = self.url.endswith("/") and \ 50 self.url + "test/bbs.cgi" or self.url + "/test/bbs.cgi" 51 52 def get_isRetrieved(self): return self.__isRetrieved 53 isRetrieved = property(get_isRetrieved) 54 55 def getUser(self): return self.__user 56 user = property(getUser) 57 58 def getServer(self): 59 return Board.__server_re.search(self.url).group(0) 60 server = property(getServer) 61 62 def getName(self): 63 return Board.__name_re.search(self.url).group(1) 64 name = property(getName) 65 66 def __init_threads(self): 67 self.__threads = [] 68 self.__isRetrieved = False 69 70 def retrieve(self): 71 """ 72 対象の板からスレッド一覧を取得します。 73 """ 74 self.__init_threads() 75 76 try: 77 response = self.user.urlopen(self.subject_url, gzip=False) 78 except urllib2.HTTPError, e: 79 return e.code 80 if response.code == 200: 81 rawdata = unicode(response.read(), 'Shift_JIS', 'ignore') 82 dat = rawdata.split("\n") 83 for thread_str in dat: 84 columns = thread_str.split("<>") 85 if len(columns) < 2 : continue 86 r = Board.__tr_re.search(columns[1]) 87 title = r.group("title") 88 res = int(r.group("res")) 89 th = twopy.Thread(self, columns[0], self.user, title, res) 90 self.__threads.append(th) 91 self.__isRetrieved = True 92 93 return response.code 94 95 def createNewThread(self, subject=u"", name=u"", mailaddr=u"", message=u"", 96 submit=u"新規スレッド作成", hidden={}, delay=5): 97 """ 98 新しくスレッドを生成します. 99 100 引数: 101 subject : スレッドのタイトル 102 name : 名前 103 mailaddr : メールアドレス 104 message : 本文の文章 105 submit : 書き込みボタンのキャプション 106 hidden : hidden属性の値 107 delay : 本来の時間から何秒だけ後戻りさせるか(未来の時間に書き込むのを防ぐ) 108 109 返り値: 110 レスポンスコードと受信した文章の本文のタプル. 111 レスポンスコード: 112 twopy.STATUS_TRUE 書き込み成功 113 twopy.STATUS_FALSE 書き込み成功&警告 114 twopy.STATUS_ERROR 書き込み失敗 115 twopy.STATUS_CHECK 書き込み警告 116 twopy.STATUS_COOKIE 書き込み確認 117 118 ただし、レスポンスコードがtwopy.STATUS_COOKIEの場合、 119 返り値はレスポンスコード、受信した文章の本文、input->hidden属性の辞書が返されます. 120 """ 121 referer = self.url 122 send_dict = { "bbs" : self.name, 123 "subject" : subject.encode("Shift_JIS"), 124 "time" : int(time.time())-delay, 125 "FROM" : name.encode("Shift_JIS"), 126 "mail" : mailaddr.encode("Shift_JIS"), 127 "MESSAGE" : message.encode("Shift_JIS"), 128 "submit" : submit.encode("Shift_JIS")} 129 send_dict.update(hidden) 130 params = urllib.urlencode(send_dict) 131 132 return utility.bbsPost(self.user, self, params, referer) 133 134 def __len__(self): 135 if not self.isRetrieved: raise twopy.NotRetrievedError 136 return len(self.__threads) 137 138 def __iter__(self): 139 if not self.isRetrieved: raise twopy.NotRetrievedError 140 for thread in self.__threads: 141 yield thread 142 143 def __getitem__(self, i): 144 if not self.isRetrieved: raise twopy.NotRetrievedError 145 return self.__threads[i] -
lang/python/twopy/trunk/twopy/comment.py
r34425 r36521 7 7 8 8 class Comment (object): 9 """10 スレッド上のコメントを管理するクラスです.11 """12 13 __delete_tag = re.compile(r"<.+?>")14 __date_and_id = re.compile(r"(?P<date>.*) ID:(?P<id>\S*)")15 __be = re.compile(r"BE:(?P<be>.*)")16 __datetime = re.compile(r"(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})\(.*\) (?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})(\.(?P<csec>\d+)|)")17 __urls = re.compile(r"(ttps?:\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+)")18 __response = re.compile(r"(>>\d{1,4}|>>[0-9]{1,4})(-\d{1,4}|−[0-9]{1,4}|)")19 20 def __init__(self, thread, line, number):21 """22 オブジェクトのコンストラクタです.23 24 thread : コメントが保存されている親スレッドのインスタンス25 line : 初期化に用いられる、datファイルの一行.26 この引数はunicode型でなければなりません.27 number : スレッドからの、コメント位置.28 """29 self.thread = thread30 self.number = number31 self.line = line32 33 self.__datetime_cache = None34 self.__urls_cache = None35 if type(line) == unicode:36 columns = line.split("<>")37 elif type(line) == str:38 columns = unicode(line, "Shift-JIS", "replace").split("<>")39 else: raise TypeError("the type of the argument 'line' is not unicode or str.")40 if len(columns) < 5 : raise TypeError41 self.__name = Comment.__delete_tag.sub("", columns[0])42 self.__mailaddr = columns[1]43 raw_body = Comment.__delete_tag.sub("", columns[3].replace(" <br> ", "\n"))44 self.__body = xml.sax.saxutils.unescape(raw_body)[1:-1] # 余分なスペースの削除45 di_result = Comment.__date_and_id.search(columns[2])46 if di_result:47 self.__date = di_result.group("date") or ""48 self.__id = di_result.group("id") or ""49 be_result = Comment.__be.search(columns[2])50 self.__be = None51 if be_result:self.__be = be_result.group("be")52 53 self.__responses_cache = None54 55 def getName(self): return self.__name56 name = property(getName)57 58 def getMailAddr(self): return self.__mailaddr59 mailaddr = property(getMailAddr)60 61 def getDate(self): return self.__date62 date = property(getDate)63 64 def getBody(self): return self.__body65 body = property(getBody)66 67 def getDatetime(self):68 if self.__datetime_cache: return self.__datetime_cache69 70 result = Comment.__datetime.search(self.__date)71 if result:72 year = int(result.group("year"))73 month = int(result.group("month"))74 day = int(result.group("day"))75 hour = int(result.group("hour"))76 m = int(result.group("min"))77 sec = int(result.group("sec"))78 c = result.group("csec")79 csec = c == None and 0 or int(c)80 81 d = datetime.datetime(year, month, day, hour, m, sec, csec*10000)82 self.__datetime_cache = d83 return d84 else: return None85 datetime = property(getDatetime)86 87 def getID(self): return self.__id88 ID = property(getID)89 90 def getBe(self): return self.__be91 be = property(getBe)92 93 def extractUrls(self):94 """95 コメントの内容から、URLの一覧を抽出して返します.96 """97 if self.__urls_cache: return self.__urls_cache98 result = Comment.__urls.finditer(self.body)99 l = ["".join( ("h", r.group(0)) ) for r in result]100 self.__urls_cache = l101 return l102 urls = property(extractUrls)103 104 def extractResponses(self, returnType="str"):105 """106 コメントの内容から、レスポンスの一覧を抽出して返します.107 """108 if self.__responses_cache == None:109 result = Comment.__response.finditer(self.body)110 l = [(r.group(1),r.group(2)) for r in result]111 self.__responses_cache = l112 113 l = self.__responses_cache114 if returnType == "str": return ["".join(i) for i in l]115 elif returnType == "int": return self.__makeIntegerLists(l)116 elif returnType == "comment":117 rl = self.__makeIntegerLists(l)118 rl2 = []119 for i in rl:120 if type(i) == int:121 if 0<i<=self.thread.res: rl2.append(self.thread[i])122 else: rl2.append([self.thread[j] for j in i if 0<j<=self.thread.res])123 return rl2124 125 else: raise TypeError126 127 res = property(extractResponses)128 responses = property(extractResponses)129 130 def __makeIntegerLists(self, l):131 rl = []132 for i in l:133 start = int(unicodedata.normalize("NFKC", i[0][2:]))134 if i[1] == "": rl.append(start)135 else:136 end = int(unicodedata.normalize("NFKC", i[1][1:]))137 rl.append(range(start, end+1))138 return rl139 140 def render(self):141 """142 取得したコメントから、整形された文章を返します.143 """144 if self.be:145 header = u"%i 名前:%s [%s]: %s ID:%s BE:%s" % \146 (self.number, self.name, self.mailaddr, self.date, self.ID, self.be)147 else:148 header = u"%i 名前:%s [%s]: %s ID:%s" % \149 (self.number, self.name, self.mailaddr, self.date, self.ID)150 return u"%s\n%s\n" % (header, self.body)151 152 def __unicode__(self): return self.render()9 """ 10 スレッド上のコメントを管理するクラスです. 11 """ 12 13 __delete_tag = re.compile(r"<.+?>") 14 __date_and_id = re.compile(r"(?P<date>.*) ID:(?P<id>\S*)") 15 __be = re.compile(r"BE:(?P<be>.*)") 16 __datetime = re.compile(r"(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})\(.*\) (?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})(\.(?P<csec>\d+)|)") 17 __urls = re.compile(r"(ttps?:\/\/[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+)") 18 __response = re.compile(r"(>>\d{1,4}|>>[0-9]{1,4})(-\d{1,4}|−[0-9]{1,4}|)") 19 20 def __init__(self, thread, line, number): 21 """ 22 オブジェクトのコンストラクタです. 23 24 thread : コメントが保存されている親スレッドのインスタンス 25 line : 初期化に用いられる、datファイルの一行. 26 この引数はunicode型でなければなりません. 27 number : スレッドからの、コメント位置. 28 """ 29 self.thread = thread 30 self.number = number 31 self.line = line 32 33 self.__datetime_cache = None 34 self.__urls_cache = None 35 if type(line) == unicode: 36 columns = line.split("<>") 37 elif type(line) == str: 38 columns = unicode(line, "Shift-JIS", "replace").split("<>") 39 else: raise TypeError("the type of the argument 'line' is not unicode or str.") 40 if len(columns) < 5 : raise TypeError 41 self.__name = Comment.__delete_tag.sub("", columns[0]) 42 self.__mailaddr = columns[1] 43 raw_body = Comment.__delete_tag.sub("", columns[3].replace(" <br> ", "\n")) 44 self.__body = xml.sax.saxutils.unescape(raw_body)[1:-1] # 余分なスペースの削除 45 di_result = Comment.__date_and_id.search(columns[2]) 46 if di_result: 47 self.__date = di_result.group("date") or "" 48 self.__id = di_result.group("id") or "" 49 be_result = Comment.__be.search(columns[2]) 50 self.__be = None 51 if be_result: self.__be = be_result.group("be") 52 53 self.__responses_cache = None 54 55 def getName(self): return self.__name 56 name = property(getName) 57 58 def getMailAddr(self): return self.__mailaddr 59 mailaddr = property(getMailAddr) 60 61 def getDate(self): return self.__date 62 date = property(getDate) 63 64 def getBody(self): return self.__body 65 body = property(getBody) 66 67 def getDatetime(self): 68 if self.__datetime_cache: return self.__datetime_cache 69 70 result = Comment.__datetime.search(self.__date) 71 if result: 72 year = int(result.group("year")) 73 month = int(result.group("month")) 74 day = int(result.group("day")) 75 hour = int(result.group("hour")) 76 m = int(result.group("min")) 77 sec = int(result.group("sec")) 78 c = result.group("csec") 79 csec = c == None and 0 or int(c) 80 81 d = datetime.datetime(year, month, day, hour, m, sec, csec*10000) 82 self.__datetime_cache = d 83 return d 84 else: return None 85 datetime = property(getDatetime) 86 87 def getID(self): return self.__id 88 ID = property(getID) 89 90 def getBe(self): return self.__be 91 be = property(getBe) 92 93 def extractUrls(self): 94 """ 95 コメントの内容から、URLの一覧を抽出して返します. 96 """ 97 if self.__urls_cache: return self.__urls_cache 98 result = Comment.__urls.finditer(self.body) 99 l = ["".join( ("h", r.group(0)) ) for r in result] 100 self.__urls_cache = l 101 return l 102 urls = property(extractUrls) 103 104 def extractResponses(self, returnType="str"): 105 """ 106 コメントの内容から、レスポンスの一覧を抽出して返します. 107 """ 108 if self.__responses_cache == None: 109 result = Comment.__response.finditer(self.body) 110 l = [(r.group(1),r.group(2)) for r in result] 111 self.__responses_cache = l 112 113 l = self.__responses_cache 114 if returnType == "str": return ["".join(i) for i in l] 115 elif returnType == "int": return self.__makeIntegerLists(l) 116 elif returnType == "comment": 117 rl = self.__makeIntegerLists(l) 118 rl2 = [] 119 for i in rl: 120 if type(i) == int: 121 if 0<i<=self.thread.res: rl2.append(self.thread[i]) 122 else: rl2.append([self.thread[j] for j in i if 0<j<=self.thread.res]) 123 return rl2 124 125 else: raise TypeError 126 127 res = property(extractResponses) 128 responses = property(extractResponses) 129 130 def __makeIntegerLists(self, l): 131 rl = [] 132 for i in l: 133 start = int(unicodedata.normalize("NFKC", i[0][2:])) 134 if i[1] == "": rl.append(start) 135 else: 136 end = int(unicodedata.normalize("NFKC", i[1][1:])) 137 rl.append(range(start, end+1)) 138 return rl 139 140 def render(self): 141 """ 142 取得したコメントから、整形された文章を返します. 143 """ 144 if self.be: 145 header = u"%i 名前:%s [%s]: %s ID:%s BE:%s" % \ 146 (self.number, self.name, self.mailaddr, self.date, self.ID, self.be) 147 else: 148 header = u"%i 名前:%s [%s]: %s ID:%s" % \ 149 (self.number, self.name, self.mailaddr, self.date, self.ID) 150 return u"%s\n%s\n" % (header, self.body) 151 152 def __unicode__(self): return self.render() -
lang/python/twopy/trunk/twopy/errors.py
r34396 r36521 5 5 6 6 class DatoutError (Exception): 7 """8 対象のURLがdat落ちとなった場合に送出されるエラーです.9 """10 def __init__(self, value):11 self.value = value12 13 def __str__(self):14 return "DatoutError: %s" % (self.value)7 """ 8 対象のURLがdat落ちとなった場合に送出されるエラーです. 9 """ 10 def __init__(self, value): 11 self.value = value 12 13 def __str__(self): 14 return "DatoutError: %s" % (self.value) 15 15 16 16 class NotRetrievedError (Exception): 17 """18 対象のオブジェクトがまだ取得されていない場合に送出されるエラーです.19 """20 def __init__(self, value="an object is not retrieved."):21 self.value = value22 23 def __str__(self):24 return "NotRetrievedError: %s" % (self.value)17 """ 18 対象のオブジェクトがまだ取得されていない場合に送出されるエラーです. 19 """ 20 def __init__(self, value="an object is not retrieved."): 21 self.value = value 22 23 def __str__(self): 24 return "NotRetrievedError: %s" % (self.value) 25 25 26 26 class Message (object): 27 28 __title_re = re.compile("<title>(?P<title>.*?)</title>")29 __body_re = re.compile("<body>(?P<body>.*?)</body>")30 31 def __init__(self, dat):32 self.__title = Message.__title_re.search(dat).group("title")33 self.__body = Message.__body_re.search(dat).group("body")34 35 def getTitle(self): return self.__title36 title = property(getTitle)37 38 def getBody(self): return self.__body39 body = property(getBody)27 28 __title_re = re.compile("<title>(?P<title>.*?)</title>") 29 __body_re = re.compile("<body>(?P<body>.*?)</body>") 30 31 def __init__(self, dat): 32 self.__title = Message.__title_re.search(dat).group("title") 33 self.__body = Message.__body_re.search(dat).group("body") 34 35 def getTitle(self): return self.__title 36 title = property(getTitle) 37 38 def getBody(self): return self.__body 39 body = property(getBody) -
lang/python/twopy/trunk/twopy/thread.py
r34400 r36521 6 6 7 7 class Thread (object): 8 """ 9 2chのスレッドを管理するクラスです. 10 """ 11 12 __url = re.compile(r"http://(.+?)/test/read.cgi/(.+?)/(\d+)") 13 __etag = re.compile(r'".*"') 14 15 __hidden = re.compile(r'input type=hidden name="(.+?)" value="(.+?)"') 16 17 @classmethod 18 def initWithUrl(cls, url, user=None): 19 """ 20 スレッドを示すURLから、対象のThreadクラスを生成します. 21 22 url : 対象となるURL 23 user : 通信に用いるtwopy.Userクラスのインスタンス 24 """ 25 rs = Thread.__url.search(url) 26 if rs == None: raise TypeError 27 28 server = rs.group(1) 29 board_name = rs.group(2) 30 dat_number = rs.group(3) 31 32 board_url = "http://%s/%s/" % (server, board_name) 33 u = user or twopy.User.anonymouse() 34 b = twopy.Board(board_url, u) 35 filename = "%s.dat" % dat_number 36 return Thread(b, filename, u) 37 38 def __init__(self, board, filename, user=None, title="", res=0): 39 """ 40 オブジェクトのコンストラクタです. 41 42 board : 対象となる板のインスタンス 43 filename : datファイル名 44 user : 通信に用いるtwopy.Userクラスのインスタンス 45 title : スレッドのタイトルが判明している場合は別途指定. 46 retrieve()が呼び出された場合は、取得したスレッド名で上書きされる. 47 res : スレッドのレス数が判明している場合は別途指定. 48 retrieve()が呼び出された場合は、取得したレス数で上書きされる. 49 """ 50 self.__board = board 51 self.__filename = filename 52 self.__user = user or twopy.User.anonymouse() 53 self.__title = title 54 self.__res = res 55 self.__rawdat = "" 56 self.__comments = [] 57 58 self.__isRetrieved = False 59 self.__isBroken = False 60 self.__last_modified = None 61 self.__etag = None 62 63 def __init_thread(self): 64 self.__rawdat = "" 65 self.__comments = [] 66 self.__isRetrieved = False 67 68 def getBoard(self): return self.__board 69 board = property(getBoard) 70 71 def getFilename(self): return self.__filename 72 filename = property(getFilename) 73 74 def getUser(self): return self.__user 75 user = property(getUser) 76 77 def getConfig(self): return self.__conf 78 def setConfig(self, conf): self__conf = conf 79 config = property(getConfig, setConfig) 80 81 def getTitle(self): return self.__title 82 title = property(getTitle) 83 84 def getResponse(self): return self.__res 85 response = property(getResponse) 86 res = property(getResponse) 87 88 def getDatNumber(self): 89 return int(self.filename[:-4]) 90 dat_number = property(getDatNumber) 91 92 def get_isRetrieved(self): return self.__isRetrieved 93 isRetrieved = property(get_isRetrieved) 94 95 def get_isBroken(self): return self.__isBroken 96 isBroken = property(get_isBroken) 97 98 def getUrl(self): 99 u = "%sdat/%s" % (self.board.url, self.filename) 100 return u 101 url = property(getUrl) 102 103 def getSince(self): 104 return datetime.datetime.fromtimestamp(self.dat_number) 105 since = property(getSince) 106 107 def getVelocity(self, t=None): 108 tp = t or time.time() 109 now = int(tp) 110 delta = now - self.dat_number 111 return self.response / float(delta)*3600 112 velocity = property(getVelocity) 113 114 def retrieve(self): 115 """ 116 スレッドからdatファイルを読み込み、その内容を取得します. 117 """ 118 self.__init_thread() 119 response = self.user.urlopen(self.url, gzip=True) 120 121 if response.code == 200: 122 headers = response.info() 123 self.__last_modified = headers["Last-Modified"] 124 self.__etag = Thread.__etag.search(headers["ETag"]).group(0) 125 gzip_str = StringIO.StringIO(response.read()) 126 self.__rawdat = gzip.GzipFile(fileobj=gzip_str).read() 127 if self.__rawdat.startswith("<html>"): 128 # Dat落ちと判断 129 raise twopy.DatoutError, twopy.Message(self.__rawdat) 130 self.__appendComments(unicode(self.__rawdat, "Shift_JIS", "replace")) 131 self.__isRetrieved = True 132 self.__isBroken = False 133 self.__res = len(self.__comments) 134 return response.code 135 136 def __appendComments(self, dat): 137 no_tag = re.compile("<.*?>") 138 nt = re.compile("(?P<name>.*)</b>(?P<trip>.*)<b>") 139 di = re.compile("(?P<date>.*) ID:(?P<id>.*)") 140 141 for (i, line) in enumerate(dat.split("\n")): 142 if len(self.__comments) == 0 : 143 columns = line.split("<>") 144 self.__title = columns[4] 145 if line != "": 146 self.__comments.append( twopy.Comment(self, line, i+1) ) 147 148 def update(self): 149 """ 150 スレッドの内容を更新します. 151 152 返り値: HTTPステータスコード 153 """ 154 if len(self.__comments) == 0: # 未取得だった場合 155 self.retrieve() 156 return 157 158 try: 159 response = self.user.urlopen(self.url, gzip=False, bytes=len(self.__rawdat), 160 if_modified_since=self.__last_modified, 161 if_none_match=self.__etag) 162 if response.code == 206: 163 # datが更新されていた場合 164 headers = response.info() 165 self.__last_modified = headers["Last-Modified"] 166 self.__etag = Thread.__etag.search(headers["ETag"]).group(0) 167 newdat = response.read() 168 self.__rawdat += newdat 169 self.__appendComments(newdat) 170 self.__res = len(self.__comments) 171 elif response.code == 416: 172 # datが壊れている場合 173 self.__isBroken = True 174 else: raise TypeError 175 176 return response.code 177 178 except urllib2.HTTPError, e: 179 if e.code == 304: 180 # datが更新されていない場合 181 pass 182 return e.code 183 184 def autopost(self, name=u"", mailaddr=u"", message=u"", 185 submit=u"書き込む", delay=5): 186 """ 187 書き込みの確認をすべてスキップして書き込みます. 188 """ 189 r1 = self.post(name, mailaddr, message, submit, delay=delay) 190 if r1[0] == twopy.STATUS_COOKIE: 191 r2 = self.post(name, mailaddr, message, submit, hidden=r1[2], delay=delay) 192 return r2 193 else: 194 return r1 195 196 def post(self, name=u"", mailaddr=u"", message=u"", 197 submit=u"書き込む", hidden={}, delay=5): 198 """ 199 コメントの書き込みを試みます. 200 201 引数: 202 name : 名前 203 mailaddr : メールアドレス 204 message : 本文の文章 205 submit : 書き込みボタンのキャプション 206 hidden : hidden属性の値 207 delay : 本来の時間から何秒だけ後戻りさせるか(未来の時間に書き込むのを防ぐ) 208 209 返り値: 210 レスポンスコードと受信した文章の本文のタプル. 211 レスポンスコード: 212 twopy.STATUS_TRUE 書き込み成功 213 twopy.STATUS_FALSE 書き込み成功&警告 214 twopy.STATUS_ERROR 書き込み失敗 215 twopy.STATUS_CHECK 書き込み警告 216 twopy.STATUS_COOKIE 書き込み確認 217 218 ただし、レスポンスコードがtwopy.STATUS_COOKIEの場合、 219 返り値はレスポンスコード、受信した文章の本文、input->hidden属性の辞書が返されます. 220 """ 221 referer = "%stest/read.cgi/%s/%i/" % (self.board.server, self.board.name, self.dat_number) 222 send_dict = { "bbs" : self.board.name, 223 "key" : self.dat_number, 224 "time" : int(time.time())-delay, 225 "FROM" : name.encode("Shift_JIS"), 226 "mail" : mailaddr.encode("Shift_JIS"), 227 "MESSAGE" : message.encode("Shift_JIS"), 228 "submit" : submit.encode("Shift_JIS")} 229 send_dict.update(hidden) 230 params = urllib.urlencode(send_dict) 231 232 return utility.bbsPost(self.user, self.board, params, referer) 233 234 def __iter__(self): 235 if not self.isRetrieved: raise twopy.NotRetrievedError 236 for comment in self.__comments: 237 yield comment 238 239 def __len__(self): 240 if not self.isRetrieved: raise twopy.NotRetrievedError 241 return len(self.__comments) 242 243 def __getitem__(self, i): 244 if not self.isRetrieved: raise twopy.NotRetrievedError 245 return self.__comments[i-1] 8 """ 9 2chのスレッドを管理するクラスです. 10 """ 11 12 __url = re.compile(r"http://(.+?)/test/read.cgi/(.+?)/(\d+)") 13 __etag = re.compile(r'".*"') 14 15 __hidden = re.compile(r'input type=hidden name="(.+?)" value="(.+?)"') 16 17 @classmethod 18 def initWithUrl(cls, url, user=None): 19 """ 20 スレッドを示すURLから、対象のThreadクラスを生成します. 21 22 url : 対象となるURL 23 user : 通信に用いるtwopy.Userクラスのインスタンス 24 """ 25 rs = Thread.__url.search(url) 26 if rs == None: raise TypeError 27 28 server = rs.group(1) 29 board_name = rs.group(2) 30 dat_number = rs.group(3) 31 32 board_url = "http://%s/%s/" % (server, board_name) 33 u = user or twopy.User.anonymouse() 34 b = twopy.Board(board_url, u) 35 filename = "%s.dat" % dat_number 36 return Thread(b, filename, u) 37 38 def __init__(self, board, filename, user=None, title="", res=0): 39 """ 40 オブジェクトのコンストラクタです. 41 42 board : 対象となる板のインスタンス 43 filename : datファイル名 44 user : 通信に用いるtwopy.Userクラスのインスタンス 45 title : スレッドのタイトルが判明している場合は別途指定. 46 retrieve()が呼び出された場合は、取得したスレッド名で上書きされる. 47 res : スレッドのレス数が判明している場合は別途指定. 48 retrieve()が呼び出された場合は、取得したレス数で上書きされる. 49 """ 50 self.__board = board 51 self.__filename = filename 52 self.__user = user or twopy.User.anonymouse() 53 self.__title = title 54 self.__res = res 55 self.__rawdat = "" 56 self.__comments = [] 57 58 self.__isRetrieved = False 59 self.__isBroken = False 60 self.__last_modified = None 61 self.__etag = None 62 63 def __init_thread(self): 64 self.__rawdat = "" 65 self.__comments = [] 66 self.__isRetrieved = False 67 68 def getBoard(self): return self.__board 69 board = property(getBoard) 70 71 def getFilename(self): return self.__filename 72 filename = property(getFilename) 73 74 def getUser(self): return self.__user 75 user = property(getUser) 76 77 def getConfig(self): return self.__conf 78 def setConfig(self, conf): self__conf = conf 79 config = property(getConfig, setConfig) 80 81 def getTitle(self): return self.__title 82 title = property(getTitle) 83 84 def getResponse(self): return self.__res 85 response = property(getResponse) 86 res = property(getResponse) 87 88 def getDatNumber(self): 89 return int(self.filename[:-4]) 90 dat_number = property(getDatNumber) 91 92 def get_isRetrieved(self): return self.__isRetrieved 93 isRetrieved = property(get_isRetrieved) 94 95 def get_isBroken(self): return self.__isBroken 96 isBroken = property(get_isBroken) 97 98 def getUrl(self): 99 u = "%sdat/%s" % (self.board.url, self.filename) 100 return u 101 url = property(getUrl) 102 103 def getSince(self): 104 return datetime.datetime.fromtimestamp(self.dat_number) 105 since = property(getSince) 106 107 def getVelocity(self, t=None): 108 tp = t or time.time() 109 now = int(tp) 110 delta = now - self.dat_number 111 return self.response / float(delta)*3600 112 velocity = property(getVelocity) 113 114 def retrieve(self): 115 """ 116 スレッドからdatファイルを読み込み、その内容を取得します. 117 """ 118 self.__init_thread() 119 response = self.user.urlopen(self.url, gzip=True) 120 121 if response.code == 200: 122 headers = response.info() 123 self.__last_modified = headers["Last-Modified"] 124 self.__etag = Thread.__etag.search(headers["ETag"]).group(0) 125 gzip_str = StringIO.StringIO(response.read()) 126 self.__rawdat = gzip.GzipFile(fileobj=gzip_str).read() 127 if self.__rawdat.startswith("<html>"): 128 # Dat落ちと判断 129 raise twopy.DatoutError, twopy.Message(self.__rawdat) 130 self.__appendComments(unicode(self.__rawdat, "Shift_JIS", "replace")) 131 self.__isRetrieved = True 132 self.__isBroken = False 133 self.__res = len(self.__comments) 134 if response.code == 203: 135 # Dat落ちと判断(10/01/24現在のanydat.soモジュールの仕様より) 136 raise twopy.DatoutError, twopy.Message(self.__rawdat) 137 138 return (response.code, self.__res) 139 140 def __appendComments(self, dat): 141 no_tag = re.compile("<.*?>") 142 nt = re.compile("(?P<name>.*)</b>(?P<trip>.*)<b>") 143 di = re.compile("(?P<date>.*) ID:(?P<id>.*)") 144 145 num = 0 146 for (i, line) in enumerate(dat.split("\n")): 147 if len(self.__comments) == 0 : 148 columns = line.split("<>") 149 self.__title = columns[4] 150 if line != "": 151 self.__comments.append( twopy.Comment(self, line, i+1) ) 152 num += 1 153 return num 154 155 def update(self): 156 """ 157 スレッドの内容を更新します. 158 159 返り値: HTTPステータスコード 160 """ 161 if not self.isRetrieved: # 未取得だった場合 162 return self.retrieve() 163 164 num = 0 165 try: 166 response = self.user.urlopen(self.url, gzip=False, bytes=len(self.__rawdat), 167 if_modified_since=self.__last_modified, 168 if_none_match=self.__etag) 169 if response.code == 206: 170 # datが更新されていた場合 171 headers = response.info() 172 self.__last_modified = headers["Last-Modified"] 173 self.__etag = Thread.__etag.search(headers["ETag"]).group(0) 174 newdat = response.read() 175 self.__rawdat += newdat 176 num = self.__appendComments(newdat) 177 self.__res = len(self.__comments) 178 elif response.code == 416: 179 # datが壊れている場合 180 self.__isBroken = True 181 else: raise TypeError 182 183 return (response.code, num) 184 185 except urllib2.HTTPError, e: 186 if e.code == 304: 187 # datが更新されていない場合 188 pass 189 return (e.code, num) 190 191 def autopost(self, name=u"", mailaddr=u"", message=u"", 192 submit=u"書き込む", delay=5): 193 """ 194 書き込みの確認をすべてスキップして書き込みます. 195 """ 196 r1 = self.post(name, mailaddr, message, submit, delay=delay) 197 if r1[0] == twopy.STATUS_COOKIE: 198 r2 = self.post(name, mailaddr, message, submit, hidden=r1[2], delay=delay) 199 return r2 200 else: 201 return r1 202 203 def post(self, name=u"", mailaddr=u"", message=u"", 204 submit=u"書き込む", hidden={}, delay=5): 205 """ 206 コメントの書き込みを試みます. 207 208 引数: 209 name : 名前 210 mailaddr : メールアドレス 211 message : 本文の文章 212 submit : 書き込みボタンのキャプション 213 hidden : hidden属性の値 214 delay : 本来の時間から何秒だけ後戻りさせるか(未来の時間に書き込むのを防ぐ) 215 216 返り値: 217 レスポンスコードと受信した文章の本文のタプル. 218 レスポンスコード: 219 twopy.STATUS_TRUE 書き込み成功 220 twopy.STATUS_FALSE 書き込み成功&警告 221 twopy.STATUS_ERROR 書き込み失敗 222 twopy.STATUS_CHECK 書き込み警告 223 twopy.STATUS_COOKIE 書き込み確認 224 225 ただし、レスポンスコードがtwopy.STATUS_COOKIEの場合、 226 返り値はレスポンスコード、受信した文章の本文、input->hidden属性の辞書が返されます. 227 """ 228 referer = "%stest/read.cgi/%s/%i/" % (self.board.server, self.board.name, self.dat_number) 229 send_dict = { "bbs" : self.board.name, 230 "key" : self.dat_number, 231 "time" : int(time.time())-delay, 232 "FROM" : name.encode("Shift_JIS"), 233 "mail" : mailaddr.encode("Shift_JIS"), 234 "MESSAGE" : message.encode("Shift_JIS"), 235 "submit" : submit.encode("Shift_JIS")} 236 send_dict.update(hidden) 237 params = urllib.urlencode(send_dict) 238 239 return utility.bbsPost(self.user, self.board, params, referer) 240 241 def __iter__(self): 242 if not self.isRetrieved: raise twopy.NotRetrievedError 243 for comment in self.__comments: 244 yield comment 245 246 def __len__(self): 247 if not self.isRetrieved: raise twopy.NotRetrievedError 248 return len(self.__comments) 249 250 def __getitem__(self, i): 251 if not self.isRetrieved: raise twopy.NotRetrievedError 252 return self.__comments[i-1] -
lang/python/twopy/trunk/twopy/user.py
r34396 r36521 5 5 6 6 class User (object): 7 """8 クッキーの管理や、ユーザーエージェントなどのヘッダ全般を管理するクラスです.9 """10 @classmethod11 def anonymouse(cls):12 return User()13 14 def __init__(self,15 user_agent = "Monazilla 1.00",16 language = "ja",17 keep_alive = 300):18 self.user_agent = user_agent19 self.language = language20 self.keep_alive = keep_alive21 22 cj = cookielib.CookieJar()23 self.__cookie = cj24 self.__opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))25 26 def getOpener(self): return self.__opener27 opener = property(getOpener)28 29 def getCookie(self): return self.__cookie30 cookie = property(getCookie)31 32 def getRequest(self, url, gzip=False, bytes=0,33 if_modified_since=None, if_none_match=None):34 headers = {35 "User-Agent" : self.user_agent,36 "Accept-Language" : self.language,37 "Keep-Alive" : str(self.keep_alive),38 "Connection" : "close",39 }40 if gzip: headers["Accept-Encoding"] = "gzip"41 if bytes > 0 :42 headers["If-Modified-Since"] = if_modified_since43 headers["If-None-Match"] = if_none_match44 headers["Range"] = "bytes= %i-" % bytes45 req = urllib2.Request(url, None, headers)46 return req47 48 def urlopen(self, url, gzip=False, bytes=0,49 if_modified_since=None, if_none_match=None):50 req = self.getRequest(url, gzip, bytes, if_modified_since, if_none_match)51 return self.opener.open(req)52 53 def urlpost(self, url, param, referer):54 headers = {55 "User-Agent" : self.user_agent,56 "Accept" : "*/*",57 "Accept-Language" : self.language,58 "Keep-Alive" : str(self.keep_alive),59 "Referer" : referer,60 "Connection" : "close",61 }62 req = urllib2.Request(url, None, headers)63 self.__cookie.add_cookie_header(req)64 return self.opener.open(req, param)7 """ 8 クッキーの管理や、ユーザーエージェントなどのヘッダ全般を管理するクラスです. 9 """ 10 @classmethod 11 def anonymouse(cls): 12 return User() 13 14 def __init__(self, 15 user_agent = "Monazilla 1.00", 16 language = "ja", 17 keep_alive = 300): 18 self.user_agent = user_agent 19 self.language = language 20 self.keep_alive = keep_alive 21 22 cj = cookielib.CookieJar() 23 self.__cookie = cj 24 self.__opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) 25 26 def getOpener(self): return self.__opener 27 opener = property(getOpener) 28 29 def getCookie(self): return self.__cookie 30 cookie = property(getCookie) 31 32 def getRequest(self, url, gzip=False, bytes=0, 33 if_modified_since=None, if_none_match=None): 34 headers = { 35 "User-Agent" : self.user_agent, 36 "Accept-Language" : self.language, 37 "Keep-Alive" : str(self.keep_alive), 38 "Connection" : "close", 39 } 40 if gzip: headers["Accept-Encoding"] = "gzip" 41 if bytes > 0 : 42 headers["If-Modified-Since"] = if_modified_since 43 headers["If-None-Match"] = if_none_match 44 headers["Range"] = "bytes= %i-" % bytes 45 req = urllib2.Request(url, None, headers) 46 return req 47 48 def urlopen(self, url, gzip=False, bytes=0, 49 if_modified_since=None, if_none_match=None): 50 req = self.getRequest(url, gzip, bytes, if_modified_since, if_none_match) 51 return self.opener.open(req) 52 53 def urlpost(self, url, param, referer): 54 headers = { 55 "User-Agent" : self.user_agent, 56 "Accept" : "*/*", 57 "Accept-Language" : self.language, 58 "Keep-Alive" : str(self.keep_alive), 59 "Referer" : referer, 60 "Connection" : "close", 61 } 62 req = urllib2.Request(url, None, headers) 63 self.__cookie.add_cookie_header(req) 64 return self.opener.open(req, param) -
lang/python/twopy/trunk/twopy/utility.py
r34396 r36521 19 19 20 20 def bbsPost(user, board, params, referer): 21 """22 サーバーの/test/bbs.cgiを呼び出して、書き込みやスレッド作成などの処理を行います.23 """24 url = "%stest/bbs.cgi" % (board.server)25 26 try:27 response = user.urlpost(url, params, referer)28 if response.code == 200:29 body = unicode(response.read(), "Shift_JIS", "ignore")30 code = __detectStatusCode(body)31 if code == STATUS_COOKIE:32 r = __hidden.search(body)33 d = {r.group(1):r.group(2)}34 return (code, body, d)35 else: return (code, body)36 else:37 raise TypeError38 except urllib2.HTTPError, e:39 print e.code21 """ 22 サーバーの/test/bbs.cgiを呼び出して、書き込みやスレッド作成などの処理を行います. 23 """ 24 url = "%stest/bbs.cgi" % (board.server) 25 26 try: 27 response = user.urlpost(url, params, referer) 28 if response.code == 200: 29 body = unicode(response.read(), "Shift_JIS", "ignore") 30 code = __detectStatusCode(body) 31 if code == STATUS_COOKIE: 32 r = __hidden.search(body) 33 d = {r.group(1):r.group(2)} 34 return (code, body, d) 35 else: return (code, body) 36 else: 37 raise TypeError 38 except urllib2.HTTPError, e: 39 print e.code 40 40 41 41 def __detectStatusCode(body): 42 """43 与えられた引数からステータスコードを返します.44 """45 if __status_false.search(body): return STATUS_FALSE46 elif __status_true.search(body): return STATUS_TRUE47 elif __status_error.search(body): return STATUS_ERROR48 elif __status_check.search(body): return STATUS_CHECK49 elif __status_cookie.search(body): return STATUS_COOKIE50 else: raise TypeError, "twopy can't detect the status code."42 """ 43 与えられた引数からステータスコードを返します. 44 """ 45 if __status_false.search(body): return STATUS_FALSE 46 elif __status_true.search(body): return STATUS_TRUE 47 elif __status_error.search(body): return STATUS_ERROR 48 elif __status_check.search(body): return STATUS_CHECK 49 elif __status_cookie.search(body): return STATUS_COOKIE 50 else: raise TypeError, "twopy can't detect the status code."
![(please configure the [header_logo] section in trac.ini)](/share/chrome/site/your_project_logo.png)