多线程
当我们写的爬虫是单线程的时候,一旦到一个地方卡的不动了,那就永远的等下去吧,我们可以使用多线程来改变这个问题。
爬虫使用多线程来处理网络请求,使用线程来处理URL队列中的url,然后将url返回的结果保存再另一个队列中,其他线程再读取这个队列中的数据,然后写到文件中去。
URL队列和结果队列
将将要爬取的url放在一个队列中,这里使用标准库Queue,访问url后的结果保存在结果队列中。
初始化一个队列:
from queue import Queue
urls_queue = Queue()
out_queue = Queue()
请求线程:使用多个线程,不停的取url队列中的url,并进行处理。
处理线程:处理结果队列中的数据,并保存到文件中。如果使用多个线程的话,必须要给文件加上锁。
lock = threading.Lock()
f = codecs.open('out.txt','w','utf-8')
当线程需要写入文件的时候,可这样处理:
with lock:
f.write(something)
Queue模块的常用方法
python的Queue模块中提供了同步的、线程安全的队列类,包括FIFO。这些队列都实现了锁原语,就能再多线程中直接使用,可以使用队列来实现线程间的同步。
案例(爬取糗事百科多条段子)
网址:https://www.qiushibaike.com/text/page/1/
代码:
import requests
from fake_useragent import UserAgent
from threading import Thread
from queue import Queue
from lxml import etree
#爬虫类
class CrawInfo(Thread):
def __init__(self,url_queue,html_queue):
Thread.__init__(self)
self.url_queue = url_queue
self.html_queue = html_queue
def run(self):
headers = {"User_Agent":UserAgent().random}
while self.url_queue.empty() == False: #循环到为空为止
url = self.url_queue.get()
response = requests.get(url, headers=headers)
# print(response.text)
if response.status_code == 200:
self.html_queue.put(response.text)
#print(html_queue)
#解析类
class ParseInfo(Thread):
def __init__(self,html_queue):
Thread.__init__(self)
self.html_queue = html_queue
def run(self):
while self.html_queue.empty() == False:
e = etree.HTML(self.html_queue.get())
span_contents = e.xpath('//div[@class="content"]/span[1]')
# print(span_contents)
with open("qiushibaike.txt",'a',encoding='utf-8') as d:
for span in span_contents:
info = span.xpath('string(.)')
d.write(info + '\n')
if __name__ == "__main__":
#存储url的容器
url_queue = Queue()
#存储内容的容器
html_queue = Queue()
base_url = "https://www.qiushibaike.com/text/page/{}/"
for i in range(1, 14):
new_url = base_url.format(i)
url_queue.put(new_url)
#创建一个爬虫,启动三个线程
crawl_list = []
for i in range(0, 3):
crawll = CrawInfo(url_queue, html_queue)
crawl_list.append(crawll)
crawll.start()
for crawl in crawl_list:
crawl.join()#等待
#创捷解析线程
#创建一个解析,启动三个线程:
parse_list = []
for i in range(0,3):
parse = ParseInfo(html_queue)
parse_list.append(parse)
parse.start()
for parse in parse_list:
parse.join()
运行结果:
已经将爬取到的文字添加到了qiushibaike.txt这个文件中。