爬虫---高性能爬虫

一:单线程爬虫:

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()
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Python爬虫高阶主要涉及以下几个方面: 1. 反爬虫策略:针对网站加入了反爬虫机制的情况,需要应对验证码、JS加密、IP封禁等问题。可以尝试使用代理IP池、验证码识别库、浏览器模拟等技术来绕过反爬虫策略。 2. 动态网页爬取:有些网站使用了JavaScript进行数据的动态加载,传统的静态爬虫无法获取这些数据。可以使用Selenium或者Pyppeteer等工具来模拟浏览器行为,实现动态网页的爬取。 3. 多线程/多进程爬取:为了提高爬虫的效率,可以使用多线程或多进程的方式进行并发爬取。多线程适合IO密集型任务,多进程适合CPU密集型任务。 4. 数据存储与处理:爬取到的数据一般需要进行存储和处理。可以选择将数据保存到数据库(如MySQL、MongoDB)或者本地文件(如CSV、Excel、JSON),然后使用相关库进行数据处理与分析。 5. 分布式爬虫:当需要爬取大规模数据时,单机爬虫可能会面临性能瓶颈。使用分布式爬虫技术可以将任务分配到多台机器上进行并行处理,提高爬取效率。 6. 登录与Cookie管理:一些网站需要登录才能获取到需要的数据。可以使用模拟登录的方式,通过提交登录表单或者使用第三方库(如requests、Selenium)模拟登录过程,并管理登录后的Cookie信息。 以上是Python爬虫高阶内容的一些常见技术点,希望可以帮助到你。如果有具体的问题或者需要更详细的解答,请提供更多细节。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奈何碎银没有几两

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值