多线程爬虫
今日内容
1. 并发与并行(**)
2. 多线程导致数据的不安全(**) --> 理解不了, 那就记住结论(多线程共同操作数据会导致数据不安全)
3. 多线程爬虫架构(*****)
4. 多线程爬虫的代码(*****)
1.并发与并行
1.并发: 在同一时间段内, 所有任务同时运行.
2.并行: 在同一时刻, 所有任务同时执行
2.多线程
i = 0
i += 1
i -= 1
print(i)
多线程共同操作数据会导致数据不安全
3.多线程架构图
1.url,发请求, 获取响应
2.数据解析
3.数据持久化
from threading import Thread
from threading import Lock
from queue import Queue
import requests
import pymysql
from lxml import etree
# base_url = 'http://xiaohua.zol.com.cn/youmo/%s.html'
# 爬虫类
class Sqider(Thread):
def __init__(self, sname, urlQueue, dataQueue):
super().__init__()
self.sname = sname
self.urlQueue = urlQueue
self.dataQueue = dataQueue
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
}
# 爬取数据
def run(self, ):
base_url = 'http://xiaohua.zol.com.cn/youmo/%s.html'
while 1:
# block 代表阻塞,block为True即为阻塞。block为False为不阻塞
try:
print('%s正在爬取数据' % self.name)
page = self.urlQueue.get(block=False)
res = requests.get(url=base_url % page, headers=self.headers)
self.dataQueue.put(res.text)
print('%s提交数据完毕--' % self.name)
except:
break
# 解析类
class Parse(Thread):
def __init__(self, pname, dataQueue, conn, cursor, lock):
super().__init__()
self.pname = pname
self.dataQueue = dataQueue
self.conn = conn
self.cursor = cursor
self.lock = lock
def run(self):
# 实现数据解析的过程
print('run方法已经调用')
self.parse(self.dataQueue) # 调用parse方法
def parse(self, dQueue):
# 实现具体的解析过程
while 1:
try:
html = dQueue.get()
tree = etree.HTML(html)
li_list = tree.xpath('//li[@class="article-summary"]')
for li in li_list:
title = li.xpath('.//span[@class="article-title"]/a/text()')[0]
content = li.xpath('.//div[@class="summary-text"]//text()')
data = {'title': title, 'content': content}
with self.lock:
self.save(data)
except:
break
# 存储数据
def save(self, data):
sql = 'insert into joke values ("%s","%s")' % (data['title'], data['content'])
try:
self.cursor.execute(sql)
self.conn.commit()
print('%s存储数据成功' % self.pname)
except Exception as e:
print(e)
self.conn.rollback()
# 主函数
def main():
# 盛放url的地方 url队列
urlQueue = Queue()
for page in range(1, 101):
urlQueue.put(page)
# 放置响应数据
dataQueue = Queue()
# 开启爬虫线程
snames = ['爬虫1号', '爬虫2号', '爬虫3号']
slist = []
for sname in snames:
# 实例化线程对象
s = Sqider(sname, urlQueue, dataQueue)
# 开启线程
s.start()
slist.append(s)
for s in slist:
s.join()
# 链接数据库
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', password='1234', charset='utf8',
database='xiaohua')
cursor = conn.cursor()
# 解析线程的开启
lock = Lock() # 上锁
plist = []
pnl = ['解析1号', '解析2号', '解析3号']
for pname in pnl:
p = Parse(pname, dataQueue, conn, cursor, lock)
p.start()
plist.append(p)
for p in plist:
p.join()
# 程序入口
if __name__ == '__main__':
main()