多线程爬虫
# 事前准备
# 两个容器: url队列, HTML队列, 爬虫线程, 解析的线程
import threading
from threading import Lock
from queue import Queue
from lxml import etree
import requests
import pymongo
# 爬虫线程类
class CrawlThread(threading.Thread):
# 初始化方法
def __init__(self, threadname, pageQueue, dataQueue):
super().__init__()
self.threadname = threadname # 爬虫名信息, 便于监控爬虫
self.pageQueue = pageQueue # 页码容器, 盛放多页爬取的页数
self.dataQueue = dataQueue # 响应数据容器, 用于盛放爬取回来的HTML页码
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36'
}
# run方法:内部实现爬取HTML页面的代码逻辑, 并将返回的响应数据的HTML形式提交至dataQueue容器中
def run(self):
# 基本url, %s出为页码数, 替换不同数字代表响应的那一页
base_url = 'http://xiaohua.zol.com.cn/lengxiaohua/%s.html'
while 1:
try:
# 从pageQueue容器中取出页码
page = self.pageQueue.get(block=False)
print('%s正在爬取数据'%self.threadname)
# 拼接页码进行请求, res接收返回的响应数据的文本形式
res = requests.get(url=base_url%page, headers=self.headers).text
# 将响应数据的文本形式, 提交至响应数据容器dataQueue中
self.dataQueue.put(res)
print('%s提交数据完毕'%self.threadname)
except:
break
# 解析线程
class ParseThread(threading.Thread):
# 初始化方法
def __init__(self, threadname, dataQueue, lock):
super().__init__()
self.threadname = threadname # 线程名, 用于监控线程
self.dataQueue = dataQueue # 响应数据容器
self.lock = lock # 线程锁
def run(self):
while 1:
try:
# 从响应数据容器中取出响应数据的文本形式, 便于解析数据
html = self.dataQueue.get(block=False)
print('%s正在解析'%self.threadname)
# 调用解析方法
self.parse(html)
print('%s解析完毕' % self.threadname)
except:
break
# 解析方法, 内部实现数据解析的具体逻辑
def parse(self, html):
tree = etree.HTML(html)
title_list = tree.xpath('//ul[@class="article-list"]/li/span[@class="article-title"]/a/text()')
for title in title_list:
data = {
'title': title
}
# 调用数据存储方法, 将解析的数据持久化至本地
self.save(data)
# 数据持久化方法
def save(self, data):
conn = pymongo.MongoClient()
db = conn.wangxin
table = db.jiangwan
# 上锁, 避免多线程操作数据导致数据的不安全
with self.lock:
table.insert_one(data)
# 主函数
def main():
# 页码容器
pageQueue = Queue()
# 响应数据容器
dataQueue = Queue()
# 线程锁
lock = Lock()
# 循环添加页码
for page in range(1,50):
# http://xiaohua.zol.com.cn/lengxiaohua/%s.html
pageQueue.put(page)
crawllist = []
crawlname = ['爬虫1号','爬虫2号','爬虫3号',]
for var in crawlname:
# 实例化线程对象
c = CrawlThread(var, pageQueue, dataQueue)
# 开启线程
c.start()
crawllist.append(c)
for c in crawllist:
c.join()
parselist = []
parsename = ['解析1号','解析2号','解析3号',]
for var in parsename:
p = ParseThread(var, dataQueue, lock)
p.start()
parselist.append(p)
for p in crawllist:
p.join()
# 程序入口
if __name__ == '__main__':
main()