今天WPS For Linux Alpha 7發布了,首先感謝WPS團隊的辛勤耕耘,論壇搶包子那個熱鬧啊,很期待明年的beta。
但論壇搶包子有個問題,樓下跟帖的內容是所有人可見的(包括遊客),於是乎就有大量的email地址暴露在大家面前。下面我將用Python試著抓取網頁中的這些email地址,順便練習一下Python的標準庫。(老鳥請繞道)
涉及到的庫有http.client(處理HTTP)、re(Regex)、threading(多線程)。
首先,要抓取網頁內容,必須先拿到html頁面。http.client.HTTPConnection就是用來做這個工作的。http.client.HTTPConnection的建構函式中,host指明web伺服器位址,port指明連接埠(預設80)。
其中以下幾種形式的效果相同:
>>> h1 = http.client.HTTPConnection('www.cwi.nl')
>>> h2 = http.client.HTTPConnection('www.cwi.nl:80')
>>> h3 = http.client.HTTPConnection('www.cwi.nl', 80)
建構函式返回一個HTTPConnection對象,代表了當前這條http串連。然後就可以用這個HTTPConnection對象發送一個request,請求頁面。request的四個參數method, url, body, headers就不詳說了,很容易理解。不過這裡要注意的一點是,headers是個dict,只要將header裡的屬性和值分別以key:value的形式存入dict即可。另外就是如果要投遞cookies,直接在headers裡加就行。
發送完request之後就可以用getresponse方法獲得頁面響應了。getresponse返回一個HTTPResponse對象,代表了http的一個response。這個HTTPResponse對象裡包含了response的頭、內容、狀態代碼等內容。通過HTTPResponse的read方法就可以讀出html頁面了。注意Python 3裡的read方法返回的是bytes對象,需要decode一下才能用於下面的Regex匹配。
有了網頁內容,接下來就可以做內容的解析了。Python有很多解析html頁面元素的庫,像BeautifulSoup、pyQuery、Regex等,這裡我選擇了Regex。關於Regex的文法可以參考下面兩篇文章,講得很詳細:PythonRegex操作指南,Regex30分鐘入門教程。我主要講一下Python的Regex模組re的用法。
re模組使用Regex有兩種用法,效果差不多:直接使用re中的函數、用過re.compile編譯成一個regex對象後再使用。只是後面一種效率高一點。大體流程如下:
通過search函數獲得一個match對象,再通過這個match對象調用group/groups函數獲得結果的分組。
也可以用findall直接搜尋pattern是否存在。
由於單線程太慢,所以開多線程(threading模組)加速。這裡主要通過threading.Thread來建立線程。threading.Thread的建構函式中,target為一個worker函數對象,線程的執行邏輯;args為傳給worker函數的參數,用tupple表示;kwargs也可以用來傳遞參數給worker,只不過這是一個dict,儲存 參數名:參數值 對。
使用start方法啟動建立的線程,join方法等待線程退出。由於這個程式不需要線程之間的同步,所以就沒有介紹互斥量、訊號量之類的同步機制,有需要的讀者請參考官方文檔。
代碼:
#!/usr/bin/env python3import http.clientimport reimport threadingpattern = re.compile(r'(?<=<a href="mailto:)[\w\W]*(?=">)')def Parse(data, fp): lines = data.split('\r\n') for line in lines: match = pattern.search(line) if(match): email = match.group(0) print(email) fp.write(email + '\n')def worker(begin, end, No): print('Thread %d initiated.' % No) conn = http.client.HTTPConnection('bbs.wps.cn') url = '/thread-22351621-%d-1.html' index = begin fp = open('email%02d.txt' % No, 'a') while(index < end): conn.request('GET', url % index) try: res = conn.getresponse() except Exception: continue print('Thread %d: %d, %s' % (No, res.status, url % index)) if(res.status == 200): Parse(res.read().decode('utf8'), fp) index += 1 fp.close()total = 10step = 180 / totalthreads = [threading.Thread(target=worker, args=(i * step, i + step, i)) for i in range(total)]for thread in threads: thread.start()for thread in threads: thread.join()