普通多线程爬虫
import threading
import requests
import time
link_list = []
with open('alexa.txt', 'r') as file:
file_list = file.readlines()
for each in file_list:
link = each.replace('\n', '')
link_list.append(link)
class MyThread(threading.Thread):
def __init__(self, name, link_range):
threading.Thread.__init__(self)
self.link_range = link_range
self.name = name
def run(self):
print("Starting" + self.name)
self.crawler(self.name, self.link_range)
print("Exiting" + self.name)
def crawler(self, thread_name, link_range):
for i in range(link_range[0], link_range[1] + 1):
try:
requests.get(link_list[i], timeout=20)
except Exception as e:
print(thread_name, 'Error:', e)
if __name__ == '__main__':
start = time.time()
thread_list = []
link_range_list = [(0, 200), (201, 400), (401, 600), (601, 800), (801, 1000)]
# 创建新线程
for i in range(1, 6):
thread = MyThread("Thread-" + str(i), link_range_list[i - 1])
thread.start()
thread_list.append(thread)
# 等待所有线程完成
for thread in thread_list:
thread.join()
end = time.time()
print('简单多线程爬虫的总时间为:', end - start)
print("Exiting Main Thread")
在上述代码中,我们将1000个网页分成了5份,每一份是200个网页,即:link_ range_list=[(0,200)(201,400),(401,600),(601,800),(801,1000)]然后利用一个for循环创建5个线程,将这些网页分别指派到5个线程中运行,即:thread MyThread(“Thread-”+str(i), link_range_list[i-1])在每一个线程中,我们将之前单线程爬虫中获取网页部分的代码放入 crawler函数中,抓取这些网页。为了让这些子线程执行完后再执行主进程,这里使用了 threadjoin(方法等待各个线程执行完毕。最后,还会记录下所有线程执行完成的时间end- start,从而得到多线程爬虫完成获取1000个网页任务所需的时间。
上述代码存在一些可以改进之处:因为我们把整个链接列表分成了5等份,所以当某个线程先完成200条网页的爬虫后会退出线程,这样就只剩下有4个线程在运行。相对于5个线程,速度会有所下降,到最后剩下一个线程在运行时,就会变成单线程。
使用 Queue的多线程爬虫
使用 Queue. PythonQueue的模块中提供了同步的、线程安全的队列类,包括FFO(先入先出)队列Queue、LIFO(后入先出)队列Lifo Queue和优先级队列 PriorityQueue将这1000个网页 Queue放入的队列
中,各个线程都是从这个队列中获取链接,直到完成所有的网页抓取为止,代码如下:
import threading
import requests
import time
import queue as Queue
link_list = []
with open('alexa.txt', 'r') as file:
file_list = file.readlines()
for each in file_list:
link = each.replace('\n', '')
link_list.append(link)
class MyThread(threading.Thread):
def __init__(self, name, q):
threading.Thread.__init__(self)
self.q = q
self.name = name
def run(self):
print("Starting" + self.name)
while True:
try:
self.crawler(self.name, self.q)
except:
break
print("Exiting" + self.name)
def crawler(self, thread_name, q):
url = q.get(timeout=2)
try:
r = requests.get(url, timeout=20)
print(q.qsize(), thread_name, r.status_code, url)
except Exception as e:
print(q.qsize(), thread_name, url, 'Error:', e)
if __name__ == '__main__':
threadList = ["Thread-1", "Thread-2", "Thread--3", "Thread--4", "Thread-5"]
workQueue = Queue.Queue(1000)
threads = []
start = time.time()
# 创建新线程
for tName in threadList:
thread = MyThread(tName, workQueue)
thread.start()
threads.append(thread)
# 填充队列
for url in link_list:
workQueue.put(url)
# 等待所有线程完成
for t in threads:
t.join()
end = time.time()
print('Queue多线程爬虫的总时间为:', end - start)
print("Exiting Main Thread")
与之前的简单多线程方法不同的是,在上述代码中,我们使用workQueue=Queue. Queue(1000)建立了一
个队列的对象,然后将这个对象传入了MyThread中,即:thread= MyThread(tName, workQueue)这个 workQueue里面有什么呢?我们可以使用一个for循环来填充队列:
for url in link_list:
workQueue. put(url)
利用 workQueue.put(url)将这1000个网页加入队列中,然后就可以在线程中使用url=.get(timeout=2)获取队列中的链接了。