一、前期準備
為了完成一個網頁爬蟲的小程式,需要有以下準備:
1 瞭解基本的http協議
2 urllib2庫介面熟悉
3 熟悉pythonRegex
二、程式編寫思路
這裡只是實現一個基本的網頁爬蟲程式,它的基本思路如下:
1 尋找到需要抓取的網頁,查看其原始碼,分析需要擷取網頁頁面的html規律
2 利用urllib2庫來讀取所需要的網頁
3 利用Regex正確提取出所需要的網頁資訊
4 對擷取的網頁資料資訊進行有效性校正工作,即資訊的甄別
5 對有效資料資訊進行儲存,如存放於檔案,或資料庫db中
三、抓取網頁Proxy 伺服器爬蟲的執行個體
找到一個Proxy 伺服器的網站,如http://www.cnproxy.com/,我們需要將http://www.cnproxy.com/proxy1.html下的IP,連接埠,type和area欄位提取出來。主要利用Regex和urllib2庫,代碼如下:
proxylist1=Queue.Queue()#用於存放從頁面中抓取的所有伺服器代理IP地址和連接埠號碼portdicts = {'z':"3", 'm':"4", 'a':"2", 'l':"9", 'f':"0", 'b':"5", 'i':"7", 'w':"6", 'x':"8", 'c':"1", 'r':"8", 'd':"0"}def get_proxy_from_cnproxy(): global proxylist1 p = re.compile(r'''<tr><td>(.+?)<SCRIPT type=text/javascript>document.write\(":"\+(.+?)\)</SCRIPT></td><td>(.+?)</td><td>.+?</td><td>(.+?)</td></tr>''') for i in range(1,11): target = r"http://www.cnproxy.com/proxy%d.html" %i print target req = urllib2.urlopen(target) result = req.read() matchs = p.findall(result) #print matchs for row in matchs: ip = row[0] port = row[1] if port is None: continue tmp = port.split('+') #發現在html中的連接埠找不到一些鍵所對應的值,過濾掉這些IP flag = 0 for x in tmp: if x not in portdicts: flag = 1 break if flag == 1: continue port = map(lambda x: portdicts[x], port.split('+')) port = ''.join(port) agent = row[2] addr = row[3].decode("cp936").encode("utf-8") l = [ip, port, agent, addr] print l proxylist1.put(l) print "page 1-10 size:%s nums proxy info" %proxylist1.qsize()
上述程式碼完成將所需要的網頁欄位提取出來存放到隊列proxylist1中。緊接著需要對隊列proxylist1中的每個欄位進行校正,判斷裡面資料的有效性,然後將檢測到的資料存放於另一隊列ProxyCheckedList中,然後對有效資料資訊進行一個排序,最後將其儲存於檔案中。代碼如下:
ProxyCheckedList = Queue.Queue()#用於存放經校正後得到的有效代理IP地址和連接埠號碼資訊 class ProxyCheck(threading.Thread): def __init__(self, fname): threading.Thread.__init__(self) self.timeout = 5 #self.test_url = "http://www.baidu.com/" #self.test_str = "030173" #self.test_url = "http://www.so.com/" #self.test_str = '08010314' self.test_url="http://www.renren.com" self.test_str="110000000009" self.fname = fname self.checkedProxyList = [] def checkProxy(self): threadpool = [] for i in range(10):#開闢10個線程放到線程池中 threadpool.append(ck_process(self.test_url, self.test_str,self.timeout,i)) #啟動10個線程同時處理校正工作 map(lambda x: x.start(), threadpool) #等待線程退出 map(lambda x: x.join(), threadpool) while ProxyCheckedList.empty() == False: try: content = ProxyCheckedList.get_nowait() except Exception,e: print e else: self.checkedProxyList.append(content) print "the checked proxylist contains: %s nums records"%len(self.checkedProxyList) for info in self.checkedProxyList: print info def sort(self): sorted(self.checkedProxyList, cmp=lambda x,y:cmp(x[4], y[4]))#對回應時間的代理ip列表進行排序 def save(self): f = open(self.fname, 'w+') for proxy in self.checkedProxyList: f.write("%s:%s\t%s\t%s\t%s\n"%(proxy[0], proxy[1], proxy[2], proxy[3], proxy[4])) f.close() def run(self): self.checkProxy() self.sort() self.save() print 'Done'
這個類主要繼承於線程類threading,它的程式流程主要看run(),它的過程就是上面分析的思路。下面對checkProxy()這個過程進行簡要說明,它建立了10個線程,利用map函數讓這十個線程同時啟動,最後是等待線程的退出。然後對隊列中ProxyCheckedList的資料再次加工就完成了。而這10個線程他們乾的事情是相同的,他們依次從一個隊列ProxyCheckedList中讀出10條http代理資料(IP,連接埠等),然後迭代對這10條資料進行有效性判斷,有效性判斷的思路如下:
用urllib2.HTTPCookieProcessor()建立一個cookie,
利用每個資料的IP和連接埠構定一個代理控制代碼對象proxy_handler = urllib2.ProxyHandler({"http":r'http://%s:%s'%(proxy[0],proxy[1])})
將代理控制代碼與cookie綁定opener = urllib2.build_opener(cookies, proxy_handler)
請求對象的安裝urllib2.install_opener(opener)
最後是利用這個代理IP訪問某個網站,如果在某一規定的時間內有資料返回,則說明有效,將其放至隊列ProxyCheckedList中,否則迭代下一個。下面是代碼:
class ck_process(threading.Thread): '''線程類:用於多執行緒校正代理IP的有效性''' def __init__(self,test_url, test_str, timeout,count): threading.Thread.__init__(self) self.proxy_contain = [] self.test_url = test_url self.test_str = test_str self.checkedProxyList = [] self.timeout = timeout self.count = count def run(self): cookies = urllib2.HTTPCookieProcessor()#構造一個cookie對象 #print "I'm thread process No. %s" %self.count while proxylist1.empty() == False: if lock_que.acquire():#成功擷取鎖 if proxylist1.qsize() >= 10: number = 10 else: number = proxylist1.qsize() for i in range(number): #從原始的列表隊列中擷取出10條代理IP資訊 proxy = proxylist1.get_nowait() self.proxy_contain.append(proxy) #print "%s thread process:%s"%(self.count,self.proxy_contain) #print lock_que.release() #每個線程每次逐一處理10條 for proxy in self.proxy_contain: proxy_handler = urllib2.ProxyHandler({"http":r'http://%s:%s'%(proxy[0],proxy[1])})#構造一個代理控制代碼對象 opener = urllib2.build_opener(cookies, proxy_handler)#將代理控制代碼與cookie綁定 #類比一個瀏覽器,加入http的user-agent頭欄位,參數放入列表的元組 opener.addheaders=[('user-agent','Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31')] urllib2.install_opener(opener)#請求註冊 t1 = time.time() #get current time try:#有時某些代理無效打不開網站 req = urllib2.urlopen(self.test_url, timeout = self.timeout) result = req.read() timeused = time.time() - t1 pos = result.find(self.test_str) if pos > 1: self.checkedProxyList.append((proxy[0],proxy[1], proxy[2], proxy[3], timeused)) else: continue except Exception, e: #print e.message continue if len(self.checkedProxyList) != 0: if lock_que_cked.acquire(): for proxy in self.checkedProxyList: ProxyCheckedList.put(proxy) lock_que_cked.release() print "%s thread process:out: %s nums"%(self.count, len(self.checkedProxyList))
(完)