目录
一:单线程爬虫:
1:新浪图片NBA标题和图片的爬取:
"""
抓取的网站链接:http://api.slide.news.sina.com.cn/interface/api_album.php?activity_size=198_132&size=img&ch_id=2&sub_ch=k&" \
"page={}&num=16&jsoncallback=slideNewsSinaComCnCB
响应:json数据
"""
import requests
import json
class NBANewSpider(object):
# 传入页数
def __init__(self, page_num):
self.url = "http://api.slide.news.sina.com.cn/interface/api_album.php?activity_size=198_132&size=img&ch_id=2&sub_ch=k&" \
"page={}&num=16&jsoncallback=slideNewsSinaComCnCB"
self.urls = [self.url.format(i) for i in range(page_num)]
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
}
def send_request(self, url):
"""发送请求,获得响应"""
response = requests.get(url, headers=self.headers)
return response.content.decode()
def parser_response(self, response_json:str):
"""解析响应"""
# 1:去掉slideNewsSinaComCnCB(字符)
ret_str = response_json.lstrip('slideNewsSinaComCnCB(')
json_str = ret_str.rstrip(')')
# 2: 将json转换成字典:
response_dict = json.loads(json_str)
# 3: 提取当前页的数据列表:
ret_list = response_dict['data']
return ret_list
def write_to_txt(self, ret_list):
"""写入文件"""
with open("NBA数据.txt", 'a', encoding="utf-8") as f:
for dict in ret_list:
f.write(dict['name'])
f.write(" : ")
f.write(dict['url'])
f.write('\n')
def run(self):
for url in self.urls:
response_json = self.send_request(url)
ret_list = self.parser_response(response_json)
self.write_to_txt(ret_list)
if __name__ == '__main__':
nba_spider = NBANewSpider(10)
nba_spider.run()
获取数据展示:
二:多线程爬虫:
1: 回顾多线程的方法:
- 1: 创建线程对象:t1 = threading.Thread(target=func,args=(,)), 参数target指定函数名,args指定传递的参数。
- 2:t1.setDaemon(True),设置守护线程,主线程结束,则子线程强制结束。
- 3:t1.start(), 启动线程。
2:回顾队列的使用:
- 1:队列所在的包:from queue import Queue
- 2:创建一个队列指定最大容量:q = Queue(maxsize=100)
- 3:入队列,如果满了则等待:q.put(item)
- 4:入队列,如果满了则报错:q.put_nowait(item)
- 5:出队列,如果不存在则等待:q.get()
- 6:出队列,如果不存在则报错:q.get_nowait()
- 7:获取队列元素个数:q.qsize()
- 8:获取队列中未完成的任务数:q.unfinished_tasks,队列中未完成的任务数不为0,不能让子线程退出,应该让主线程阻塞。队列中未完成的任务数不为0,任务全部完成,取消主线程阻塞。
- 9:使未完成数量-1:q.task_done()
- 10:q.join() : 让主线程阻塞,直到未完成数量为0。
3:多线程爬虫的执行流程:
问题1: 主线程,也就是我们运行代码这个线程,当创建完子线程后可能子线程还没有执行完呢,而主线程执行完毕了,怎么操作呢?
答: 使用子线程阻塞主线程。 queue.join()
问题2:主线程万一真的因为断电,强制终止了,子线程也要关闭,因此要设置子线程守护主线程。
4:糗事百科多线程爬虫:
import time
import requests
from lxml import html
etree = html.etree
from queue import Queue
from threading import Thread
class QiuShiSpider(object):
"""糗事百科多线程爬虫"""
def __init__(self):
# 构建基础的url地址
# "http://www.qiushibaike.com/8hr/page/{}/".format("2")
self.base_url = " http://www.qiushibaike.com/8hr/page/{}/"
# 构建请求头
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
}
# 创建url队列容器
self.url_queue = Queue()
# 创建html队列容器
self.html_queue = Queue()
# 创建数据队列容器
self.data_queue = Queue()
# 1.构建url地址
def get_urls(self):
"""
:return: 目标url列表
"""
# 一共13页
for i in range(1, 14):
# 拼接翻页的url地址
url = self.base_url.format(i)
# 将url地址装入队列
self.url_queue.put(url)
# 2.发送请求获取数据
def get_html(self):
# 循环的取出url,发送请求,直到任务全部完成
# 完成标志:Thread().unfinished_task 值为0
while True:
# 1.从队列中取出url地址
url = self.url_queue.get()
print(url)
# 2.发送请求获取html页面数据--网络延迟
response = requests.get(url=url, headers=self.headers)
html = response.content.decode()
# 3.将数据存入html队列容器中
self.html_queue.put(html)
# 4.通知url队列任务完成
# 任务数量-1
self.url_queue.task_done()
# 3.从响应中提取数据
def get_content(self):
while True:
# 1.从html队列中取出html数据
html = self.html_queue.get()
# 2.创建etree对象
eroot = etree.HTML(html)
# 3.使用xpath规则提取数据
title_list = eroot.xpath("//a[@class='recmd-content']/text()")
# 4.将数据装入数据队列容器
self.data_queue.put(title_list)
# 5.通知html队列任务完成
# 任务数量-1
self.html_queue.task_done()
# 4.保存数据
def save_data(self):
while True:
# 1.从数据队列中取出数据
data = self.data_queue.get()
# 2.保存数据
print(data)
# 3.通知数据队列任务完成
self.data_queue.task_done()
# 5.运行多线程爬虫
def run(self):
# 线程列表
thread_list = []
# 1.创建获取url的任务线程对象
url_thread = Thread(target=self.get_urls)
thread_list.append(url_thread)
# 2.创建获取html的任务线程对象
html_thread = Thread(target=self.get_html)
thread_list.append(html_thread)
# 3.创建获取数据的任务线程对象
data_thread = Thread(target=self.get_content)
thread_list.append(data_thread)
# 4.创建保存数据的任务线程对象
save_thread = Thread(target=self.save_data)
thread_list.append(save_thread)
# 5.分别将线程对象添加到列表中,方便遍历执行任务
for thread in thread_list:
# 以守护线程的方式执行
# 特点:主线程结束,子线程也会结束
thread.setDaemon(True)
# 启动子线程
thread.start()
# 保证子线程一定能执行
time.sleep(2)
# 同时让队列中所有任务执行完毕后才结束程序
for queue in [self.url_queue, self.html_queue, self.data_queue]:
# 如果unfinished_task不为0的时,处于队列就等待状态,等待任务执行,
# 而每个任务函数中里面都有task_done方法[任务数量-1],任务数量为0时,程序继续执行
queue.join()
if __name__ == '__main__':
spider = QiuShiSpider()
spider.run()
三:多进程爬虫:
1:多进程回顾复习:
- 1:导入多进程包:from multiprocessing import Process
- 2:进程间的队列:from multiprocessing import JoinableQueue
- 3:进程设置守护的方式:task.daemon = True,是设置这个属性。
2:NBA进程爬取数据:
- 注意:睡眠的意义:子进程的启动需要一定的时间,为了防止子进程还没有起来主进程就已经结束了,所以要睡眠。
import json
import time
import requests
from multiprocessing import JoinableQueue, Process
class NBANewSpider(object):
def __init__(self):
self.url = "http://api.slide.news.sina.com.cn/interface/api_album.php?activity_size=198_132&size=img&ch_id=2&sub_ch=k&" \
"page={}&num=16&jsoncallback=slideNewsSinaComCnCB"
self.headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
}
# 分别构建:url队列,html队列,结果数据队列
self.url_queue = JoinableQueue()
self.html_queue = JoinableQueue()
self.result_queue = JoinableQueue()
def get_urls(self):
"""获取urls列表,将url加入url队列"""
urls = [self.url.format(i) for i in range(1, 20)]
for url in urls:
self.url_queue.put(url)
def send_request(self):
"""在url队列中获取数据,然后发送url请求,获取响应,将响应内容加入响应数据队列"""
while True:
url = self.url_queue.get()
response = requests.get(url, self.headers)
response_str = response.content.decode()
self.html_queue.put(response_str)
self.url_queue.task_done()
def parser_response(self):
"""在数据队列中获取数据,然后把数据存放入结果队列中"""
while True:
response_str = self.html_queue.get()
ret_str = response_str.lstrip("slideNewsSinaComCnCB(")
json_str = ret_str.rstrip(")")
# 将json数据转换成字典数据
ret_dict = json.loads(json_str)
ret_list = ret_dict['data']
self.result_queue.put(ret_list)
self.html_queue.task_done()
def save_to_txt(self):
"""从结果队列中取出,将数据存储进入文本文件"""
while True:
ret_list = self.result_queue.get()
print(ret_list)
for ret_str in ret_list:
with open("NBA线程爬取数据.txt", "a", encoding="utf-8") as f:
f.write(ret_str.get('name'))
f.write(' : ')
f.write(ret_str.get('url'))
f.write('\n')
self.result_queue.task_done()
def run(self):
"""多进程运行爬虫"""
process_list = []
# 创建四个子进程:
url_process = Process(target=self.get_urls)
process_list.append(url_process)
html_process = Process(target=self.send_request)
process_list.append(html_process)
parser_process = Process(target=self.parser_response)
process_list.append(parser_process)
save_process = Process(target=self.save_to_txt)
process_list.append(save_process)
# 设置守护进程,运行各个子进程
for process in process_list:
# 设置守护进程
process.daemon = True
process.start()
# 失眠的意义:子进程的启动需要一定的时间,为了防止子进程还没有起来主进程就已经结束了,所以要睡眠
time.sleep(2)
# 让队列阻塞主进程
for queue in [self.url_queue, self.html_queue, self.result_queue]:
queue.join()
if __name__ == '__main__':
spider = NBANewSpider()
spider.run()