首先我們需要幾個包:requests, lxml, bs4, pymongo, redis
1. 創建爬蟲對象,具有的幾個行為:抓取頁面,解析頁面,抽取頁面,儲存頁面
class Spider(object): def __init__(self): # 狀態(是否工作) self.status = SpiderStatus.IDLE # 抓取頁面 def fetch(self, current_url): pass # 解析頁面 def parse(self, html_page): pass # 抽取頁面 def extract(self, html_page): pass # 儲存頁面 def store(self, data_dict): pass
2. 設置爬蟲屬性,沒有在爬取和在爬取中,我們用一個類封裝, @unique使里面元素獨一無二,Enum和unique需要從 enum里面導入:
@uniqueclass SpiderStatus(Enum): IDLE = 0 WORKING = 1
3. 重寫多線程的類:
class SpiderThread(Thread): def __init__(self, spider, tasks): super().__init__(daemon=True) self.spider = spider self.tasks = tasks def run(self): while True: pass
4. 現在爬蟲的基本結構已經做完了,在main函數創建tasks, Queue需要從queue里面導入:
def main(): # list沒有鎖,所以使用Queue比較安全, task_queue=[]也可以使用,Queue 是先進先出結構, 即 FIFO task_queue = Queue() # 往隊列放種子url, 即搜狐手機端的url task_queue.put('http://m.sohu,com/') # 指定起多少個線程 spider_threads = [SpiderThread(Spider(), task_queue) for _ in range(10)] for spider_thread in spider_threads: spider_thread.start() # 控制主線程不能停下,如果隊列里有東西,任務不能停, 或者spider處于工作狀態,也不能停 while task_queue.empty() or is_any_alive(spider_threads): pass print('Over')
4-1. 而 is_any_threads則是判斷線程里是否有spider還活著,所以我們再寫一個函數來封裝一下:
def is_any_alive(spider_threads): return any([spider_thread.spider.status == SpiderStatus.WORKING for spider_thread in spider_threads])
5. 所有的結構已經全部寫完,接下來就是可以填補爬蟲部分的代碼,在SpiderThread(Thread)
里面,開始寫爬蟲運行 run 的方法,即線程起來后,要做的事情:
def run(self): while True: # 獲取url current_url = self.tasks_queue.get() visited_urls.add(current_url) # 把爬蟲的status改成working self.spider.status = SpiderStatus.WORKING # 獲取頁面 html_page = self.spider.fetch(current_url) # 判斷頁面是否為空 if html_page not in [None, '']: # 去解析這個頁面, 拿到列表 url_links = self.spider.parse(html_page) # 把解析完的結構加到 self.tasks_queue里面來 # 沒有一次性添加到隊列的方法 用循環添加算求了 for url_link in url_links: self.tasks_queue.put(url_link) # 完成任務,狀態變回IDLE self.spider.status = SpiderStatus.IDLE
6. 現在可以開始寫 Spider()這個類里面的四個方法,首先寫fetch()抓取頁面里面的:
新聞熱點
疑難解答